datasource 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 892fcf4ff3a4d4988111a50f2b6773df1d102da1
4
- data.tar.gz: a9f398491b40f774f767563e4bb1e022b7aee945
3
+ metadata.gz: 6c4654c0cc97e0cba3fd8ce3d9b99a520d590afa
4
+ data.tar.gz: a37d72813c8e2d0a6ef6430a380115126f28dfad
5
5
  SHA512:
6
- metadata.gz: 1595cd7d940f7ec47d5b3cae6c242f71516ddf4ba06c62a04aa5518c563e82ddf7108432940c12107eadaf5f290faeb3f89a1d781dbafffd43a70e80a29eb263
7
- data.tar.gz: c851fcf944f8f56e9dab25f36f8371f58fd6cb6719f14a27593649ef847e862a4ff5d92c4292b23484eebd7481142c9f86edfd680165dac47d35e76e2e314bd7
6
+ metadata.gz: 6d68a095c8b8af62be19f3fbb7048b634b5d0fb5d113f3e81d6e1430959d69bc9f93cce68d02977066ff782361c7915b0a3cab0a9003b85598caa019a5bb5c7e
7
+ data.tar.gz: 4cf182d351a3bfc21f6dfcd2062eb97f21f36dd39ccf9ef4bfaf6cca418aea7b7fc9105fe613936f95f19a4a32d74714e51b27ebccfc15fa9de9a7b716f25dab
data/README.md CHANGED
@@ -4,12 +4,16 @@
4
4
  - Specify custom SQL snippets for virtual attributes (Query attributes)
5
5
  - Write custom preloading logic in a reusable way
6
6
 
7
+ ** Note: the API of this gem is still unstable and may change a lot between versions! This project uses semantic versioning (until version 1.0.0, minor version changes may include API changes, but patch version will not) **
8
+
7
9
  #### Install
8
10
 
9
- Add to Gemfile
11
+ Requires Ruby 2.0 or higher.
12
+
13
+ Add to Gemfile (recommended to use github version until API is stable)
10
14
 
11
15
  ```
12
- gem 'datasource'
16
+ gem 'datasource', github: 'kundi/datasource'
13
17
  ```
14
18
 
15
19
  ```
@@ -32,18 +36,10 @@ rails g datasource:install
32
36
 
33
37
  - active_model_serializers
34
38
 
35
- ## Simple Mode
36
-
37
- Datasource is configured to run in Simple mode by default, which makes it easier
38
- to start with, but disables some advanced optimizations. See
39
- [Advanced mode](https://github.com/mrbrdo/datasource/wiki/Advanced-mode) for more
40
- information after you understand Simple mode.
41
-
42
39
  ### Associations
43
40
 
44
- The most noticable magic effect of using Datasource in Simple mode (Advanced mode
45
- has other benefits) is that associations will automatically be preloaded using a
46
- single query.
41
+ The most noticable magic effect of using Datasource is that associations will
42
+ automatically be preloaded using a single query.
47
43
 
48
44
  ```ruby
49
45
  class PostSerializer < ActiveModel::Serializer
@@ -65,7 +61,8 @@ automatically by Datasource.
65
61
 
66
62
  ### Show action
67
63
 
68
- You will probably want to reuse the same preloading logic in your show action.
64
+ If you use the more advanced features like Loaded, you will probably want to
65
+ reuse the same loading logic in your show action.
69
66
  You will need to call `for_serializer` on the scope before you call `find`.
70
67
  You can optionally give it the serializer class as an argument.
71
68
 
@@ -117,96 +114,120 @@ end
117
114
  SELECT users.*, (users.first_name || ' ' || users.last_name) AS full_name FROM users
118
115
  ```
119
116
 
120
- Note: If you need data from another table, use a join in a loader (see below).
117
+ Note: If you need data from another table, use a join in a loaded value (see below).
121
118
 
122
- ### Loader
119
+ ### Standalone Datasource class
123
120
 
124
- You might want to have some more complex preloading logic. In that case you can use a loader.
125
- A loader will receive ids of the records, and needs to return a hash.
126
- The key of the hash must be the id of the record for which the value is.
121
+ If you are going to have more complex preloading logic (like using Loaded below),
122
+ then it might be better to put Datasource code into its own class. This is pretty
123
+ easy, just create a directory `app/datasources` (or whatever you like), and create
124
+ a file depending on your model name, for example for a `Post` model, create
125
+ `post_datasource.rb`. The name is important for auto-magic reasons. Example file:
127
126
 
128
- A loader will only be executed if a computed attribute depends on it. See
129
- [Advanced mode](https://github.com/mrbrdo/datasource/wiki/Advanced-mode) for
130
- information about computed attributes (but this works the same way in Simple mode).
131
- A more simple alternative to loader which doesn't require computed attributes is to use
132
- [Loaded](#loaded).
133
- If an attribute depends on multiple loaders, pass an array of loaders like
134
- so `computed :attr, loaders: [:loader1, :loader2]`.
127
+ ```ruby
128
+ class PostDatasource < Datasource::From(Post)
129
+ query(:full_name) { "users.first_name || ' ' || users.last_name" }
130
+ end
131
+ ```
135
132
 
136
- Be careful that if your hash does not contain a value for the object ID, the loaded value
137
- will be nil. However you can use the `default` option for such cases (see below example).
133
+ ### Loaded
134
+
135
+ You might want to have some more complex preloading logic. In that case you can
136
+ use a method to load values for all the records at once (e.g. with a custom query
137
+ or even from a cache). The loading methods are only executed if you use the values,
138
+ otherwise they will be skipped.
139
+
140
+ First just declare that you want to have a loaded attribute (the parameters will be explained shortly):
138
141
 
139
142
  ```ruby
140
- class User < ActiveRecord::Base
141
- datasource_module do
142
- computed :post_count, loader: :post_counts
143
- loader :post_counts, array_to_hash: true, default: 0 do |user_ids|
144
- results = Post
145
- .where(user_id: user_ids)
146
- .group(:user_id)
147
- .pluck("user_id, COUNT(id)")
148
- end
149
- end
143
+ class UserDatasource < Datasource::From(User)
144
+ loaded :post_count, from: :array, default: 0
150
145
  end
146
+ ```
151
147
 
152
- class UserSerializer < ActiveModel::Serializer
153
- attributes :id, :post_count
148
+ By default, datasource will look for a method named `load_<name>` for loading
149
+ the values, in this case `load_newest_comment`. It needs to be defined in the
150
+ collection block, which has methods to access information about the collection (posts)
151
+ that are being loaded. These methods are `scope`, `models`, `model_ids`,
152
+ `datasource`, `datasource_class` and `params`.
154
153
 
155
- def post_count
156
- # Will automatically give you the value for this user's ID
157
- object.loaded_values[:post_counts]
154
+ ```ruby
155
+ class UserDatasource < Datasource::From(User)
156
+ loaded :post_count, from: :array, default: 0
157
+
158
+ collection do
159
+ def load_post_count
160
+ Post.where(user_id: model_ids)
161
+ .group(:user_id)
162
+ .pluck("user_id, COUNT(id)")
163
+ end
158
164
  end
159
165
  end
160
166
  ```
161
167
 
162
- ```sql
163
- SELECT users.* FROM users
164
- SELECT user_id, COUNT(id) FROM posts WHERE user_id IN (?)
165
- ```
166
-
167
- Datasource provides shortcuts to transform your data into a hash. Here are examples:
168
+ In this case `load_post_count` returns an array of pairs.
169
+ For example: `[[1, 10], [2, 5]]`. Datasource can understand this because of
170
+ `from: :array`. This would result in the following:
168
171
 
169
172
  ```ruby
170
- loader :stuff, array_to_hash: true do |ids|
171
- [[1, "first"], [2, "second"]]
172
- # will be transformed into
173
- # { 1 => "first", 2 => "second" }
174
- end
173
+ post_id_1.post_count # => 10
174
+ post_id_2.post_count # => 5
175
+ # other posts will have the default value or nil if no default value was given
176
+ other_post.post_count # => 0
177
+ ```
175
178
 
176
- loader :stuff, group_by: :user_id do |ids|
177
- Post.where(user_id: ids)
178
- # will be transformed into
179
- # { 1 => [#<Post>, #<Post>, ...], 2 => [ ... ], ... }
180
- end
179
+ Besides `default` and `from: :array`, you can also specify `group_by`, `one`
180
+ and `source`. Source is just the name of the load method.
181
181
 
182
- loader :stuff, group_by: :user_id, one: true do |ids|
183
- Post.where(user_id: ids)
184
- # will be transformed into
185
- # { 1 => #<Post>, 2 => #<Post>, ... }
186
- end
182
+ The other two are explained in the following example.
187
183
 
188
- loader :stuff, group_by: "user_id", one: true do |ids|
189
- # it works the same way on an array of hashes
190
- # but be careful about Symbol/String difference
191
- [{ "title" => "Something", "user_id" => 10 }]
192
- # will be transformed into
193
- # { 10 => { "title" => "Something", "user_id" => 10 } }
184
+ ```ruby
185
+ class PostDatasource < Datasource::From(Post)
186
+ loaded :newest_comment, group_by: :post_id, one: true, source: :load_newest_comment
187
+
188
+ collection do
189
+ def load_newest_comment
190
+ Comment.for_serializer.where(post_id: model_ids)
191
+ .group("post_id")
192
+ .having("id = MAX(id)")
193
+ end
194
+ end
194
195
  end
195
196
  ```
196
197
 
197
- ### Loaded
198
+ In this case the load method returns an ActiveRecord relation, which for our purposes
199
+ acts the same as an Array (so we could also return an Array if we wanted).
200
+ Using `group_by: :post_id` in the `loaded` call tells datasource to group the
201
+ results in this array by that attribute (or key if it's an array of hashes instead
202
+ of model objects). `one: true` means that we only want a single value instead of
203
+ an array of values (we might want multiple, e.g. `newest_10_comments`).
204
+ So in this case, if we had a Post with id 1, `post.newest_comment` would be a
205
+ Comment from the array that has `post_id` equal to 1.
198
206
 
199
- Loaded is the same as loader, but it automatically creates a computed attribute
200
- and defines a method with the same name on your model.
207
+ In this case, in the load method, we also used `for_serializer`, which will load
208
+ the `Comment`s according to the `CommentSerializer`.
201
209
 
202
- Here is the previous example with `loaded` instead of `loader`:
210
+ Note that it's perfectly fine (even good) to already have a method with the same
211
+ name in your model.
212
+ If you use that method outside of serializers/datasource, it will work just as
213
+ it should. But when using datasource, it will be overwritten by the datasource
214
+ version. Counts is a good example:
203
215
 
204
216
  ```ruby
205
217
  class User < ActiveRecord::Base
206
- datasource_module do
207
- loaded :post_count, array_to_hash: true, default: 0 do |user_ids|
208
- results = Post
209
- .where(user_id: user_ids)
218
+ has_many :posts
219
+
220
+ def post_count
221
+ posts.count
222
+ end
223
+ end
224
+
225
+ class UserDatasource < Datasource::From(User)
226
+ loaded :post_count, from: :array, default: 0
227
+
228
+ collection do
229
+ def load_post_count
230
+ Post.where(user_id: model_ids)
210
231
  .group(:user_id)
211
232
  .pluck("user_id, COUNT(id)")
212
233
  end
@@ -214,50 +235,56 @@ class User < ActiveRecord::Base
214
235
  end
215
236
 
216
237
  class UserSerializer < ActiveModel::Serializer
217
- attributes :id, :post_count
218
- # Note that the User now has a generated post_count method
238
+ attributes :id, :post_count # <- post_count will be read from load_post_count
219
239
  end
240
+
241
+ User.first.post_count # <- your model method will be called
220
242
  ```
221
243
 
222
- When using `loaded`, if you already have the method with this name defined in your
223
- model, datasource will automatically create a 'wrapper' method that will use the
224
- loaded value if available (when you are using a serializer/datasource), otherwise
225
- it will fallback to your original method. This way you can still use the same
226
- method when you are not using a serializer/datasource. For example:
244
+ ### Params
245
+
246
+ You can also specify params that can be read from collection methods. The params
247
+ can be specified when you call `render`:
227
248
 
228
249
  ```ruby
229
- class User < ActiveRecord::Base
230
- datasource_module do
231
- loaded :post_count, array_to_hash: true, default: 0 do |user_ids|
232
- results = Post
233
- .where(user_id: user_ids)
234
- .group(:user_id)
235
- .pluck("user_id, COUNT(id)")
250
+ # controller
251
+ render json: posts,
252
+ datasource_params: { include_newest_comments: true }
253
+
254
+ # datasource
255
+ loaded :newest_comments, default: []
256
+
257
+ collection do
258
+ def load_newest_comments
259
+ if params[:include_newest_comments]
260
+ # ...
261
+ end
236
262
  end
237
263
  end
264
+ ```
238
265
 
239
- def post_count
240
- posts.count
241
- end
242
- end
243
-
244
- class UserSerializer < ActiveModel::Serializer
245
- attributes :id, :post_count # <- post_count will be read from loaded_values
246
- end
266
+ ### Debugging and logging
247
267
 
248
- User.first.post_count # <- your method will be called
268
+ Datasource outputs some useful logs that you can use debugging. By default the log level is
269
+ set to warnings only, but you can change it. You can add the following line to your
270
+ `config/initializers/datasource.rb`:
249
271
 
272
+ ```ruby
273
+ Datasource.logger.level = Logger::INFO
250
274
  ```
251
275
 
276
+ You can also set it to `DEBUG` for more output. The logger outputs to `stdout` by default. It
277
+ is not recommended to have this enabled in production.
278
+
252
279
  ## Getting Help
253
280
 
254
- If you find a bug, please report an [Issue](https://github.com/mrbrdo/datasource/issues/new).
281
+ If you find a bug, please report an [Issue](https://github.com/kundi/datasource/issues/new).
255
282
 
256
283
  If you have a question, you can also open an Issue.
257
284
 
258
285
  ## Contributing
259
286
 
260
- 1. Fork it ( https://github.com/mrbrdo/datasource/fork )
287
+ 1. Fork it ( https://github.com/kundi/datasource/fork )
261
288
  2. Create your feature branch (`git checkout -b my-new-feature`)
262
289
  3. Commit your changes (`git commit -am 'Add some feature'`)
263
290
  4. Push to the branch (`git push origin my-new-feature`)
@@ -5,27 +5,35 @@ module Datasource
5
5
  module Adapters
6
6
  module ActiveRecord
7
7
  module ScopeExtensions
8
- def use_datasource_serializer(value)
9
- @datasource_serializer = value
10
- self
8
+ def self.extended(mod)
9
+ mod.instance_exec do
10
+ @datasource_info ||= { select: [], params: [] }
11
+ end
11
12
  end
12
13
 
13
- def use_datasource(value)
14
- @datasource = value
14
+ def datasource_set(hash)
15
+ @datasource_info.merge!(hash)
15
16
  self
16
17
  end
17
18
 
18
19
  def datasource_select(*args)
19
- @datasource_select = Array(@datasource_select) + args
20
+ @datasource_info[:select] += args
21
+ self
22
+ end
23
+
24
+ def datasource_params(*args)
25
+ @datasource_info[:params] += args
20
26
  self
21
27
  end
22
28
 
23
29
  def get_datasource
24
- datasource = @datasource.new(self)
25
- datasource.select(*Array(@datasource_select))
26
- if @datasource_serializer
30
+ klass = @datasource_info[:datasource_class]
31
+ datasource = klass.new(self)
32
+ datasource.select(*@datasource_info[:select])
33
+ datasource.params(*@datasource_info[:params])
34
+ if @datasource_info[:serializer_class]
27
35
  select = []
28
- Datasource::Base.consumer_adapter.to_datasource_select(select, @datasource.orm_klass, @datasource_serializer, nil, datasource.adapter)
36
+ Datasource::Base.consumer_adapter.to_datasource_select(select, klass.orm_klass, @datasource_info[:serializer_class], nil, datasource.adapter)
29
37
 
30
38
  datasource.select(*select)
31
39
  end
@@ -34,9 +42,12 @@ module Datasource
34
42
 
35
43
  private
36
44
  def exec_queries
37
- if @datasource
45
+ if @datasource_info[:datasource_class]
38
46
  datasource = get_datasource
39
47
 
48
+ Datasource.logger.debug { "exec_queries expose_attributes: #{datasource.expose_attributes.inspect}" }
49
+ Datasource.logger.debug { "exec_queries expose_associations: #{datasource.expose_associations.inspect}" }
50
+
40
51
  @loaded = true
41
52
  @records = datasource.results
42
53
  else
@@ -49,7 +60,7 @@ module Datasource
49
60
  extend ActiveSupport::Concern
50
61
 
51
62
  included do
52
- attr_accessor :loaded_values
63
+ attr_accessor :_datasource_loaded, :_datasource_instance
53
64
  end
54
65
 
55
66
  def for_serializer(serializer = nil)
@@ -58,7 +69,10 @@ module Datasource
58
69
 
59
70
  scope = self.class
60
71
  .with_datasource(datasource_class)
61
- .for_serializer(serializer).where(pk => send(pk))
72
+ .for_serializer(serializer)
73
+ .where(pk => send(pk))
74
+
75
+ scope = yield(scope) if block_given?
62
76
 
63
77
  datasource = scope.get_datasource
64
78
  if datasource.can_upgrade?(self)
@@ -69,37 +83,47 @@ module Datasource
69
83
  end
70
84
 
71
85
  module ClassMethods
72
- def for_serializer(serializer = nil)
73
- scope = if all.respond_to?(:use_datasource_serializer)
74
- all
75
- else
76
- all.extending(ScopeExtensions).use_datasource(default_datasource)
77
- end
78
- scope.use_datasource_serializer(serializer || Datasource::Base.consumer_adapter.get_serializer_for(Adapters::ActiveRecord.scope_to_class(scope)))
86
+ def for_serializer(serializer_class = nil)
87
+ scope = scope_with_datasource_ext
88
+ serializer_class ||=
89
+ Datasource::Base.consumer_adapter.get_serializer_for(
90
+ Adapters::ActiveRecord.scope_to_class(scope))
91
+ scope.datasource_set(serializer_class: serializer_class)
79
92
  end
80
93
 
81
- def with_datasource(datasource = nil)
82
- scope = if all.respond_to?(:use_datasource)
83
- all
84
- else
85
- all.extending(ScopeExtensions)
86
- end
87
- scope.use_datasource(datasource || default_datasource)
94
+ def with_datasource(datasource_class = nil)
95
+ scope_with_datasource_ext(datasource_class)
88
96
  end
89
97
 
90
98
  def default_datasource
91
- @default_datasource ||= Datasource::From(self)
99
+ @default_datasource ||= begin
100
+ "#{name}Datasource".constantize
101
+ rescue NameError
102
+ Datasource::From(self)
103
+ end
92
104
  end
93
105
 
94
106
  def datasource_module(&block)
95
107
  default_datasource.instance_exec(&block)
96
108
  end
109
+
110
+ private
111
+ def scope_with_datasource_ext(datasource_class = nil)
112
+ if all.respond_to?(:datasource_set)
113
+ all
114
+ else
115
+ datasource_class ||= default_datasource
116
+
117
+ all.extending(ScopeExtensions)
118
+ .datasource_set(datasource_class: datasource_class)
119
+ end
120
+ end
97
121
  end
98
122
  end
99
123
 
100
124
  module_function
101
125
  def association_reflection(klass, name)
102
- if reflection = klass.reflections[name]
126
+ if reflection = klass.reflect_on_association(name)
103
127
  {
104
128
  klass: reflection.klass,
105
129
  macro: reflection.macro,
@@ -124,6 +148,10 @@ module Datasource
124
148
  scope.loaded?
125
149
  end
126
150
 
151
+ def scope_to_records(scope)
152
+ scope.to_a
153
+ end
154
+
127
155
  def has_attribute?(record, name)
128
156
  record.attributes.key?(name.to_s)
129
157
  end
@@ -136,22 +164,50 @@ module Datasource
136
164
  end
137
165
  end
138
166
 
139
- def load_association(records, name, assoc_select)
167
+ def association_loaded?(records, name, assoc_select)
168
+ if records.first.association(name).loaded?
169
+ all_loaded = records.all? { |record| record.association(name).loaded? }
170
+ if assoc_select == ["*"]
171
+ all_loaded
172
+ elsif all_loaded
173
+ records.all? do |record|
174
+ assoc_sample = Array(record.send(name)).first
175
+ assoc_sample.nil? || assoc_sample._datasource_instance
176
+ end
177
+ else
178
+ false
179
+ end
180
+ else
181
+ false
182
+ end
183
+ end
184
+
185
+ def load_association(records, name, assoc_select, params)
140
186
  return if records.empty?
141
- return if records.first.association(name.to_sym).loaded?
187
+ name = name.to_sym
142
188
  klass = records.first.class
143
- if reflection = klass.reflections[name.to_sym]
189
+ if reflection = klass.reflect_on_association(name)
144
190
  assoc_class = association_klass(reflection)
145
191
  datasource_class = assoc_class.default_datasource
146
192
 
147
193
  scope = assoc_class.all
148
194
  datasource = datasource_class.new(scope)
149
195
  assoc_select_attributes = assoc_select.reject { |att| att.kind_of?(Hash) }
150
- assoc_select_associations = assoc_select.select { |att| att.kind_of?(Hash) }
151
- Datasource::Base.reflection_select(association_reflection(klass, name.to_sym), [], assoc_select_attributes)
196
+ assoc_select_associations = assoc_select.inject({}) do |hash, att|
197
+ hash.deep_merge!(att) if att.kind_of?(Hash)
198
+ hash
199
+ end
200
+ Datasource::Base.reflection_select(association_reflection(klass, name), [], assoc_select_attributes)
201
+ datasource.params(params)
202
+
203
+ Datasource.logger.debug { "load_association #{records.first.try!(:class)} #{name}: #{assoc_select_attributes.inspect}" }
152
204
  datasource.select(*assoc_select_attributes)
153
205
  select_values = datasource.get_select_values
154
206
 
207
+ # TODO: manually load associations, and load them all at once for
208
+ # nested associations, eg. in following, load all Users in 1 query:
209
+ # {"user"=>["*"], "players"=>["*"], "picked_players"=>["*",
210
+ # {:position=>["*"]}], "parent_picked_team"=>["*", {:user=>["*"]}]}
155
211
  begin
156
212
  ::ActiveRecord::Associations::Preloader
157
213
  .new.preload(records, name, assoc_class.select(*select_values))
@@ -161,12 +217,16 @@ module Datasource
161
217
  end
162
218
 
163
219
  assoc_records = records.flat_map { |record| record.send(name) }.compact
164
- assoc_select_associations.each do |assocs|
165
- assocs.each_pair do |assoc_name, assoc_select|
166
- load_association(assoc_records, assoc_name, assoc_select)
220
+ unless assoc_records.empty?
221
+ if Datasource.logger.info? && !assoc_select_associations.empty?
222
+ Datasource.logger.info { "Loading associations " + assoc_select_associations.keys.map(&:to_s).join(", ") + " for #{assoc_records.first.try!(:class)}s" }
167
223
  end
224
+ assoc_select_associations.each_pair do |assoc_name, assoc_select|
225
+ Datasource.logger.debug { "load_association nested association #{assoc_name}: #{assoc_select.inspect}" }
226
+ load_association(assoc_records, assoc_name, assoc_select, params)
227
+ end
228
+ datasource.results(assoc_records)
168
229
  end
169
- datasource.results(assoc_records)
170
230
  end
171
231
  rescue Exception => ex
172
232
  if ex.is_a?(SystemStackError) || ex.is_a?(Datasource::RecursionError)
@@ -187,13 +247,18 @@ module Datasource
187
247
  end
188
248
 
189
249
  def upgrade_records(ds, records)
250
+ Datasource.logger.debug { "Upgrading records #{records.map(&:class).map(&:name).join(', ')}" }
190
251
  load_associations(ds, records)
191
252
  ds.results(records)
192
253
  end
193
254
 
194
255
  def load_associations(ds, records)
256
+ if Datasource.logger.info? && !ds.expose_associations.empty?
257
+ Datasource.logger.info { "Loading associations " + ds.expose_associations.keys.map(&:to_s).join(", ") + " for #{records.first.try!(:class)}s" }
258
+ end
259
+ Datasource.logger.debug { "load_associations (#{records.size} #{records.first.try!(:class)}): #{ds.expose_associations.inspect}" }
195
260
  ds.expose_associations.each_pair do |assoc_name, assoc_select|
196
- load_association(records, assoc_name, assoc_select)
261
+ load_association(records, assoc_name, assoc_select, ds.params)
197
262
  end
198
263
  end
199
264
 
@@ -207,8 +272,8 @@ module Datasource
207
272
  ds.select(*append_select)
208
273
 
209
274
  scope = select_scope(ds)
210
- if scope.respond_to?(:use_datasource)
211
- scope = scope.spawn.use_datasource(nil)
275
+ if scope.respond_to?(:datasource_set)
276
+ scope = scope.spawn.datasource_set(datasource_class: nil)
212
277
  end
213
278
  scope.includes_values = []
214
279
  scope.to_a.tap do |records|