datasource 0.2.0 → 0.3.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: 6c4654c0cc97e0cba3fd8ce3d9b99a520d590afa
4
- data.tar.gz: a37d72813c8e2d0a6ef6430a380115126f28dfad
3
+ metadata.gz: acadf2996a54023f7ea9ec464ae0b82ad0c57dc9
4
+ data.tar.gz: 7f1759d99758a076f66787bb152c147cbb0ba714
5
5
  SHA512:
6
- metadata.gz: 6d68a095c8b8af62be19f3fbb7048b634b5d0fb5d113f3e81d6e1430959d69bc9f93cce68d02977066ff782361c7915b0a3cab0a9003b85598caa019a5bb5c7e
7
- data.tar.gz: 4cf182d351a3bfc21f6dfcd2062eb97f21f36dd39ccf9ef4bfaf6cca418aea7b7fc9105fe613936f95f19a4a32d74714e51b27ebccfc15fa9de9a7b716f25dab
6
+ metadata.gz: a8d565d3e8d3e2e44ee7c3c22f27f8e776b10f3b301c6f252a996b9130d1a39c15da04e92530d1a5eb81b892815d7377b61d2aaadeadd1841c22df1c5199a0ab
7
+ data.tar.gz: c40d2192fcdaf926b25f4ece6f8378a763e63459d1ff5a673d06a14e32fbe2183c26b43de1a3f49955b5c73eb9ab6a18bfbc6c6e03b3f78dbefa0d6930a83d5f
data/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # Datasource
2
2
 
3
- - Automatically preload associations for your serializers
4
- - Specify custom SQL snippets for virtual attributes (Query attributes)
5
- - Write custom preloading logic in a reusable way
3
+ **Please see gem [active_loaders](https://github.com/kundi/active_loaders)
4
+ documentation for now, it includes all the necessary information**
6
5
 
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) **
6
+ Documentation for datasource will be updated later. Active Model Serializer support
7
+ was extracted into the active_loaders gem.
8
8
 
9
9
  #### Install
10
10
 
@@ -32,237 +32,6 @@ rails g datasource:install
32
32
  - ActiveRecord
33
33
  - Sequel
34
34
 
35
- #### Serializer support
36
-
37
- - active_model_serializers
38
-
39
- ### Associations
40
-
41
- The most noticable magic effect of using Datasource is that associations will
42
- automatically be preloaded using a single query.
43
-
44
- ```ruby
45
- class PostSerializer < ActiveModel::Serializer
46
- attributes :id, :title
47
- end
48
-
49
- class UserSerializer < ActiveModel::Serializer
50
- attributes :id
51
- has_many :posts
52
- end
53
- ```
54
- ```sql
55
- SELECT users.* FROM users
56
- SELECT posts.* FROM posts WHERE id IN (?)
57
- ```
58
-
59
- This means you **do not** need to call `includes` yourself. It will be done
60
- automatically by Datasource.
61
-
62
- ### Show action
63
-
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.
66
- You will need to call `for_serializer` on the scope before you call `find`.
67
- You can optionally give it the serializer class as an argument.
68
-
69
- ```ruby
70
- class PostsController < ApplicationController
71
- def show
72
- post = Post.for_serializer.find(params[:id])
73
- # more explicit:
74
- # post = Post.for_serializer(PostSerializer).find(params[:id])
75
-
76
- render json: post
77
- end
78
- end
79
- ```
80
-
81
- You can also use it on an existing record, but doing it this way may result in
82
- an additional SQL query (for example if you use query attributes).
83
-
84
- ```ruby
85
- class UsersController < ApplicationController
86
- def show
87
- user = current_user.for_serializer
88
-
89
- render json: user
90
- end
91
- end
92
- ```
93
-
94
- ### Query attribute
95
-
96
- You can specify a SQL fragment for `SELECT` and use that as an attribute on your
97
- model. As a simple example you can concatenate 2 strings together in SQL:
98
-
99
- ```ruby
100
- class User < ActiveRecord::Base
101
- datasource_module do
102
- query :full_name do
103
- "users.first_name || ' ' || users.last_name"
104
- end
105
- end
106
- end
107
-
108
- class UserSerializer < ActiveModel::Serializer
109
- attributes :id, :full_name
110
- end
111
- ```
112
-
113
- ```sql
114
- SELECT users.*, (users.first_name || ' ' || users.last_name) AS full_name FROM users
115
- ```
116
-
117
- Note: If you need data from another table, use a join in a loaded value (see below).
118
-
119
- ### Standalone Datasource class
120
-
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:
126
-
127
- ```ruby
128
- class PostDatasource < Datasource::From(Post)
129
- query(:full_name) { "users.first_name || ' ' || users.last_name" }
130
- end
131
- ```
132
-
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):
141
-
142
- ```ruby
143
- class UserDatasource < Datasource::From(User)
144
- loaded :post_count, from: :array, default: 0
145
- end
146
- ```
147
-
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`.
153
-
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
164
- end
165
- end
166
- ```
167
-
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:
171
-
172
- ```ruby
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
- ```
178
-
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
-
182
- The other two are explained in the following example.
183
-
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
195
- end
196
- ```
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.
206
-
207
- In this case, in the load method, we also used `for_serializer`, which will load
208
- the `Comment`s according to the `CommentSerializer`.
209
-
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:
215
-
216
- ```ruby
217
- class User < ActiveRecord::Base
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)
231
- .group(:user_id)
232
- .pluck("user_id, COUNT(id)")
233
- end
234
- end
235
- end
236
-
237
- class UserSerializer < ActiveModel::Serializer
238
- attributes :id, :post_count # <- post_count will be read from load_post_count
239
- end
240
-
241
- User.first.post_count # <- your model method will be called
242
- ```
243
-
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`:
248
-
249
- ```ruby
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
262
- end
263
- end
264
- ```
265
-
266
35
  ### Debugging and logging
267
36
 
268
37
  Datasource outputs some useful logs that you can use debugging. By default the log level is
@@ -270,11 +39,11 @@ set to warnings only, but you can change it. You can add the following line to y
270
39
  `config/initializers/datasource.rb`:
271
40
 
272
41
  ```ruby
273
- Datasource.logger.level = Logger::INFO
42
+ Datasource.logger.level = Logger::INFO unless Rails.env.production?
274
43
  ```
275
44
 
276
45
  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.
46
+ is not recommended to have this enabled in production (simply for performance reasons).
278
47
 
279
48
  ## Getting Help
280
49
 
data/lib/datasource.rb CHANGED
@@ -9,9 +9,7 @@ module Datasource
9
9
  AdapterPaths = {
10
10
  activerecord: 'datasource/adapters/active_record',
11
11
  active_record: :activerecord,
12
- sequel: 'datasource/adapters/sequel',
13
- ams: 'datasource/consumer_adapters/active_model_serializers',
14
- active_model_serializers: :ams
12
+ sequel: 'datasource/adapters/sequel'
15
13
  }
16
14
 
17
15
  module_function
@@ -26,10 +24,11 @@ module_function
26
24
 
27
25
  yield(config)
28
26
 
29
- config.adapters.each do |adapter|
30
- adapter = AdapterPaths[adapter]
31
- adapter = AdapterPaths[adapter] if adapter.is_a?(Symbol)
32
- require adapter
27
+ config.adapters.each do |adapter_name|
28
+ adapter_path = AdapterPaths[adapter_name]
29
+ adapter_path = AdapterPaths[adapter_path] if adapter_path.is_a?(Symbol)
30
+ fail "Unknown Datasource adapter '#{adapter_name}'." unless adapter_path
31
+ require adapter_path
33
32
  end
34
33
  end
35
34
 
@@ -11,6 +11,10 @@ module Datasource
11
11
  end
12
12
  end
13
13
 
14
+ def datasource_get(key)
15
+ @datasource_info[key]
16
+ end
17
+
14
18
  def datasource_set(hash)
15
19
  @datasource_info.merge!(hash)
16
20
  self
@@ -33,7 +37,7 @@ module Datasource
33
37
  datasource.params(*@datasource_info[:params])
34
38
  if @datasource_info[:serializer_class]
35
39
  select = []
36
- Datasource::Base.consumer_adapter.to_datasource_select(select, klass.orm_klass, @datasource_info[:serializer_class], nil, datasource.adapter)
40
+ @datasource_info[:serializer_class].datasource_adapter.to_datasource_select(select, klass.orm_klass, @datasource_info[:serializer_class], nil, datasource.adapter, datasource)
37
41
 
38
42
  datasource.select(*select)
39
43
  end
@@ -63,31 +67,19 @@ module Datasource
63
67
  attr_accessor :_datasource_loaded, :_datasource_instance
64
68
  end
65
69
 
66
- def for_serializer(serializer = nil)
67
- datasource_class = self.class.default_datasource
68
- pk = datasource_class.primary_key.to_sym
69
-
70
- scope = self.class
71
- .with_datasource(datasource_class)
72
- .for_serializer(serializer)
73
- .where(pk => send(pk))
74
-
75
- scope = yield(scope) if block_given?
76
-
77
- datasource = scope.get_datasource
78
- if datasource.can_upgrade?(self)
79
- datasource.upgrade_records(self).first
80
- else
81
- scope.first
82
- end
70
+ def for_serializer(serializer_class = nil, datasource_class = nil)
71
+ self.class.upgrade_for_serializer([self], serializer_class, datasource_class).first
83
72
  end
84
73
 
85
74
  module ClassMethods
86
75
  def for_serializer(serializer_class = nil)
87
- scope = scope_with_datasource_ext
76
+ if Datasource::Base.default_consumer_adapter.nil?
77
+ fail Datasource::Error, "No serializer adapter loaded, see the active_loaders gem."
78
+ end
88
79
  serializer_class ||=
89
- Datasource::Base.consumer_adapter.get_serializer_for(
90
- Adapters::ActiveRecord.scope_to_class(scope))
80
+ Datasource::Base.default_consumer_adapter.get_serializer_for(
81
+ Adapters::ActiveRecord.scope_to_class(all))
82
+ scope = scope_with_datasource_ext(serializer_class.use_datasource)
91
83
  scope.datasource_set(serializer_class: serializer_class)
92
84
  end
93
85
 
@@ -95,6 +87,25 @@ module Datasource
95
87
  scope_with_datasource_ext(datasource_class)
96
88
  end
97
89
 
90
+ def upgrade_for_serializer(records, serializer_class = nil, datasource_class = nil)
91
+ scope = with_datasource(datasource_class).for_serializer(serializer_class)
92
+ records = Array(records)
93
+
94
+ pk = scope.datasource_get(:datasource_class).primary_key.to_sym
95
+ if primary_keys = records.map(&pk)
96
+ scope = scope.where(pk => primary_keys.compact)
97
+ end
98
+
99
+ scope = yield(scope) if block_given?
100
+
101
+ datasource = scope.get_datasource
102
+ if datasource.can_upgrade?(records)
103
+ datasource.upgrade_records(records)
104
+ else
105
+ scope.to_a
106
+ end
107
+ end
108
+
98
109
  def default_datasource
99
110
  @default_datasource ||= begin
100
111
  "#{name}Datasource".constantize
@@ -110,7 +121,11 @@ module Datasource
110
121
  private
111
122
  def scope_with_datasource_ext(datasource_class = nil)
112
123
  if all.respond_to?(:datasource_set)
113
- all
124
+ if datasource_class
125
+ all.datasource_set(datasource_class: datasource_class)
126
+ else
127
+ all
128
+ end
114
129
  else
115
130
  datasource_class ||= default_datasource
116
131
 
@@ -10,6 +10,10 @@ module Datasource
10
10
  end
11
11
  end
12
12
 
13
+ def datasource_get(key)
14
+ @datasource_info[key]
15
+ end
16
+
13
17
  def datasource_set(hash)
14
18
  @datasource_info.merge!(hash)
15
19
  self
@@ -32,7 +36,7 @@ module Datasource
32
36
  datasource.params(*@datasource_info[:params])
33
37
  if @datasource_info[:serializer_class]
34
38
  select = []
35
- Datasource::Base.consumer_adapter.to_datasource_select(select, klass.orm_klass, @datasource_info[:serializer_class], nil, datasource.adapter)
39
+ @datasource_info[:serializer_class].datasource_adapter.to_datasource_select(select, klass.orm_klass, @datasource_info[:serializer_class], nil, datasource.adapter, datasource)
36
40
 
37
41
  datasource.select(*select)
38
42
  end
@@ -58,10 +62,10 @@ module Datasource
58
62
 
59
63
  dataset_module do
60
64
  def for_serializer(serializer_class = nil)
61
- scope = scope_with_datasource_ext
62
65
  serializer_class ||=
63
- Datasource::Base.consumer_adapter
64
- .get_serializer_for(Adapters::Sequel.scope_to_class(scope))
66
+ Datasource::Base.default_consumer_adapter
67
+ .get_serializer_for(Adapters::Sequel.scope_to_class(self))
68
+ scope = scope_with_datasource_ext(serializer_class.use_datasource)
65
69
  scope.datasource_set(serializer_class: serializer_class)
66
70
  end
67
71
 
@@ -72,7 +76,11 @@ module Datasource
72
76
  private
73
77
  def scope_with_datasource_ext(datasource_class = nil)
74
78
  if respond_to?(:datasource_set)
75
- self
79
+ if datasource_class
80
+ datasource_set(datasource_class: datasource_class)
81
+ else
82
+ self
83
+ end
76
84
  else
77
85
  datasource_class ||= Adapters::Sequel.scope_to_class(self).default_datasource
78
86
 
@@ -83,23 +91,8 @@ module Datasource
83
91
  end
84
92
  end
85
93
 
86
- def for_serializer(serializer = nil)
87
- datasource_class = self.class.default_datasource
88
- pk = datasource_class.primary_key.to_sym
89
-
90
- scope = self.class
91
- .with_datasource(datasource_class)
92
- .for_serializer(serializer)
93
- .where(pk => send(pk))
94
-
95
- scope = yield(scope) if block_given?
96
-
97
- datasource = scope.get_datasource
98
- if datasource.can_upgrade?(self)
99
- datasource.upgrade_records(self).first
100
- else
101
- scope.first
102
- end
94
+ def for_serializer(serializer_class = nil, datasource_class = nil)
95
+ self.class.upgrade_for_serializer([self], serializer_class, datasource_class).first
103
96
  end
104
97
 
105
98
  module ClassMethods
@@ -110,6 +103,28 @@ module Datasource
110
103
  def datasource_module(&block)
111
104
  default_datasource.instance_exec(&block)
112
105
  end
106
+
107
+ def upgrade_for_serializer(records, serializer_class = nil, datasource_class = nil)
108
+ # must use filter to get a new scope
109
+ scope = filter.with_datasource(datasource_class).for_serializer(serializer_class)
110
+ records = Array(records)
111
+
112
+ binding.pry if scope.datasource_get(:datasource_class).nil?
113
+
114
+ pk = scope.datasource_get(:datasource_class).primary_key.to_sym
115
+ if primary_keys = records.map(&pk)
116
+ scope = scope.where(pk => primary_keys.compact)
117
+ end
118
+
119
+ scope = yield(scope) if block_given?
120
+
121
+ datasource = scope.get_datasource
122
+ if datasource.can_upgrade?(records)
123
+ datasource.upgrade_records(records)
124
+ else
125
+ scope.all
126
+ end
127
+ end
113
128
  end
114
129
  end
115
130
 
@@ -193,7 +208,11 @@ module Datasource
193
208
 
194
209
  def upgrade_records(ds, records)
195
210
  Datasource.logger.debug { "Upgrading records #{records.map(&:class).map(&:name).join(', ')}" }
196
- get_final_scope(ds).send :post_load, records
211
+ # NOTE: this does not guarantee assocs are loaded, there might be a better way
212
+ assocs_loaded = records.all? { |record|
213
+ record._datasource_instance
214
+ }
215
+ get_final_scope(ds).send :post_load, records unless assocs_loaded
197
216
  ds.results(records)
198
217
  end
199
218
 
@@ -3,6 +3,8 @@ module Datasource
3
3
  class << self
4
4
  attr_accessor :_attributes, :_associations, :_update_scope, :_loaders, :_loader_order, :_collection_context
5
5
  attr_writer :orm_klass
6
+ # Should be set by consumer adapter library (e.g. for ActiveModelSerializers)
7
+ attr_accessor :default_consumer_adapter
6
8
 
7
9
  def inherited(base)
8
10
  base._attributes = (_attributes || {}).dup
@@ -18,10 +20,6 @@ module Datasource
18
20
  end
19
21
  end
20
22
 
21
- def consumer_adapter
22
- @consumer_adapter = Datasource::ConsumerAdapters::ActiveModelSerializers
23
- end
24
-
25
23
  def orm_klass
26
24
  fail Datasource::Error, "Model class not set for #{name}. You should define it:\nclass YourDatasource\n @orm_klass = MyModelClass\nend"
27
25
  end
@@ -45,6 +43,12 @@ module Datasource
45
43
  _collection_context.class_exec(&block)
46
44
  end
47
45
 
46
+ def _column_attribute_names
47
+ column_attributes = _attributes.values.select { |att|
48
+ att[:klass].nil?
49
+ }.map { |att| att[:name] }
50
+ end
51
+
48
52
  private
49
53
  def attributes(*attrs)
50
54
  attrs.each { |name| attribute(name) }
@@ -108,10 +112,7 @@ module Datasource
108
112
  end
109
113
 
110
114
  def select_all_columns
111
- column_attributes = self.class._attributes.values.select do |att|
112
- att[:klass].nil?
113
- end
114
- columns = column_attributes.map { |att| att[:name] }
115
+ columns = self.class._column_attribute_names
115
116
  select(*columns)
116
117
  @select_all_columns = true
117
118
 
@@ -156,7 +157,7 @@ module Datasource
156
157
  end
157
158
  end
158
159
  update_dependencies(newly_exposed_attributes) unless newly_exposed_attributes.empty?
159
- fail_missing_attributes(missing_attributes) unless Datasource.config.simple_mode || missing_attributes.empty?
160
+ fail_missing_attributes(missing_attributes) if Datasource.config.raise_error_on_unknown_attribute_select && !missing_attributes.empty?
160
161
  self
161
162
  end
162
163
 
@@ -5,7 +5,7 @@ module Datasource
5
5
 
6
6
  included do |base|
7
7
  base.config.adapters = Configuration.default_adapters
8
- base.config.simple_mode = false
8
+ base.config.raise_error_on_unknown_attribute_select = false
9
9
  end
10
10
 
11
11
  def self.default_adapters
@@ -1,10 +1,5 @@
1
1
  Datasource.setup do |config|
2
2
  # Adapters to load
3
3
  # Available ORM adapters: activerecord, sequel
4
- # Available Serializer adapters: active_model_serializers
5
- config.adapters = [:activerecord, :active_model_serializers]
6
-
7
- # Enable simple mode, which will always select all model database columns,
8
- # making Datasource easier to use. See documentation for details.
9
- config.simple_mode = true
4
+ config.adapters = [:activerecord]
10
5
  end
metadata CHANGED
@@ -1,29 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: datasource
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jan Berdajs
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-04 00:00:00.000000000 Z
11
+ date: 2015-02-24 00:00:00.000000000 Z
12
12
  dependencies:
13
- - !ruby/object:Gem::Dependency
14
- name: active_model_serializers
15
- requirement: !ruby/object:Gem::Requirement
16
- requirements:
17
- - - ">="
18
- - !ruby/object:Gem::Version
19
- version: '0.8'
20
- type: :runtime
21
- prerelease: false
22
- version_requirements: !ruby/object:Gem::Requirement
23
- requirements:
24
- - - ">="
25
- - !ruby/object:Gem::Version
26
- version: '0.8'
27
13
  - !ruby/object:Gem::Dependency
28
14
  name: activesupport
29
15
  requirement: !ruby/object:Gem::Requirement
@@ -141,7 +127,6 @@ files:
141
127
  - lib/datasource/base.rb
142
128
  - lib/datasource/collection_context.rb
143
129
  - lib/datasource/configuration.rb
144
- - lib/datasource/consumer_adapters/active_model_serializers.rb
145
130
  - lib/generators/datasource/install_generator.rb
146
131
  - lib/generators/datasource/templates/initializer.rb
147
132
  homepage: https://github.com/kundi/datasource
@@ -1,138 +0,0 @@
1
- require "active_model/serializer"
2
-
3
- module Datasource
4
- module ConsumerAdapters
5
- module ActiveModelSerializers
6
- module ArraySerializer
7
- def initialize_with_datasource(objects, options = {})
8
- datasource_class = options.delete(:datasource)
9
- adapter = Datasource.orm_adapters.find { |a| a.is_scope?(objects) }
10
- if adapter && !adapter.scope_loaded?(objects)
11
- datasource_class ||= adapter.scope_to_class(objects).default_datasource
12
-
13
- scope = begin
14
- objects
15
- .with_datasource(datasource_class)
16
- .for_serializer(options[:serializer])
17
- .datasource_params(*[options[:datasource_params]].compact)
18
- rescue NameError
19
- if options[:serializer].nil?
20
- return initialize_without_datasource(objects, options)
21
- else
22
- raise
23
- end
24
- end
25
-
26
- records = adapter.scope_to_records(scope)
27
-
28
- initialize_without_datasource(records, options)
29
- else
30
- initialize_without_datasource(objects, options)
31
- end
32
- end
33
- end
34
-
35
- module_function
36
- def get_serializer_for(klass, serializer_assoc = nil)
37
- serializer = if serializer_assoc
38
- if serializer_assoc.kind_of?(Hash)
39
- serializer_assoc[:options].try(:[], :serializer)
40
- else
41
- serializer_assoc.options[:serializer]
42
- end
43
- end
44
- serializer || "#{klass.name}Serializer".constantize
45
- end
46
-
47
- def to_datasource_select(result, klass, serializer = nil, serializer_assoc = nil, adapter = nil)
48
- adapter ||= Datasource::Base.default_adapter
49
- serializer ||= get_serializer_for(klass, serializer_assoc)
50
- result.unshift("*") if Datasource.config.simple_mode
51
- if serializer._attributes.respond_to?(:keys) # AMS 0.8
52
- result.concat(serializer._attributes.keys)
53
- else # AMS 0.9
54
- result.concat(serializer._attributes)
55
- end
56
- result.concat(serializer.datasource_select)
57
- result_assocs = serializer.datasource_includes.dup
58
- result.push(result_assocs)
59
-
60
- serializer._associations.each_pair do |name, serializer_assoc|
61
- # TODO: what if assoc is renamed in serializer?
62
- reflection = adapter.association_reflection(klass, name.to_sym)
63
- assoc_class = reflection[:klass]
64
-
65
- name = name.to_s
66
- result_assocs[name] = []
67
- to_datasource_select(result_assocs[name], assoc_class, nil, serializer_assoc, adapter)
68
- end
69
- rescue Exception => ex
70
- if ex.is_a?(SystemStackError) || ex.is_a?(Datasource::RecursionError)
71
- fail Datasource::RecursionError, "recursive association (involving #{klass.name})"
72
- else
73
- raise
74
- end
75
- end
76
- end
77
- end
78
- end
79
-
80
- array_serializer_class = if defined?(ActiveModel::Serializer::ArraySerializer)
81
- ActiveModel::Serializer::ArraySerializer
82
- else
83
- ActiveModel::ArraySerializer
84
- end
85
-
86
- array_serializer_class.class_exec do
87
- alias_method :initialize_without_datasource, :initialize
88
- include Datasource::ConsumerAdapters::ActiveModelSerializers::ArraySerializer
89
- def initialize(*args)
90
- initialize_with_datasource(*args)
91
- end
92
- end
93
-
94
- module SerializerClassMethods
95
- def inherited(base)
96
- base.datasource_select(*datasource_select.deep_dup)
97
- base.datasource_includes(*datasource_includes.deep_dup)
98
-
99
- super
100
- end
101
-
102
- def datasource_select(*args)
103
- @datasource_select ||= []
104
- @datasource_select.concat(args)
105
-
106
- @datasource_select
107
- end
108
-
109
- def datasource_includes(*args)
110
- @datasource_includes ||= {}
111
-
112
- args.each do |arg|
113
- @datasource_includes.deep_merge!(datasource_includes_to_select(arg))
114
- end
115
-
116
- @datasource_includes
117
- end
118
-
119
- private
120
- def datasource_includes_to_select(arg)
121
- if arg.kind_of?(Hash)
122
- arg.keys.inject({}) do |memo, key|
123
- memo[key.to_sym] = ["*", datasource_includes_to_select(arg[key])]
124
- memo
125
- end
126
- elsif arg.kind_of?(Array)
127
- arg.inject({}) do |memo, element|
128
- memo.deep_merge!(datasource_includes_to_select(element))
129
- end
130
- elsif arg.respond_to?(:to_sym)
131
- { arg.to_sym => ["*"] }
132
- else
133
- fail Datasource::Error, "unknown includes value type #{arg.class}"
134
- end
135
- end
136
- end
137
-
138
- ActiveModel::Serializer.singleton_class.send :prepend, SerializerClassMethods