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 +4 -4
- data/README.md +129 -102
- data/lib/datasource/adapters/active_record.rb +106 -41
- data/lib/datasource/adapters/sequel.rb +53 -30
- data/lib/datasource/attributes/loaded.rb +101 -0
- data/lib/datasource/base.rb +90 -29
- data/lib/datasource/collection_context.rb +32 -0
- data/lib/datasource/consumer_adapters/active_model_serializers.rb +66 -4
- data/lib/datasource.rb +13 -12
- metadata +9 -9
- data/lib/datasource/attributes/loader.rb +0 -86
- data/lib/datasource/serializer.rb +0 -117
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6c4654c0cc97e0cba3fd8ce3d9b99a520d590afa
|
4
|
+
data.tar.gz: a37d72813c8e2d0a6ef6430a380115126f28dfad
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
45
|
-
|
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
|
-
|
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
|
117
|
+
Note: If you need data from another table, use a join in a loaded value (see below).
|
121
118
|
|
122
|
-
###
|
119
|
+
### Standalone Datasource class
|
123
120
|
|
124
|
-
|
125
|
-
|
126
|
-
|
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
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
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
|
-
|
137
|
-
|
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
|
141
|
-
|
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
|
-
|
153
|
-
|
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
|
-
|
156
|
-
|
157
|
-
|
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
|
-
|
163
|
-
|
164
|
-
|
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
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
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
|
-
|
177
|
-
|
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
|
-
|
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
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
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
|
-
|
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
|
-
|
200
|
-
|
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
|
-
|
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
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
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
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
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
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
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
|
-
|
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
|
-
|
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/
|
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/
|
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
|
9
|
-
|
10
|
-
|
8
|
+
def self.extended(mod)
|
9
|
+
mod.instance_exec do
|
10
|
+
@datasource_info ||= { select: [], params: [] }
|
11
|
+
end
|
11
12
|
end
|
12
13
|
|
13
|
-
def
|
14
|
-
@
|
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
|
-
@
|
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
|
-
|
25
|
-
datasource.
|
26
|
-
|
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,
|
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 @
|
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 :
|
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)
|
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(
|
73
|
-
scope =
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
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(
|
82
|
-
|
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 ||=
|
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.
|
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
|
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
|
-
|
187
|
+
name = name.to_sym
|
142
188
|
klass = records.first.class
|
143
|
-
if reflection = klass.
|
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.
|
151
|
-
|
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
|
-
|
165
|
-
|
166
|
-
|
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?(:
|
211
|
-
scope = scope.spawn.
|
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|
|