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



106
107
108
109
110
111
112
# File 'lib/datasource/adapters/active_record.rb', line 106

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_reflection(klass, name) ⇒ Object



80
81
82
83
84
85
86
87
88
# File 'lib/datasource/adapters/active_record.rb', line 80

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

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



189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/datasource/adapters/active_record.rb', line 189

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



164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
# File 'lib/datasource/adapters/active_record.rb', line 164

def get_rows(ds)
  append_select = []
  ds.expose_associations.each_pair do |assoc_name, assoc_select|
    if reflection = Adapters::ActiveRecord.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?(:use_datasource)
    scope = scope.spawn.use_datasource(nil)
  end
  scope.includes_values = []
  scope.to_a.tap do |records|
    ds.expose_associations.each_pair do |assoc_name, assoc_select|
      Adapters::ActiveRecord.preload_association(records, assoc_name)
    end
  end
end

.get_table_name(klass) ⇒ Object



90
91
92
# File 'lib/datasource/adapters/active_record.rb', line 90

def get_table_name(klass)
  klass.table_name.to_sym
end

.is_scope?(obj) ⇒ Boolean



94
95
96
# File 'lib/datasource/adapters/active_record.rb', line 94

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

.preload_association(records, name) ⇒ Object



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/datasource/adapters/active_record.rb', line 114

def preload_association(records, name)
  return if records.empty?
  return if records.first.association(name.to_sym).loaded?
  klass = records.first.class
  if reflection = klass.reflections[name.to_sym]
    assoc_class = association_klass(reflection)
    datasource_class = assoc_class.default_datasource
    # TODO: extract serializer_class from parent serializer association
    serializer_class = Datasource::Base.consumer_adapter.get_serializer_for(assoc_class)

    # TODO: can we make it use datasource scope (with_serializer)? like Sequel
    scope = assoc_class.all
    datasource = datasource_class.new(scope)
    datasource_select = serializer_class._attributes.dup
    Datasource::Base.reflection_select(Adapters::ActiveRecord.association_reflection(klass, name.to_sym), [], datasource_select)
    datasource.select(*datasource_select)
    select_values = datasource.get_select_values

    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
    serializer_class._associations.each_pair do |assoc_name, options|
      preload_association(assoc_records, assoc_name)
    end
    datasource.results(assoc_records)
  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

.primary_scope_table(ds) ⇒ Object



185
186
187
# File 'lib/datasource/adapters/active_record.rb', line 185

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

.scope_loaded?(scope) ⇒ Boolean



102
103
104
# File 'lib/datasource/adapters/active_record.rb', line 102

def scope_loaded?(scope)
  scope.loaded?
end

.scope_to_class(scope) ⇒ Object



98
99
100
# File 'lib/datasource/adapters/active_record.rb', line 98

def scope_to_class(scope)
  scope.klass
end

.select_scope(ds) ⇒ Object



160
161
162
# File 'lib/datasource/adapters/active_record.rb', line 160

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

.to_query(ds) ⇒ Object



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

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