datasource 0.0.5 → 0.0.6

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: 107812940a2c8fc80ebec710b0ebd63c5f710fd4
4
- data.tar.gz: b0d4b77a9cace8c1f559d39738065cfe69930c55
3
+ metadata.gz: 89a34ecb59d8f4183295c43b4badd33aca5f5e62
4
+ data.tar.gz: b408320273ce49236bd022d7db00c1fce06bdfd5
5
5
  SHA512:
6
- metadata.gz: 960aa77805c5fe4e0a2c1ac658956bf0c2b4d8e81769c96850a012b5e95facac053ba6407f1b8d88ec65749b7ecc813fb6229502302ea4d070873b4483d8113f
7
- data.tar.gz: 22dad36f267317cb360ffc30d035b8efd3074faa3ae801e26bbef76c3b9c9d358f417f8a0dee31cf37ea9a49c92e33afe472a753b2d86276e13319cf72b057e8
6
+ metadata.gz: 0db668df6f4ae71223655a544d59b805e6bdd2694ab540f37dacc7962700d427799f00308d0cdf03a180a7e45f7d3891ff795e73def10c180bf9df5d92942df3
7
+ data.tar.gz: c69ae3fe12467516c7f24d090e7240a398e9e7d341dd82a1c54b459c57dd4fb2f9c796a6180a85e02fc304ea45bbf96361579af3249974df4fd73cc0a02dd997
data/README.md CHANGED
@@ -18,12 +18,12 @@ Run install generator:
18
18
  rails g datasource:install
19
19
  ```
20
20
 
21
- ### ORM support
21
+ #### ORM support
22
22
 
23
23
  - ActiveRecord
24
24
  - Sequel
25
25
 
26
- ### Serializer support
26
+ #### Serializer support
27
27
 
28
28
  - active_model_serializers
29
29
 
@@ -66,18 +66,16 @@ SELECT id, title, user_id FROM posts WHERE id IN (?)
66
66
  ```
67
67
 
68
68
  ### Model Methods / Virtual Attributes
69
- You need to specify which database columns a method depends on to be able to use it.
70
- The method itself can be either in the serializer or in your model, it doesn't matter.
71
-
72
- You can list multiple dependency columns.
69
+ You need to use `computed` in a `datasource_module` block to specify what a method depends on. It can depend on database columns, other computed attributes or loaders.
73
70
 
74
71
  ```ruby
75
72
  class User < ActiveRecord::Base
76
73
  datasource_module do
77
74
  computed :first_name_initial, :first_name
78
- computed :last_name_initial, :last_name
75
+ computed :both_initials, :first_name, :last_name
79
76
  end
80
77
 
78
+ # method can be in model
81
79
  def first_name_initial
82
80
  first_name[0].upcase
83
81
  end
@@ -86,8 +84,9 @@ end
86
84
  class UserSerializer < ActiveModel::Serializer
87
85
  attributes :first_name_initial, :last_name_initial
88
86
 
89
- def last_name_initial
90
- object.last_name[0].upcase
87
+ # method can also be in serializer
88
+ def both_initials
89
+ object.last_name[0].upcase + object.last_name[0].upcase
91
90
  end
92
91
  end
93
92
  ```
@@ -100,15 +99,15 @@ You will be reminded with an exception if you forget to do this.
100
99
 
101
100
  ### Show action
102
101
 
103
- You will probably want to reuse the same preloading rules in your show action.
104
- You just need to call `.for_serializer` on the scope. You can optionally give it
105
- the serializer class as an argument.
102
+ You will probably want to reuse the same preloading logic in your show action.
103
+ You will need to call `for_serializer` on the scope before you call `find`.
104
+ You can optionally give it the serializer class as an argument.
106
105
 
107
106
  ```ruby
108
- class UsersController < ApplicationController
107
+ class PostsController < ApplicationController
109
108
  def show
110
109
  post = Post.for_serializer.find(params[:id])
111
- # also works:
110
+ # more explicit:
112
111
  # post = Post.for_serializer(PostSerializer).find(params[:id])
113
112
 
114
113
  render json: post
@@ -116,9 +115,22 @@ class UsersController < ApplicationController
116
115
  end
117
116
  ```
118
117
 
118
+ You can also use it on an existing record, but doing it this way may result in
119
+ an additional SQL query (for example if you use query attributes).
120
+
121
+ ```ruby
122
+ class UsersController < ApplicationController
123
+ def show
124
+ user = current_user.for_serializer
125
+
126
+ render json: user
127
+ end
128
+ end
129
+ ```
130
+
119
131
  ## Advanced Usage
120
132
 
121
- ### Query attributes
133
+ ### Query attribute
122
134
 
123
135
  You can specify a SQL fragment for `SELECT` and use that as an attribute on your
124
136
  model. As a simple example you can concatenate 2 strings together in SQL:
@@ -141,11 +153,13 @@ end
141
153
  SELECT users.id, (users.first_name || ' ' || users.last_name) AS full_name FROM users
142
154
  ```
143
155
 
144
- ### Loaders
156
+ Note: If you need data from another table, use a join in a loader (see below).
157
+
158
+ ### Loader
145
159
 
146
160
  You might want to have some more complex preloading logic. In that case you can use a loader.
147
- The loader will receive ids of the records, and you need to return a hash with your data.
148
- The key of the hash must be the id of the record for which the data is.
161
+ A loader will receive ids of the records, and needs to return a hash.
162
+ The key of the hash must be the id of the record for which the value is.
149
163
 
150
164
  A loader will only be executed if a computed attribute depends on it. If an attribute depends
151
165
  on multiple loaders, pass an array of loaders like so `computed :attr, loaders: [:loader1, :loader2]`.
@@ -156,7 +170,7 @@ will be nil. However you can use the `default` option for such cases (see below
156
170
  ```ruby
157
171
  class User < ActiveRecord::Base
158
172
  datasource_module do
159
- computed :post_count, loaders: :post_counts
173
+ computed :post_count, loader: :post_counts
160
174
  loader :post_counts, array_to_hash: true, default: 0 do |user_ids|
161
175
  results = Post
162
176
  .where(user_id: user_ids)
@@ -210,3 +224,58 @@ loader :stuff, group_by: "user_id", one: true do |ids|
210
224
  # { 10 => { "title" => "Something", "user_id" => 10 } }
211
225
  end
212
226
  ```
227
+
228
+ ### Loaded
229
+
230
+ Loaded is the same as loader, but it also creates a computed attribute and defines
231
+ a method with the same name on your model.
232
+
233
+ Here is the previous example with `loaded` instead of `loader`:
234
+
235
+ ```ruby
236
+ class User < ActiveRecord::Base
237
+ datasource_module do
238
+ loaded :post_count, array_to_hash: true, default: 0 do |user_ids|
239
+ results = Post
240
+ .where(user_id: user_ids)
241
+ .group(:user_id)
242
+ .pluck("user_id, COUNT(id)")
243
+ end
244
+ end
245
+ end
246
+
247
+ class UserSerializer < ActiveModel::Serializer
248
+ attributes :id, :post_count
249
+ # Note that the User now has a generated post_count method
250
+ end
251
+ ```
252
+
253
+ When using `loaded`, if you already have the method with this name defined in your
254
+ model, datasource will automatically create a 'wrapper' method that will use the
255
+ loaded value if available (when you are using a serializer/datasource), otherwise
256
+ it will fallback to your original method. This way you can still use the same
257
+ method when you are not using a serializer/datasource. For example:
258
+
259
+ ```ruby
260
+ class User < ActiveRecord::Base
261
+ datasource_module do
262
+ loaded :post_count, array_to_hash: true, default: 0 do |user_ids|
263
+ results = Post
264
+ .where(user_id: user_ids)
265
+ .group(:user_id)
266
+ .pluck("user_id, COUNT(id)")
267
+ end
268
+
269
+ def post_count
270
+ posts.count
271
+ end
272
+ end
273
+ end
274
+
275
+ class UserSerializer < ActiveModel::Serializer
276
+ attributes :id, :post_count # <- post_count will be read from loaded_values
277
+ end
278
+
279
+ User.first.post_count # <- your method will be called
280
+
281
+ ```
@@ -20,17 +20,22 @@ module Datasource
20
20
  self
21
21
  end
22
22
 
23
+ def get_datasource
24
+ datasource = @datasource.new(self)
25
+ datasource.select(*Array(@datasource_select))
26
+ if @datasource_serializer
27
+ select = []
28
+ Datasource::Base.consumer_adapter.to_datasource_select(select, @datasource.orm_klass, @datasource_serializer, nil, datasource.adapter)
29
+
30
+ datasource.select(*select)
31
+ end
32
+ datasource
33
+ end
34
+
23
35
  private
24
36
  def exec_queries
25
37
  if @datasource
26
- datasource = @datasource.new(self)
27
- datasource.select(*Array(@datasource_select))
28
- if @datasource_serializer
29
- select = []
30
- Datasource::Base.consumer_adapter.to_datasource_select(select, @datasource.orm_klass, @datasource_serializer, nil, datasource.adapter)
31
-
32
- datasource.select(*select)
33
- end
38
+ datasource = get_datasource
34
39
 
35
40
  @loaded = true
36
41
  @records = datasource.results
@@ -47,6 +52,22 @@ module Datasource
47
52
  attr_accessor :loaded_values
48
53
  end
49
54
 
55
+ def for_serializer(serializer = nil)
56
+ datasource_class = self.class.default_datasource
57
+ pk = datasource_class.primary_key.to_sym
58
+
59
+ scope = self.class
60
+ .with_datasource(datasource_class)
61
+ .for_serializer(serializer).where(pk => send(pk))
62
+
63
+ datasource = scope.get_datasource
64
+ if datasource.can_upgrade?(self)
65
+ datasource.upgrade_records(self).first
66
+ else
67
+ scope.first
68
+ end
69
+ end
70
+
50
71
  module ClassMethods
51
72
  def for_serializer(serializer = nil)
52
73
  scope = if all.respond_to?(:use_datasource_serializer)
@@ -67,7 +88,7 @@ module Datasource
67
88
  end
68
89
 
69
90
  def default_datasource
70
- @default_datasource ||= Class.new(Datasource::From(self))
91
+ @default_datasource ||= Datasource::From(self)
71
92
  end
72
93
 
73
94
  def datasource_module(&block)
@@ -103,6 +124,10 @@ module Datasource
103
124
  scope.loaded?
104
125
  end
105
126
 
127
+ def has_attribute?(record, name)
128
+ record.attributes.key?(name.to_s)
129
+ end
130
+
106
131
  def association_klass(reflection)
107
132
  if reflection.macro == :belongs_to && reflection.options[:polymorphic]
108
133
  fail Datasource::Error, "polymorphic belongs_to not supported, write custom loader"
@@ -111,7 +136,7 @@ module Datasource
111
136
  end
112
137
  end
113
138
 
114
- def preload_association(records, name)
139
+ def load_association(records, name)
115
140
  return if records.empty?
116
141
  return if records.first.association(name.to_sym).loaded?
117
142
  klass = records.first.class
@@ -125,7 +150,7 @@ module Datasource
125
150
  scope = assoc_class.all
126
151
  datasource = datasource_class.new(scope)
127
152
  datasource_select = serializer_class._attributes.dup
128
- Datasource::Base.reflection_select(Adapters::ActiveRecord.association_reflection(klass, name.to_sym), [], datasource_select)
153
+ Datasource::Base.reflection_select(association_reflection(klass, name.to_sym), [], datasource_select)
129
154
  datasource.select(*datasource_select)
130
155
  select_values = datasource.get_select_values
131
156
 
@@ -139,7 +164,7 @@ module Datasource
139
164
 
140
165
  assoc_records = records.flat_map { |record| record.send(name) }.compact
141
166
  serializer_class._associations.each_pair do |assoc_name, options|
142
- preload_association(assoc_records, assoc_name)
167
+ load_association(assoc_records, assoc_name)
143
168
  end
144
169
  datasource.results(assoc_records)
145
170
  end
@@ -161,10 +186,21 @@ module Datasource
161
186
  ds.scope.select(*ds.get_select_values)
162
187
  end
163
188
 
189
+ def upgrade_records(ds, records)
190
+ load_associations(ds, records)
191
+ ds.results(records)
192
+ end
193
+
194
+ def load_associations(ds, records)
195
+ ds.expose_associations.each_pair do |assoc_name, assoc_select|
196
+ load_association(records, assoc_name)
197
+ end
198
+ end
199
+
164
200
  def get_rows(ds)
165
201
  append_select = []
166
202
  ds.expose_associations.each_pair do |assoc_name, assoc_select|
167
- if reflection = Adapters::ActiveRecord.association_reflection(ds.class.orm_klass, assoc_name.to_sym)
203
+ if reflection = association_reflection(ds.class.orm_klass, assoc_name.to_sym)
168
204
  Datasource::Base.reflection_select(reflection, append_select, [])
169
205
  end
170
206
  end
@@ -176,9 +212,7 @@ module Datasource
176
212
  end
177
213
  scope.includes_values = []
178
214
  scope.to_a.tap do |records|
179
- ds.expose_associations.each_pair do |assoc_name, assoc_select|
180
- Adapters::ActiveRecord.preload_association(records, assoc_name)
181
- end
215
+ load_associations(ds, records)
182
216
  end
183
217
  end
184
218
 
@@ -214,7 +248,7 @@ module Datasource
214
248
  Datasource::Adapters::ActiveRecord
215
249
  end
216
250
 
217
- define_method(:primary_key) do
251
+ define_singleton_method(:primary_key) do
218
252
  klass.primary_key.to_sym
219
253
  end
220
254
  end
@@ -14,6 +14,18 @@ module Datasource
14
14
  self
15
15
  end
16
16
 
17
+ def get_datasource
18
+ datasource = @datasource.new(self)
19
+ datasource.select(*Array(@datasource_select))
20
+ if @datasource_serializer
21
+ select = []
22
+ Datasource::Base.consumer_adapter.to_datasource_select(select, @datasource.orm_klass, @datasource_serializer, nil, datasource.adapter)
23
+
24
+ datasource.select(*select)
25
+ end
26
+ datasource
27
+ end
28
+
17
29
  def datasource_select(*args)
18
30
  @datasource_select = Array(@datasource_select) + args
19
31
  self
@@ -21,14 +33,7 @@ module Datasource
21
33
 
22
34
  def each(&block)
23
35
  if @datasource
24
- datasource = @datasource.new(self)
25
- datasource.select(*Array(@datasource_select))
26
- if @datasource_serializer
27
- select = []
28
- Datasource::Base.consumer_adapter.to_datasource_select(select, @datasource.orm_klass, @datasource_serializer, nil, datasource.adapter)
29
-
30
- datasource.select(*select)
31
- end
36
+ datasource = get_datasource
32
37
 
33
38
  datasource.results.each(&block)
34
39
  else
@@ -64,9 +69,25 @@ module Datasource
64
69
  end
65
70
  end
66
71
 
72
+ def for_serializer(serializer = nil)
73
+ datasource_class = self.class.default_datasource
74
+ pk = datasource_class.primary_key.to_sym
75
+
76
+ scope = self.class
77
+ .with_datasource(datasource_class)
78
+ .for_serializer(serializer).where(pk => send(pk))
79
+
80
+ datasource = scope.get_datasource
81
+ if datasource.can_upgrade?(self)
82
+ datasource.upgrade_records(self).first
83
+ else
84
+ scope.first
85
+ end
86
+ end
87
+
67
88
  module ClassMethods
68
89
  def default_datasource
69
- @default_datasource ||= Class.new(Datasource::From(self))
90
+ @default_datasource ||= Datasource::From(self)
70
91
  end
71
92
 
72
93
  def datasource_module(&block)
@@ -113,8 +134,12 @@ module Datasource
113
134
  false
114
135
  end
115
136
 
137
+ def has_attribute?(record, name)
138
+ record.values.key?(name.to_sym)
139
+ end
140
+
116
141
  def get_assoc_eager_options(klass, name, assoc_select, append_select)
117
- if reflection = Adapters::Sequel.association_reflection(klass, name)
142
+ if reflection = association_reflection(klass, name)
118
143
  self_append_select = []
119
144
  Datasource::Base.reflection_select(reflection, append_select, self_append_select)
120
145
  assoc_class = reflection[:klass]
@@ -144,7 +169,12 @@ module Datasource
144
169
  ds.scope.select(*get_sequel_select_values(ds.get_select_values))
145
170
  end
146
171
 
147
- def get_rows(ds)
172
+ def upgrade_records(ds, records)
173
+ get_final_scope(ds).send :post_load, records
174
+ ds.results(records)
175
+ end
176
+
177
+ def get_final_scope(ds)
148
178
  eager = {}
149
179
  append_select = []
150
180
  ds.expose_associations.each_pair do |assoc_name, assoc_select|
@@ -158,7 +188,11 @@ module Datasource
158
188
  end
159
189
  scope
160
190
  .select_append(*get_sequel_select_values(append_select.map { |v| primary_scope_table(ds) + ".#{v}" }))
161
- .eager(eager).all
191
+ .eager(eager)
192
+ end
193
+
194
+ def get_rows(ds)
195
+ get_final_scope(ds).all
162
196
  end
163
197
 
164
198
  def primary_scope_table(ds)
@@ -187,7 +221,7 @@ module Datasource
187
221
  Datasource::Adapters::Sequel
188
222
  end
189
223
 
190
- define_method(:primary_key) do
224
+ define_singleton_method(:primary_key) do
191
225
  klass.primary_key
192
226
  end
193
227
  end
@@ -2,16 +2,23 @@ module Datasource
2
2
  module Attributes
3
3
  class ComputedAttribute
4
4
  class << self
5
- attr_accessor :_depends
5
+ attr_accessor :_depends, :_loader_depends
6
6
 
7
7
  def inherited(base)
8
8
  base._depends = (_depends || {}).dup # TODO: deep dup?
9
+ base._loader_depends = (_loader_depends || []).dup # TODO: deep dup?
9
10
  end
10
11
 
11
12
  def depends(*args)
12
13
  args.each do |dep|
13
14
  _depends.deep_merge!(dep)
14
15
  end
16
+ _depends.delete_if do |key, value|
17
+ if [:loaders, :loader].include?(key.to_sym)
18
+ self._loader_depends += Array(value).map(&:to_sym)
19
+ true
20
+ end
21
+ end
15
22
  end
16
23
  end
17
24
  end
@@ -65,5 +65,22 @@ module Datasource
65
65
  end
66
66
  @_loaders[name.to_sym] = klass
67
67
  end
68
+
69
+ def self.loaded(name, _options = {}, &block)
70
+ loader(name, _options, &block)
71
+ renamed_existing_method = :"#{name}_without_datasource"
72
+ orm_klass.class_eval do
73
+ alias_method renamed_existing_method, name if method_defined?(name)
74
+ fail "#{name} already defined on #{to_s}, would be overridden by datasource loaded method" if method_defined?(name)
75
+ define_method name do |*args, &block|
76
+ if loaded_values || !method_defined?(renamed_existing_method)
77
+ loaded_values[name.to_sym]
78
+ else
79
+ send(renamed_existing_method, *args, &block)
80
+ end
81
+ end
82
+ end
83
+ computed name, loader: name
84
+ end
68
85
  end
69
86
  end
@@ -24,6 +24,10 @@ module Datasource
24
24
  fail Datasource::Error, "Model class not set for #{name}. You should define it:\nclass YourDatasource\n @orm_klass = MyModelClass\nend"
25
25
  end
26
26
 
27
+ def primary_key
28
+ :id
29
+ end
30
+
27
31
  def reflection_select(reflection, parent_select, assoc_select)
28
32
  # append foreign key depending on assoication
29
33
  if reflection[:macro] == :belongs_to
@@ -81,16 +85,14 @@ module Datasource
81
85
  @expose_associations = {}
82
86
  end
83
87
 
84
- def primary_key
85
- :id
86
- end
87
-
88
88
  def select_all
89
89
  @expose_attributes = self.class._attributes.keys.dup
90
90
  end
91
91
 
92
92
  def select(*names)
93
93
  failure = ->(name) { fail Datasource::Error, "attribute or association #{name} doesn't exist for #{self.class.orm_klass.name}, did you forget to call \"computed :#{name}, <dependencies>\" in your datasource_module?" }
94
+ newly_exposed_attributes = []
95
+ missing_attributes = []
94
96
  names.each do |name|
95
97
  if name.kind_of?(Hash)
96
98
  name.each_pair do |assoc_name, assoc_select|
@@ -100,53 +102,80 @@ module Datasource
100
102
  @expose_associations[assoc_name] += Array(assoc_select)
101
103
  @expose_associations[assoc_name].uniq!
102
104
  else
105
+ missing_attributes << assoc_name
103
106
  failure.call(assoc_name)
104
107
  end
105
108
  end
106
109
  else
107
110
  name = name.to_s
108
111
  if self.class._attributes.key?(name)
109
- @expose_attributes.push(name)
112
+ unless @expose_attributes.include?(name)
113
+ @expose_attributes.push(name)
114
+ newly_exposed_attributes.push(name)
115
+ end
110
116
  else
117
+ missing_attributes << name
111
118
  failure.call(name)
112
119
  end
113
120
  end
114
121
  end
115
- @expose_attributes.uniq!
122
+ update_dependencies(newly_exposed_attributes) unless newly_exposed_attributes.empty?
123
+ fail_missing_attributes(missing_attributes) unless missing_attributes.blank?
116
124
  self
117
125
  end
118
126
 
127
+ def fail_missing_attributes(names)
128
+ message = if names.size > 1
129
+ "attributes or associations #{names.join(', ')} don't exist "
130
+ else
131
+ "attribute or association #{names.first} doesn't exist "
132
+ end
133
+ message += "for #{self.class.orm_klass.name}, "
134
+ message += "did you forget to call \"computed :#{name}, <dependencies>\" in your datasource_module?"
135
+ fail Datasource::Error, message
136
+ end
137
+
138
+ def update_dependencies(names)
139
+ scope_table = adapter.primary_scope_table(self)
140
+
141
+ self.class._attributes.values.each do |att|
142
+ next unless names.include?(att[:name])
143
+ next unless att[:klass]
144
+
145
+ if att[:klass].ancestors.include?(Attributes::ComputedAttribute)
146
+ att[:klass]._depends.each_pair do |key, value|
147
+ if key.to_s == scope_table
148
+ select(*value)
149
+ else
150
+ select(key => value)
151
+ end
152
+ end
153
+ elsif att[:klass].ancestors.include?(Attributes::QueryAttribute)
154
+ att[:klass]._depends.each do |name|
155
+ next if name == scope_table
156
+ adapter.ensure_table_join!(self, name, att)
157
+ end
158
+ end
159
+ end
160
+ end
161
+
119
162
  def get_select_values
120
163
  scope_table = adapter.primary_scope_table(self)
164
+
165
+ # SQL select values
121
166
  select_values = Set.new
122
- select_values.add("#{scope_table}.#{primary_key}")
167
+ select_values.add("#{scope_table}.#{self.class.primary_key}")
123
168
 
124
169
  self.class._attributes.values.each do |att|
125
170
  if attribute_exposed?(att[:name])
126
171
  if att[:klass] == nil
127
172
  select_values.add("#{scope_table}.#{att[:name]}")
128
- elsif att[:klass].ancestors.include?(Attributes::ComputedAttribute)
129
- att[:klass]._depends.keys.map(&:to_s).each do |name|
130
- next if name == scope_table
131
- next if name == "loaders"
132
- adapter.ensure_table_join!(self, name, att)
133
- end
134
- att[:klass]._depends.each_pair do |table, names|
135
- next if table.to_sym == :loaders
136
- Array(names).each do |name|
137
- select_values.add("#{table}.#{name}")
138
- end
139
- # TODO: handle depends on virtual attribute
140
- end
141
173
  elsif att[:klass].ancestors.include?(Attributes::QueryAttribute)
142
174
  select_values.add("(#{att[:klass].select_value}) as #{att[:name]}")
143
- att[:klass]._depends.each do |name|
144
- next if name == scope_table
145
- adapter.ensure_table_join!(self, name, att)
146
- end
147
175
  end
148
176
  end
149
177
  end
178
+
150
179
  select_values.to_a
151
180
  end
152
181
 
@@ -154,6 +183,27 @@ module Datasource
154
183
  @expose_attributes.include?(name.to_s)
155
184
  end
156
185
 
186
+ # assume records have all attributes selected (default ORM record)
187
+ def can_upgrade?(records)
188
+ query_attributes = @expose_attributes.select do |name|
189
+ klass = self.class._attributes[name][:klass]
190
+ if klass
191
+ klass.ancestors.include?(Attributes::QueryAttribute)
192
+ end
193
+ end
194
+
195
+ return true if query_attributes.empty?
196
+ Array(records).all? do |record|
197
+ query_attributes.all? do |name|
198
+ adapter.has_attribute?(record, name)
199
+ end
200
+ end
201
+ end
202
+
203
+ def upgrade_records(records)
204
+ adapter.upgrade_records(self, Array(records))
205
+ end
206
+
157
207
  def results(rows = nil)
158
208
  rows ||= adapter.get_rows(self)
159
209
 
@@ -166,23 +216,20 @@ module Datasource
166
216
  next if rows.empty?
167
217
 
168
218
  if att[:klass].ancestors.include?(Attributes::ComputedAttribute)
169
- loaders = att[:klass]._depends[:loaders]
170
- if loaders
171
- Array(loaders).each do |name|
172
- if loader = self.class._loaders[name]
173
- if loaded_values = loader.load(rows.map(&primary_key), rows, @scope)
174
- unless rows.first.loaded_values
175
- rows.each do |row|
176
- row.loaded_values = {}
177
- end
178
- end
219
+ att[:klass]._loader_depends.each do |name|
220
+ if loader = self.class._loaders[name]
221
+ if loaded_values = loader.load(rows.map(&self.class.primary_key), rows, @scope)
222
+ unless rows.first.loaded_values
179
223
  rows.each do |row|
180
- row.loaded_values[name] = loaded_values[row.send(primary_key)] || loader.default_value
224
+ row.loaded_values = {}
181
225
  end
182
226
  end
183
- else
184
- raise Datasource::Error, "loader with name :#{name} could not be found"
227
+ rows.each do |row|
228
+ row.loaded_values[name] = loaded_values[row.send(self.class.primary_key)] || loader.default_value
229
+ end
185
230
  end
231
+ else
232
+ raise Datasource::Error, "loader with name :#{name} could not be found"
186
233
  end
187
234
  end
188
235
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datasource
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.5
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Berdajs