datasource 0.1.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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|