Module: Datasource::Adapters::ActiveRecord

Defined in:
lib/datasource/adapters/active_record.rb

Defined Under Namespace

Modules: DatasourceGenerator, Model, ScopeExtensions

Class Method Summary collapse

Class Method Details

.association_klass(reflection) ⇒ Object



174
175
176
177
178
179
180
# File 'lib/datasource/adapters/active_record.rb', line 174

def association_klass(reflection)
  if reflection.macro == :belongs_to && reflection.options[:polymorphic]
    fail Datasource::Error, "polymorphic belongs_to not supported, write custom loader"
  else
    reflection.klass
  end
end

.association_loaded?(records, name, assoc_select) ⇒ Boolean

Returns:

  • (Boolean)


182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/datasource/adapters/active_record.rb', line 182

def association_loaded?(records, name, assoc_select)
  if records.first.association(name).loaded?
    all_loaded = records.all? { |record| record.association(name).loaded? }
    if assoc_select == ["*"]
      all_loaded
    elsif all_loaded
      records.all? do |record|
        assoc_sample = Array(record.send(name)).first
        assoc_sample.nil? || assoc_sample._datasource_instance
      end
    else
      false
    end
  else
    false
  end
end

.association_reflection(klass, name) ⇒ Object



140
141
142
143
144
145
146
147
148
# File 'lib/datasource/adapters/active_record.rb', line 140

def association_reflection(klass, name)
  if reflection = klass.reflect_on_association(name)
    {
      klass: reflection.klass,
      macro: reflection.macro,
      foreign_key: reflection.try(:foreign_key)
    }
  end
end

.ensure_table_join!(ds, name, att) ⇒ Object



303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/datasource/adapters/active_record.rb', line 303

def ensure_table_join!(ds, name, att)
  join_value = ds.scope.joins_values.find do |value|
    if value.is_a?(Symbol)
      value.to_s == att[:name]
    elsif value.is_a?(String)
      if value =~ /join (\w+)/i
        $1 == att[:name]
      end
    end
  end
  fail Datasource::Error, "given scope does not join on #{name}, but it is required by #{att[:name]}" unless join_value
end

.get_rows(ds) ⇒ Object



280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/datasource/adapters/active_record.rb', line 280

def get_rows(ds)
  append_select = []
  ds.expose_associations.each_pair do |assoc_name, assoc_select|
    if reflection = association_reflection(ds.class.orm_klass, assoc_name.to_sym)
      Datasource::Base.reflection_select(reflection, append_select, [])
    end
  end
  ds.select(*append_select)

  scope = select_scope(ds)
  if scope.respond_to?(:datasource_set)
    scope = scope.spawn.datasource_set(datasource_class: nil)
  end
  scope.includes_values = []
  scope.to_a.tap do |records|
    load_associations(ds, records)
  end
end

.get_table_name(klass) ⇒ Object



150
151
152
# File 'lib/datasource/adapters/active_record.rb', line 150

def get_table_name(klass)
  klass.table_name.to_sym
end

.has_attribute?(record, name) ⇒ Boolean

Returns:

  • (Boolean)


170
171
172
# File 'lib/datasource/adapters/active_record.rb', line 170

def has_attribute?(record, name)
  record.attributes.key?(name.to_s)
end

.is_scope?(obj) ⇒ Boolean

Returns:

  • (Boolean)


154
155
156
# File 'lib/datasource/adapters/active_record.rb', line 154

def is_scope?(obj)
  obj.kind_of?(::ActiveRecord::Relation)
end

.load_association(records, name, assoc_select, params) ⇒ Object



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/datasource/adapters/active_record.rb', line 200

def load_association(records, name, assoc_select, params)
  return if records.empty?
  name = name.to_sym
  klass = records.first.class
  if reflection = klass.reflect_on_association(name)
    assoc_class = association_klass(reflection)
    datasource_class = assoc_class.default_datasource

    scope = assoc_class.all
    datasource = datasource_class.new(scope)
    assoc_select_attributes = assoc_select.reject { |att| att.kind_of?(Hash) }
    assoc_select_associations = assoc_select.inject({}) do |hash, att|
      hash.deep_merge!(att) if att.kind_of?(Hash)
      hash
    end
    Datasource::Base.reflection_select(association_reflection(klass, name), [], assoc_select_attributes)
    datasource.params(params)

    Datasource.logger.debug { "load_association #{records.first.try!(:class)} #{name}: #{assoc_select_attributes.inspect}" }
    datasource.select(*assoc_select_attributes)
    select_values = datasource.get_select_values

    # TODO: manually load associations, and load them all at once for
    # nested associations, eg. in following, load all Users in 1 query:
    # {"user"=>["*"], "players"=>["*"], "picked_players"=>["*",
    # {:position=>["*"]}], "parent_picked_team"=>["*", {:user=>["*"]}]}
    begin
      ::ActiveRecord::Associations::Preloader
        .new.preload(records, name, assoc_class.select(*select_values))
    rescue ArgumentError
      ::ActiveRecord::Associations::Preloader
        .new(records, name, assoc_class.select(*select_values)).run
    end

    assoc_records = records.flat_map { |record| record.send(name) }.compact
    unless assoc_records.empty?
      if Datasource.logger.info? && !assoc_select_associations.empty?
        Datasource.logger.info { "Loading associations " + assoc_select_associations.keys.map(&:to_s).join(", ") + " for #{assoc_records.first.try!(:class)}s" }
      end
      assoc_select_associations.each_pair do |assoc_name, assoc_select|
        Datasource.logger.debug { "load_association nested association #{assoc_name}: #{assoc_select.inspect}" }
        load_association(assoc_records, assoc_name, assoc_select, params)
      end
      datasource.results(assoc_records)
    end
  end
rescue Exception => ex
  if ex.is_a?(SystemStackError) || ex.is_a?(Datasource::RecursionError)
    fail Datasource::RecursionError, "recursive association (involving #{name})"
  else
    raise
  end
end

.load_associations(ds, records) ⇒ Object



270
271
272
273
274
275
276
277
278
# File 'lib/datasource/adapters/active_record.rb', line 270

def load_associations(ds, records)
  if Datasource.logger.info? && !ds.expose_associations.empty?
    Datasource.logger.info { "Loading associations " + ds.expose_associations.keys.map(&:to_s).join(", ") + " for #{records.first.try!(:class)}s" }
  end
  Datasource.logger.debug { "load_associations (#{records.size} #{records.first.try!(:class)}): #{ds.expose_associations.inspect}" }
  ds.expose_associations.each_pair do |assoc_name, assoc_select|
    load_association(records, assoc_name, assoc_select, ds.params)
  end
end

.primary_scope_table(ds) ⇒ Object



299
300
301
# File 'lib/datasource/adapters/active_record.rb', line 299

def primary_scope_table(ds)
  ds.scope.klass.table_name
end

.scope_loaded?(scope) ⇒ Boolean

Returns:

  • (Boolean)


162
163
164
# File 'lib/datasource/adapters/active_record.rb', line 162

def scope_loaded?(scope)
  scope.loaded?
end

.scope_to_class(scope) ⇒ Object



158
159
160
# File 'lib/datasource/adapters/active_record.rb', line 158

def scope_to_class(scope)
  scope.klass
end

.scope_to_records(scope) ⇒ Object



166
167
168
# File 'lib/datasource/adapters/active_record.rb', line 166

def scope_to_records(scope)
  scope.to_a
end

.select_scope(ds) ⇒ Object



260
261
262
# File 'lib/datasource/adapters/active_record.rb', line 260

def select_scope(ds)
  ds.scope.select(*ds.get_select_values)
end

.to_query(ds) ⇒ Object



254
255
256
257
258
# File 'lib/datasource/adapters/active_record.rb', line 254

def to_query(ds)
  ::ActiveRecord::Base.uncached do
    ds.scope.select(*ds.get_select_values).to_sql
  end
end

.upgrade_records(ds, records) ⇒ Object



264
265
266
267
268
# File 'lib/datasource/adapters/active_record.rb', line 264

def upgrade_records(ds, records)
  Datasource.logger.debug { "Upgrading records #{records.map(&:class).map(&:name).join(', ')}" }
  load_associations(ds, records)
  ds.results(records)
end