# File lib/datasource/base.rb, line 27 def primary_key :id end
class Datasource::Base
Attributes
_associations[RW]
_attributes[RW]
_collection_context[RW]
_loader_order[RW]
_loaders[RW]
_update_scope[RW]
default_consumer_adapter[RW]
Should be set by consumer adapter library (e.g. for ActiveModelSerializers)
orm_klass[W]
adapter[R]
expose_associations[R]
expose_attributes[R]
scope[R]
Public Class Methods
_column_attribute_names()
click to toggle source
# File lib/datasource/base.rb, line 46 def _column_attribute_names column_attributes = _attributes.values.select { |att| att[:klass].nil? }.map { |att| att[:name] } end
collection(&block)
click to toggle source
# File lib/datasource/base.rb, line 42 def collection(&block) _collection_context.class_exec(&block) end
default_adapter()
click to toggle source
# File lib/datasource/base.rb, line 17 def default_adapter @adapter ||= begin Datasource::Adapters.const_get(Datasource::Adapters.constants.first) end end
inherited(base)
click to toggle source
# File lib/datasource/base.rb, line 9 def inherited(base) base._attributes = (_attributes || {}).dup base._associations = (_associations || {}).dup base._loaders = (_loaders || {}).dup base._loader_order = (_loader_order || []).dup base._collection_context = Class.new(_collection_context || CollectionContext) end
new(scope, adapter = nil)
click to toggle source
# File lib/datasource/base.rb, line 86 def initialize(scope, adapter = nil) @adapter = adapter || self.class.default_adapter @scope = if self.class._update_scope self.class._update_scope.call(scope) else scope end @expose_attributes = [] @expose_associations = {} @select_all_columns = false @params = {} end
orm_klass()
click to toggle source
# File lib/datasource/base.rb, line 23 def orm_klass fail Datasource::Error, "Model class not set for #{name}. You should define it:\nclass YourDatasource\n @orm_klass = MyModelClass\nend" end
primary_key()
click to toggle source
reflection_select(reflection, parent_select, assoc_select)
click to toggle source
# File lib/datasource/base.rb, line 31 def reflection_select(reflection, parent_select, assoc_select) # append foreign key depending on assoication if reflection[:macro] == :belongs_to parent_select.push(reflection[:foreign_key]) elsif [:has_many, :has_one].include?(reflection[:macro]) assoc_select.push(reflection[:foreign_key]) else fail Datasource::Error, "unsupported association type #{reflection[:macro]} - TODO" end end
Private Class Methods
association(name)
click to toggle source
# File lib/datasource/base.rb, line 61 def association(name) @_associations[name.to_s] = true end
associations(*assocs)
click to toggle source
# File lib/datasource/base.rb, line 57 def associations(*assocs) assocs.each { |name| association(name) } end
attribute(name, klass = nil)
click to toggle source
# File lib/datasource/base.rb, line 65 def attribute(name, klass = nil) att = { name: name.to_s, klass: klass } @_attributes[att[:name]] = att end
attributes(*attrs)
click to toggle source
# File lib/datasource/base.rb, line 53 def attributes(*attrs) attrs.each { |name| attribute(name) } end
computed(name, *_deps)
click to toggle source
# File lib/datasource/attributes/computed_attribute.rb, line 29 def self.computed(name, *_deps) deps = _deps.select { |dep| dep.kind_of?(Hash) } _deps.reject! { |dep| dep.kind_of?(Hash) } unless _deps.empty? self_key = default_adapter.get_table_name(orm_klass) deps.push(self_key => _deps) end klass = Class.new(Attributes::ComputedAttribute) do depends *deps end attribute name, klass end
group_by_column(column, rows, remove_column = false)
click to toggle source
# File lib/datasource/base.rb, line 75 def group_by_column(column, rows, remove_column = false) rows.inject({}) do |map, row| map[row[column]] = row row.delete(column) if remove_column map end end
loaded(name, _options = {}, &block)
click to toggle source
Calls superclass method
# File lib/datasource/attributes/loaded.rb, line 70 def self.loaded(name, _options = {}, &block) name = name.to_sym datasource_class = self loader_class = Class.new(Attributes::Loaded) do options(_options.reverse_merge(source: :"load_#{name}")) end @_loaders[name] = loader_class @_loader_order << name method_module = Module.new do define_method name do |*args, &block| if _datasource_loaded if _datasource_loaded.key?(name) _datasource_loaded[name] else fail Datasource::Error, "loader #{name} called but was not selected" end elsif defined?(super) super(*args, &block) else method_missing(name, *args, &block) end end end orm_klass.class_eval do prepend method_module end computed name, loader: name end
query(name, deps = nil, value = nil, &block)
click to toggle source
# File lib/datasource/attributes/query_attribute.rb, line 20 def self.query(name, deps = nil, value = nil, &block) klass = Class.new(Attributes::QueryAttribute) do depends deps if deps if block define_singleton_method(:select_value, &block) else define_singleton_method(:select_value) { value } end end attribute name, klass end
update_scope(&block)
click to toggle source
# File lib/datasource/base.rb, line 70 def update_scope(&block) # TODO: careful about scope module extension, to_a infinite recursion @_update_scope = block end
Public Instance Methods
attribute_exposed?(name)
click to toggle source
# File lib/datasource/base.rb, line 230 def attribute_exposed?(name) @expose_attributes.include?(name.to_s) end
can_upgrade?(records)
click to toggle source
assume records have all attributes selected (default ORM record)
# File lib/datasource/base.rb, line 235 def can_upgrade?(records) query_attributes = @expose_attributes.select do |name| klass = self.class._attributes[name][:klass] if klass klass.ancestors.include?(Attributes::QueryAttribute) end end return true if query_attributes.empty? Array(records).all? do |record| query_attributes.all? do |name| adapter.has_attribute?(record, name) end end end
fail_missing_attributes(names)
click to toggle source
# File lib/datasource/base.rb, line 164 def fail_missing_attributes(names) message = if names.size > 1 "attributes or associations #{names.join(', ')} don't exist " else "attribute or association #{names.first} doesn't exist " end message += "for #{self.class.orm_klass.name}, " message += "did you forget to call \"computed :#{names.first}, <dependencies>\" in your datasource_module?" fail Datasource::Error, message end
get_collection_context(rows)
click to toggle source
# File lib/datasource/base.rb, line 255 def get_collection_context(rows) self.class._collection_context.new(@scope, rows, self, @params) end
get_exposed_loaders()
click to toggle source
# File lib/datasource/base.rb, line 259 def get_exposed_loaders @expose_attributes .map { |name| self.class._attributes[name] }.select { |att| att[:klass] && att[:klass].ancestors.include?(Attributes::ComputedAttribute) }.flat_map { |att| att[:klass]._loader_depends }.uniq .sort_by { |loader_name| self.class._loader_order.index(loader_name) } end
get_select_values()
click to toggle source
# File lib/datasource/base.rb, line 199 def get_select_values scope_table = adapter.primary_scope_table(self) select_values = Set.new if @select_all_columns select_values.add("#{scope_table}.*") self.class._attributes.values.each do |att| if att[:klass] && attribute_exposed?(att[:name]) if att[:klass].ancestors.include?(Attributes::QueryAttribute) select_values.add("(#{att[:klass].select_value}) as #{att[:name]}") end end end else select_values.add("#{scope_table}.#{self.class.primary_key}") self.class._attributes.values.each do |att| if attribute_exposed?(att[:name]) if att[:klass] == nil select_values.add("#{scope_table}.#{att[:name]}") elsif att[:klass].ancestors.include?(Attributes::QueryAttribute) select_values.add("(#{att[:klass].select_value}) as #{att[:name]}") end end end end select_values.to_a end
params(*args)
click to toggle source
# File lib/datasource/base.rb, line 100 def params(*args) args.each do |arg| if arg.kind_of?(Hash) @params.deep_merge!(arg.symbolize_keys) elsif arg.is_a?(Symbol) @params.merge!(arg => true) else fail Datasource::Error, "unknown parameter type #{arg.class}" end end @params end
results(rows = nil)
click to toggle source
# File lib/datasource/base.rb, line 320 def results(rows = nil) rows ||= adapter.get_rows(self) collection_context = run_loaders(rows) rows.each do |row| row._datasource_instance = self set_row_loaded_values(collection_context, row) if collection_context end rows end
run_loaders(rows)
click to toggle source
# File lib/datasource/base.rb, line 273 def run_loaders(rows) return if rows.empty? # check if loaders have already been ran if rows.first._datasource_loaded # not frequent, so we can afford to check all rows check_list = get_exposed_loaders run_list = [] rows.each do |row| if row._datasource_loaded check_list.delete_if do |name| if !row._datasource_loaded.key?(name) run_list << name true end end break if check_list.empty? else run_list.concat(check_list) break end end else # most frequent case - loaders haven't been ran run_list = get_exposed_loaders end get_collection_context(rows).tap do |collection_context| run_list.each do |loader_name| loader = self.class._loaders[loader_name] or fail Datasource::Error, "loader with name :#{loader_name} could not be found" Datasource.logger.info { "Running loader #{loader_name} for #{rows.first.try!(:class)}" } collection_context.loaded_values[loader_name] = loader.load(collection_context) end end end
select(*names)
click to toggle source
# File lib/datasource/base.rb, line 130 def select(*names) newly_exposed_attributes = [] missing_attributes = [] names.each do |name| if name.kind_of?(Hash) name.each_pair do |assoc_name, assoc_select| assoc_name = assoc_name.to_s if self.class._associations.key?(assoc_name) @expose_associations[assoc_name] ||= [] @expose_associations[assoc_name].concat(Array(assoc_select)) @expose_associations[assoc_name].uniq! else missing_attributes << assoc_name end end else name = name.to_s if name == "*" select_all_columns elsif self.class._attributes.key?(name) unless @expose_attributes.include?(name) @expose_attributes.push(name) newly_exposed_attributes.push(name) end else missing_attributes << name end end end update_dependencies(newly_exposed_attributes) unless newly_exposed_attributes.empty? fail_missing_attributes(missing_attributes) if Datasource.config.raise_error_on_unknown_attribute_select && !missing_attributes.empty? self end
select_all()
click to toggle source
# File lib/datasource/base.rb, line 122 def select_all attributes = self.class._attributes.keys select(*attributes) @select_all_columns = true attributes end
select_all_columns()
click to toggle source
# File lib/datasource/base.rb, line 114 def select_all_columns columns = self.class._column_attribute_names select(*columns) @select_all_columns = true columns end
set_row_loaded_values(collection_context, row)
click to toggle source
# File lib/datasource/base.rb, line 311 def set_row_loaded_values(collection_context, row) row._datasource_loaded ||= {} primary_key = row.send(self.class.primary_key) collection_context.loaded_values.each_pair do |name, values| row._datasource_loaded[name] = values[primary_key] end end
update_dependencies(names)
click to toggle source
# File lib/datasource/base.rb, line 175 def update_dependencies(names) scope_table = adapter.primary_scope_table(self) self.class._attributes.values.each do |att| next unless names.include?(att[:name]) next unless att[:klass] if att[:klass].ancestors.include?(Attributes::ComputedAttribute) att[:klass]._depends.each_pair do |key, value| if key.to_s == scope_table select(*value) else select(key => value) end end elsif att[:klass].ancestors.include?(Attributes::QueryAttribute) att[:klass]._depends.each do |name| next if name == scope_table adapter.ensure_table_join!(self, name, att) end end end end
upgrade_records(records)
click to toggle source
# File lib/datasource/base.rb, line 251 def upgrade_records(records) adapter.upgrade_records(self, Array(records)) end