fast_jsonapi 1.2 → 1.3

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: 346edf85242b61d0747883aaa7b62da16b1a8fec
4
- data.tar.gz: 36d21132fe65b9c88e2f1afce9aa935ae094d7ec
3
+ metadata.gz: ad201aff3dee102a9760daee3777a225d73bcfcb
4
+ data.tar.gz: a6e4f0f7840f27c95f3530b68542eae6422d8975
5
5
  SHA512:
6
- metadata.gz: 9bb346d89aa234162c0bdc5f269e82735890ba6ebe60bb6e448506ef1d2d299c08be234b8acf4827fdc8860bee1439cc5dded54107b857ed536d2229c894519c
7
- data.tar.gz: 3e4f56de96e171a931a5479edd16600fd6bc3fb586b1152a8b63a4e39fc73075d01a276bba831f577d74a951c494a5744d584e3a33f482c4139f410db40ebffb
6
+ metadata.gz: c9503ec4e6d642f72b2a5ccb34f4b33bc68e6fda067e1a16e43d6f30f0f5b95f7e86626d608ab38fbe277ff778dae908e71252e354a1834121b013219cd77e0c
7
+ data.tar.gz: 069b1ac282d7b2a3f559f398c4090f188c72f8120f4bfeff01f7126a57fbc789725ef3f2e6cf1c88c541a99681914c4f32bb5855655b9f65a7c55262c02f449e
data/README.md CHANGED
@@ -30,6 +30,9 @@ Fast JSON API serialized 250 records in 3.01 ms
30
30
  * [Collection Serialization](#collection-serialization)
31
31
  * [Caching](#caching)
32
32
  * [Params](#params)
33
+ * [Conditional Attributes](#conditional-attributes)
34
+ * [Conditional Relationships](#conditional-relationships)
35
+ * [Sparse Fieldsets](#sparse-fieldsets)
33
36
  * [Contributing](#contributing)
34
37
 
35
38
 
@@ -205,6 +208,18 @@ class MovieSerializer
205
208
  end
206
209
  ```
207
210
 
211
+ Attributes can also use a different name by passing the original method or accessor with a proc shortcut:
212
+
213
+ ```ruby
214
+ class MovieSerializer
215
+ include FastJsonapi::ObjectSerializer
216
+
217
+ attributes :name
218
+
219
+ attribute :released_in_year, &:year
220
+ end
221
+ ```
222
+
208
223
  ### Links Per Object
209
224
  Links are defined in FastJsonapi using the `link` method. By default, link are read directly from the model property of the same name.In this example, `public_url` is expected to be a property of the object being serialized.
210
225
 
@@ -259,6 +274,26 @@ hash = MovieSerializer.new([movie, movie], options).serializable_hash
259
274
  json_string = MovieSerializer.new([movie, movie], options).serialized_json
260
275
  ```
261
276
 
277
+ #### Control Over Collection Serialization
278
+
279
+ You can use `is_collection` option to have better control over collection serialization.
280
+
281
+ If this option is not provided or `nil` autedetect logic is used to try understand
282
+ if provided resource is a single object or collection.
283
+
284
+ Autodetect logic is compatible with most DB toolkits (ActiveRecord, Sequel, etc.) but
285
+ **cannot** guarantee that single vs collection will be always detected properly.
286
+
287
+ ```ruby
288
+ options[:is_collection]
289
+ ```
290
+
291
+ was introduced to be able to have precise control this behavior
292
+
293
+ - `nil` or not provided: will try to autodetect single vs collection (please, see notes above)
294
+ - `true` will always treat input resource as *collection*
295
+ - `false` will always treat input resource as *single object*
296
+
262
297
  ### Caching
263
298
  Requires a `cache_key` method be defined on model:
264
299
 
@@ -284,7 +319,6 @@ block you opt-in to using params by adding it as a block parameter.
284
319
 
285
320
  ```ruby
286
321
  class MovieSerializer
287
- class MovieSerializer
288
322
  include FastJsonapi::ObjectSerializer
289
323
 
290
324
  attributes :name, :year
@@ -308,6 +342,68 @@ serializer.serializable_hash
308
342
  Custom attributes and relationships that only receive the resource are still possible by defining
309
343
  the block to only receive one argument.
310
344
 
345
+ ### Conditional Attributes
346
+
347
+ Conditional attributes can be defined by passing a Proc to the `if` key on the `attribute` method. Return `true` if the attribute should be serialized, and `false` if not. The record and any params passed to the serializer are available inside the Proc as the first and second parameters, respectively.
348
+
349
+ ```ruby
350
+ class MovieSerializer
351
+ include FastJsonapi::ObjectSerializer
352
+
353
+ attributes :name, :year
354
+ attribute :release_year, if: Proc.new do |record|
355
+ # Release year will only be serialized if it's greater than 1990
356
+ record.release_year > 1990
357
+ end
358
+
359
+ attribute :director, if: Proc.new do |record, params|
360
+ # The director will be serialized only if the :admin key of params is true
361
+ params && params[:admin] == true
362
+ end
363
+ end
364
+
365
+ # ...
366
+ current_user = User.find(cookies[:current_user_id])
367
+ serializer = MovieSerializer.new(movie, { params: { admin: current_user.admin? }})
368
+ serializer.serializable_hash
369
+ ```
370
+
371
+ ### Conditional Relationships
372
+
373
+ Conditional relationships can be defined by passing a Proc to the `if` key. Return `true` if the relationship should be serialized, and `false` if not. The record and any params passed to the serializer are available inside the Proc as the first and second parameters, respectively.
374
+
375
+ ```ruby
376
+ class MovieSerializer
377
+ include FastJsonapi::ObjectSerializer
378
+
379
+ # Actors will only be serialized if the record has any associated actors
380
+ has_many :actors, if: Proc.new { |record| record.actors.any? }
381
+
382
+ # Owner will only be serialized if the :admin key of params is true
383
+ belongs_to :owner, if: Proc.new { |record, params| params && params[:admin] == true }
384
+ end
385
+
386
+ # ...
387
+ current_user = User.find(cookies[:current_user_id])
388
+ serializer = MovieSerializer.new(movie, { params: { admin: current_user.admin? }})
389
+ serializer.serializable_hash
390
+ ```
391
+
392
+ ### Sparse Fieldsets
393
+
394
+ Attributes and relationships can be selectively returned per record type by using the `fields` option.
395
+
396
+ ```ruby
397
+ class MovieSerializer
398
+ include FastJsonapi::ObjectSerializer
399
+
400
+ attributes :name, :year
401
+ end
402
+
403
+ serializer = MovieSerializer.new(movie, { fields: { movie: [:name] } })
404
+ serializer.serializable_hash
405
+ ```
406
+
311
407
  ### Customizable Options
312
408
 
313
409
  Option | Purpose | Example
@@ -1,20 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- if defined?(::ActiveRecord)
4
- ::ActiveRecord::Associations::Builder::HasOne.class_eval do
5
- # Based on
6
- # https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/builder/collection_association.rb#L50
7
- # https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/builder/singular_association.rb#L11
8
- def self.define_accessors(mixin, reflection)
9
- super
10
- name = reflection.name
11
- mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
12
- def #{name}_id
13
- # if an attribute is already defined with this methods name we should just use it
14
- return read_attribute(__method__) if has_attribute?(__method__)
15
- association(:#{name}).reader.try(:id)
16
- end
17
- CODE
18
- end
3
+ ::ActiveRecord::Associations::Builder::HasOne.class_eval do
4
+ # Based on
5
+ # https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/builder/collection_association.rb#L50
6
+ # https://github.com/rails/rails/blob/master/activerecord/lib/active_record/associations/builder/singular_association.rb#L11
7
+ def self.define_accessors(mixin, reflection)
8
+ super
9
+ name = reflection.name
10
+ mixin.class_eval <<-CODE, __FILE__, __LINE__ + 1
11
+ def #{name}_id
12
+ # if an attribute is already defined with this methods name we should just use it
13
+ return read_attribute(__method__) if has_attribute?(__method__)
14
+ association(:#{name}).reader.try(:id)
15
+ end
16
+ CODE
19
17
  end
20
18
  end
@@ -2,5 +2,9 @@
2
2
 
3
3
  module FastJsonapi
4
4
  require 'fast_jsonapi/object_serializer'
5
- require 'extensions/has_one'
5
+ if defined?(::Rails)
6
+ require 'fast_jsonapi/railtie'
7
+ elsif defined?(::ActiveRecord)
8
+ require 'extensions/has_one'
9
+ end
6
10
  end
@@ -0,0 +1,29 @@
1
+ module FastJsonapi
2
+ class Attribute
3
+ attr_reader :key, :method, :conditional_proc
4
+
5
+ def initialize(key:, method:, options: {})
6
+ @key = key
7
+ @method = method
8
+ @conditional_proc = options[:if]
9
+ end
10
+
11
+ def serialize(record, serialization_params, output_hash)
12
+ if include_attribute?(record, serialization_params)
13
+ output_hash[key] = if method.is_a?(Proc)
14
+ method.arity.abs == 1 ? method.call(record) : method.call(record, serialization_params)
15
+ else
16
+ record.public_send(method)
17
+ end
18
+ end
19
+ end
20
+
21
+ def include_attribute?(record, serialization_params)
22
+ if conditional_proc.present?
23
+ conditional_proc.call(record, serialization_params)
24
+ else
25
+ true
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,18 @@
1
+ module FastJsonapi
2
+ class Link
3
+ attr_reader :key, :method
4
+
5
+ def initialize(key:, method:)
6
+ @key = key
7
+ @method = method
8
+ end
9
+
10
+ def serialize(record, serialization_params, output_hash)
11
+ output_hash[key] = if method.is_a?(Proc)
12
+ method.arity == 1 ? method.call(record) : method.call(record, serialization_params)
13
+ else
14
+ record.public_send(method)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -3,6 +3,9 @@
3
3
  require 'active_support/core_ext/object'
4
4
  require 'active_support/concern'
5
5
  require 'active_support/inflector'
6
+ require 'fast_jsonapi/attribute'
7
+ require 'fast_jsonapi/relationship'
8
+ require 'fast_jsonapi/link'
6
9
  require 'fast_jsonapi/serialization_core'
7
10
 
8
11
  module FastJsonapi
@@ -25,7 +28,7 @@ module FastJsonapi
25
28
  end
26
29
 
27
30
  def serializable_hash
28
- return hash_for_collection if is_collection?(@resource)
31
+ return hash_for_collection if is_collection?(@resource, @is_collection)
29
32
 
30
33
  hash_for_one_record
31
34
  end
@@ -38,8 +41,8 @@ module FastJsonapi
38
41
 
39
42
  return serializable_hash unless @resource
40
43
 
41
- serializable_hash[:data] = self.class.record_hash(@resource, @params)
42
- serializable_hash[:included] = self.class.get_included_records(@resource, @includes, @known_included_objects, @params) if @includes.present?
44
+ serializable_hash[:data] = self.class.record_hash(@resource, @fieldsets[self.class.record_type.to_sym], @params)
45
+ serializable_hash[:included] = self.class.get_included_records(@resource, @includes, @known_included_objects, @fieldsets, @params) if @includes.present?
43
46
  serializable_hash
44
47
  end
45
48
 
@@ -48,9 +51,10 @@ module FastJsonapi
48
51
 
49
52
  data = []
50
53
  included = []
54
+ fieldset = @fieldsets[self.class.record_type.to_sym]
51
55
  @resource.each do |record|
52
- data << self.class.record_hash(record, @params)
53
- included.concat self.class.get_included_records(record, @includes, @known_included_objects, @params) if @includes.present?
56
+ data << self.class.record_hash(record, fieldset, @params)
57
+ included.concat self.class.get_included_records(record, @includes, @known_included_objects, @fieldsets, @params) if @includes.present?
54
58
  end
55
59
 
56
60
  serializable_hash[:data] = data
@@ -67,11 +71,14 @@ module FastJsonapi
67
71
  private
68
72
 
69
73
  def process_options(options)
74
+ @fieldsets = deep_symbolize(options[:fields].presence || {})
75
+
70
76
  return if options.blank?
71
77
 
72
78
  @known_included_objects = {}
73
79
  @meta = options[:meta]
74
80
  @links = options[:links]
81
+ @is_collection = options[:is_collection]
75
82
  @params = options[:params] || {}
76
83
  raise ArgumentError.new("`params` option passed to serializer must be a hash") unless @params.is_a?(Hash)
77
84
 
@@ -81,8 +88,22 @@ module FastJsonapi
81
88
  end
82
89
  end
83
90
 
84
- def is_collection?(resource)
85
- resource.respond_to?(:each) && !resource.respond_to?(:each_pair)
91
+ def deep_symbolize(collection)
92
+ if collection.is_a? Hash
93
+ Hash[collection.map do |k, v|
94
+ [k.to_sym, deep_symbolize(v)]
95
+ end]
96
+ elsif collection.is_a? Array
97
+ collection.map { |i| deep_symbolize(i) }
98
+ else
99
+ collection.to_sym
100
+ end
101
+ end
102
+
103
+ def is_collection?(resource, force_is_collection = nil)
104
+ return force_is_collection unless force_is_collection.nil?
105
+
106
+ resource.respond_to?(:size) && !resource.respond_to?(:each_pair)
86
107
  end
87
108
 
88
109
  class_methods do
@@ -98,6 +119,7 @@ module FastJsonapi
98
119
  subclass.race_condition_ttl = race_condition_ttl
99
120
  subclass.data_links = data_links
100
121
  subclass.cached = cached
122
+ subclass.set_type(subclass.reflected_record_type) if subclass.reflected_record_type
101
123
  end
102
124
 
103
125
  def reflected_record_type
@@ -118,6 +140,9 @@ module FastJsonapi
118
140
  underscore: :underscore
119
141
  }
120
142
  self.transform_method = mapping[transform_name.to_sym]
143
+
144
+ # ensure that the record type is correctly transformed
145
+ set_type(reflected_record_type) if reflected_record_type
121
146
  end
122
147
 
123
148
  def run_key_transform(input)
@@ -149,48 +174,51 @@ module FastJsonapi
149
174
 
150
175
  def attributes(*attributes_list, &block)
151
176
  attributes_list = attributes_list.first if attributes_list.first.class.is_a?(Array)
177
+ options = attributes_list.last.is_a?(Hash) ? attributes_list.pop : {}
152
178
  self.attributes_to_serialize = {} if self.attributes_to_serialize.nil?
179
+
153
180
  attributes_list.each do |attr_name|
154
181
  method_name = attr_name
155
182
  key = run_key_transform(method_name)
156
- attributes_to_serialize[key] = block || method_name
183
+ attributes_to_serialize[key] = Attribute.new(
184
+ key: key,
185
+ method: block || method_name,
186
+ options: options
187
+ )
157
188
  end
158
189
  end
159
190
 
160
191
  alias_method :attribute, :attributes
161
192
 
162
- def add_relationship(name, relationship)
193
+ def add_relationship(relationship)
163
194
  self.relationships_to_serialize = {} if relationships_to_serialize.nil?
164
195
  self.cachable_relationships_to_serialize = {} if cachable_relationships_to_serialize.nil?
165
196
  self.uncachable_relationships_to_serialize = {} if uncachable_relationships_to_serialize.nil?
166
-
167
- if !relationship[:cached]
168
- self.uncachable_relationships_to_serialize[name] = relationship
197
+
198
+ if !relationship.cached
199
+ self.uncachable_relationships_to_serialize[relationship.name] = relationship
169
200
  else
170
- self.cachable_relationships_to_serialize[name] = relationship
201
+ self.cachable_relationships_to_serialize[relationship.name] = relationship
171
202
  end
172
- self.relationships_to_serialize[name] = relationship
173
- end
203
+ self.relationships_to_serialize[relationship.name] = relationship
204
+ end
174
205
 
175
206
  def has_many(relationship_name, options = {}, &block)
176
- name = relationship_name.to_sym
177
- hash = create_relationship_hash(relationship_name, :has_many, options, block)
178
- add_relationship(name, hash)
207
+ relationship = create_relationship(relationship_name, :has_many, options, block)
208
+ add_relationship(relationship)
179
209
  end
180
210
 
181
211
  def has_one(relationship_name, options = {}, &block)
182
- name = relationship_name.to_sym
183
- hash = create_relationship_hash(relationship_name, :has_one, options, block)
184
- add_relationship(name, hash)
212
+ relationship = create_relationship(relationship_name, :has_one, options, block)
213
+ add_relationship(relationship)
185
214
  end
186
215
 
187
216
  def belongs_to(relationship_name, options = {}, &block)
188
- name = relationship_name.to_sym
189
- hash = create_relationship_hash(relationship_name, :belongs_to, options, block)
190
- add_relationship(name, hash)
217
+ relationship = create_relationship(relationship_name, :belongs_to, options, block)
218
+ add_relationship(relationship)
191
219
  end
192
220
 
193
- def create_relationship_hash(base_key, relationship_type, options, block)
221
+ def create_relationship(base_key, relationship_type, options, block)
194
222
  name = base_key.to_sym
195
223
  if relationship_type == :has_many
196
224
  base_serialization_key = base_key.to_s.singularize
@@ -201,7 +229,7 @@ module FastJsonapi
201
229
  base_key_sym = name
202
230
  id_postfix = '_id'
203
231
  end
204
- {
232
+ Relationship.new(
205
233
  key: options[:key] || run_key_transform(base_key),
206
234
  name: name,
207
235
  id_method_name: options[:id_method_name] || "#{base_serialization_key}#{id_postfix}".to_sym,
@@ -210,9 +238,10 @@ module FastJsonapi
210
238
  object_block: block,
211
239
  serializer: compute_serializer_name(options[:serializer] || base_key_sym),
212
240
  relationship_type: relationship_type,
213
- cached: options[:cached] || false,
214
- polymorphic: fetch_polymorphic_option(options)
215
- }
241
+ cached: options[:cached],
242
+ polymorphic: fetch_polymorphic_option(options),
243
+ conditional_proc: options[:if]
244
+ )
216
245
  end
217
246
 
218
247
  def compute_serializer_name(serializer_key)
@@ -233,7 +262,11 @@ module FastJsonapi
233
262
  self.data_links = {} if self.data_links.nil?
234
263
  link_method_name = link_name if link_method_name.nil?
235
264
  key = run_key_transform(link_name)
236
- self.data_links[key] = block || link_method_name
265
+
266
+ self.data_links[key] = Link.new(
267
+ key: key,
268
+ method: block || link_method_name
269
+ )
237
270
  end
238
271
 
239
272
  def validate_includes!(includes)
@@ -244,8 +277,8 @@ module FastJsonapi
244
277
  parse_include_item(include_item).each do |parsed_include|
245
278
  relationship_to_include = klass.relationships_to_serialize[parsed_include]
246
279
  raise ArgumentError, "#{parsed_include} is not specified as a relationship on #{klass.name}" unless relationship_to_include
247
- raise NotImplementedError if relationship_to_include[:polymorphic].is_a?(Hash)
248
- klass = relationship_to_include[:serializer].to_s.constantize
280
+ raise NotImplementedError if relationship_to_include.polymorphic.is_a?(Hash)
281
+ klass = relationship_to_include.serializer.to_s.constantize
249
282
  end
250
283
  end
251
284
  end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/railtie'
4
+
5
+ class Railtie < Rails::Railtie
6
+ initializer 'fast_jsonapi.active_record' do
7
+ ActiveSupport.on_load :active_record do
8
+ require 'extensions/has_one'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,99 @@
1
+ module FastJsonapi
2
+ class Relationship
3
+ attr_reader :key, :name, :id_method_name, :record_type, :object_method_name, :object_block, :serializer, :relationship_type, :cached, :polymorphic, :conditional_proc
4
+
5
+ def initialize(
6
+ key:,
7
+ name:,
8
+ id_method_name:,
9
+ record_type:,
10
+ object_method_name:,
11
+ object_block:,
12
+ serializer:,
13
+ relationship_type:,
14
+ cached: false,
15
+ polymorphic:,
16
+ conditional_proc:
17
+ )
18
+ @key = key
19
+ @name = name
20
+ @id_method_name = id_method_name
21
+ @record_type = record_type
22
+ @object_method_name = object_method_name
23
+ @object_block = object_block
24
+ @serializer = serializer
25
+ @relationship_type = relationship_type
26
+ @cached = cached
27
+ @polymorphic = polymorphic
28
+ @conditional_proc = conditional_proc
29
+ end
30
+
31
+ def serialize(record, serialization_params, output_hash)
32
+ if include_relationship?(record, serialization_params)
33
+ empty_case = relationship_type == :has_many ? [] : nil
34
+ output_hash[key] = {
35
+ data: ids_hash_from_record_and_relationship(record, serialization_params) || empty_case
36
+ }
37
+ end
38
+ end
39
+
40
+ def fetch_associated_object(record, params)
41
+ return object_block.call(record, params) unless object_block.nil?
42
+ record.send(object_method_name)
43
+ end
44
+
45
+ def include_relationship?(record, serialization_params)
46
+ if conditional_proc.present?
47
+ conditional_proc.call(record, serialization_params)
48
+ else
49
+ true
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ def ids_hash_from_record_and_relationship(record, params = {})
56
+ return ids_hash(
57
+ fetch_id(record, params)
58
+ ) unless polymorphic
59
+
60
+ return unless associated_object = fetch_associated_object(record, params)
61
+
62
+ return associated_object.map do |object|
63
+ id_hash_from_record object, polymorphic
64
+ end if associated_object.respond_to? :map
65
+
66
+ id_hash_from_record associated_object, polymorphic
67
+ end
68
+
69
+ def id_hash_from_record(record, record_types)
70
+ # memoize the record type within the record_types dictionary, then assigning to record_type:
71
+ associated_record_type = record_types[record.class] ||= record.class.name.underscore.to_sym
72
+ id_hash(record.id, associated_record_type)
73
+ end
74
+
75
+ def ids_hash(ids)
76
+ return ids.map { |id| id_hash(id, record_type) } if ids.respond_to? :map
77
+ id_hash(ids, record_type) # ids variable is just a single id here
78
+ end
79
+
80
+ def id_hash(id, record_type, default_return=false)
81
+ if id.present?
82
+ { id: id.to_s, type: record_type }
83
+ else
84
+ default_return ? { id: nil, type: record_type } : nil
85
+ end
86
+ end
87
+
88
+ def fetch_id(record, params)
89
+ unless object_block.nil?
90
+ object = object_block.call(record, params)
91
+
92
+ return object.map(&:id) if object.respond_to? :map
93
+ return object.try(:id)
94
+ end
95
+
96
+ record.public_send(id_method_name)
97
+ end
98
+ end
99
+ end
@@ -34,73 +34,36 @@ module FastJsonapi
34
34
  end
35
35
  end
36
36
 
37
- def ids_hash(ids, record_type)
38
- return ids.map { |id| id_hash(id, record_type) } if ids.respond_to? :map
39
- id_hash(ids, record_type) # ids variable is just a single id here
40
- end
41
-
42
- def id_hash_from_record(record, record_types)
43
- # memoize the record type within the record_types dictionary, then assigning to record_type:
44
- record_type = record_types[record.class] ||= record.class.name.underscore.to_sym
45
- id_hash(record.id, record_type)
46
- end
47
-
48
- def ids_hash_from_record_and_relationship(record, relationship, params = {})
49
- polymorphic = relationship[:polymorphic]
50
-
51
- return ids_hash(
52
- fetch_id(record, relationship, params),
53
- relationship[:record_type]
54
- ) unless polymorphic
55
-
56
- return unless associated_object = fetch_associated_object(record, relationship, params)
57
-
58
- return associated_object.map do |object|
59
- id_hash_from_record object, polymorphic
60
- end if associated_object.respond_to? :map
61
-
62
- id_hash_from_record associated_object, polymorphic
63
- end
64
-
65
37
  def links_hash(record, params = {})
66
- data_links.each_with_object({}) do |(key, method), link_hash|
67
- link_hash[key] = if method.is_a?(Proc)
68
- method.arity == 1 ? method.call(record) : method.call(record, params)
69
- else
70
- record.public_send(method)
71
- end
38
+ data_links.each_with_object({}) do |(_k, link), hash|
39
+ link.serialize(record, params, hash)
72
40
  end
73
41
  end
74
42
 
75
- def attributes_hash(record, params = {})
76
- attributes_to_serialize.each_with_object({}) do |(key, method), attr_hash|
77
- attr_hash[key] = if method.is_a?(Proc)
78
- method.arity == 1 ? method.call(record) : method.call(record, params)
79
- else
80
- record.public_send(method)
81
- end
43
+ def attributes_hash(record, fieldset = nil, params = {})
44
+ attributes = attributes_to_serialize
45
+ attributes = attributes.slice(*fieldset) if fieldset.present?
46
+ attributes.each_with_object({}) do |(_k, attribute), hash|
47
+ attribute.serialize(record, params, hash)
82
48
  end
83
49
  end
84
50
 
85
- def relationships_hash(record, relationships = nil, params = {})
51
+ def relationships_hash(record, relationships = nil, fieldset = nil, params = {})
86
52
  relationships = relationships_to_serialize if relationships.nil?
53
+ relationships = relationships.slice(*fieldset) if fieldset.present?
87
54
 
88
55
  relationships.each_with_object({}) do |(_k, relationship), hash|
89
- name = relationship[:key]
90
- empty_case = relationship[:relationship_type] == :has_many ? [] : nil
91
- hash[name] = {
92
- data: ids_hash_from_record_and_relationship(record, relationship, params) || empty_case
93
- }
56
+ relationship.serialize(record, params, hash)
94
57
  end
95
58
  end
96
59
 
97
- def record_hash(record, params = {})
60
+ def record_hash(record, fieldset, params = {})
98
61
  if cached
99
62
  record_hash = Rails.cache.fetch(record.cache_key, expires_in: cache_length, race_condition_ttl: race_condition_ttl) do
100
63
  temp_hash = id_hash(id_from_record(record), record_type, true)
101
- temp_hash[:attributes] = attributes_hash(record, params) if attributes_to_serialize.present?
64
+ temp_hash[:attributes] = attributes_hash(record, fieldset, params) if attributes_to_serialize.present?
102
65
  temp_hash[:relationships] = {}
103
- temp_hash[:relationships] = relationships_hash(record, cachable_relationships_to_serialize, params) if cachable_relationships_to_serialize.present?
66
+ temp_hash[:relationships] = relationships_hash(record, cachable_relationships_to_serialize, fieldset, params) if cachable_relationships_to_serialize.present?
104
67
  temp_hash[:links] = links_hash(record, params) if data_links.present?
105
68
  temp_hash
106
69
  end
@@ -108,8 +71,8 @@ module FastJsonapi
108
71
  record_hash
109
72
  else
110
73
  record_hash = id_hash(id_from_record(record), record_type, true)
111
- record_hash[:attributes] = attributes_hash(record, params) if attributes_to_serialize.present?
112
- record_hash[:relationships] = relationships_hash(record, nil, params) if relationships_to_serialize.present?
74
+ record_hash[:attributes] = attributes_hash(record, fieldset, params) if attributes_to_serialize.present?
75
+ record_hash[:relationships] = relationships_hash(record, nil, fieldset, params) if relationships_to_serialize.present?
113
76
  record_hash[:links] = links_hash(record, params) if data_links.present?
114
77
  record_hash
115
78
  end
@@ -140,25 +103,27 @@ module FastJsonapi
140
103
  end
141
104
 
142
105
  # includes handler
143
- def get_included_records(record, includes_list, known_included_objects, params = {})
106
+ def get_included_records(record, includes_list, known_included_objects, fieldsets, params = {})
144
107
  return unless includes_list.present?
145
108
 
146
109
  includes_list.sort.each_with_object([]) do |include_item, included_records|
147
110
  items = parse_include_item(include_item)
148
111
  items.each do |item|
149
112
  next unless relationships_to_serialize && relationships_to_serialize[item]
150
- raise NotImplementedError if @relationships_to_serialize[item][:polymorphic].is_a?(Hash)
151
- record_type = @relationships_to_serialize[item][:record_type]
152
- serializer = @relationships_to_serialize[item][:serializer].to_s.constantize
153
- relationship_type = @relationships_to_serialize[item][:relationship_type]
154
-
155
- included_objects = fetch_associated_object(record, @relationships_to_serialize[item], params)
113
+ relationship_item = relationships_to_serialize[item]
114
+ next unless relationship_item.include_relationship?(record, params)
115
+ raise NotImplementedError if relationship_item.polymorphic.is_a?(Hash)
116
+ record_type = relationship_item.record_type
117
+ serializer = relationship_item.serializer.to_s.constantize
118
+ relationship_type = relationship_item.relationship_type
119
+
120
+ included_objects = relationship_item.fetch_associated_object(record, params)
156
121
  next if included_objects.blank?
157
122
  included_objects = [included_objects] unless relationship_type == :has_many
158
123
 
159
124
  included_objects.each do |inc_obj|
160
125
  if remaining_items(items)
161
- serializer_records = serializer.get_included_records(inc_obj, remaining_items(items), known_included_objects)
126
+ serializer_records = serializer.get_included_records(inc_obj, remaining_items(items), known_included_objects, fieldsets)
162
127
  included_records.concat(serializer_records) unless serializer_records.empty?
163
128
  end
164
129
 
@@ -166,27 +131,12 @@ module FastJsonapi
166
131
  next if known_included_objects.key?(code)
167
132
 
168
133
  known_included_objects[code] = inc_obj
169
- included_records << serializer.record_hash(inc_obj, params)
134
+
135
+ included_records << serializer.record_hash(inc_obj, fieldsets[serializer.record_type], params)
170
136
  end
171
137
  end
172
138
  end
173
139
  end
174
-
175
- def fetch_associated_object(record, relationship, params)
176
- return relationship[:object_block].call(record, params) unless relationship[:object_block].nil?
177
- record.send(relationship[:object_method_name])
178
- end
179
-
180
- def fetch_id(record, relationship, params)
181
- unless relationship[:object_block].nil?
182
- object = relationship[:object_block].call(record, params)
183
-
184
- return object.map(&:id) if object.respond_to? :map
185
- return object.id
186
- end
187
-
188
- record.public_send(relationship[:id_method_name])
189
- end
190
140
  end
191
141
  end
192
142
  end
@@ -1,3 +1,3 @@
1
1
  module FastJsonapi
2
- VERSION = "1.2"
2
+ VERSION = "1.3"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fast_jsonapi
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.2'
4
+ version: '1.3'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shishir Kakaraddi
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2018-05-20 00:00:00.000000000 Z
13
+ date: 2018-07-17 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activesupport
@@ -193,6 +193,7 @@ files:
193
193
  - README.md
194
194
  - lib/extensions/has_one.rb
195
195
  - lib/fast_jsonapi.rb
196
+ - lib/fast_jsonapi/attribute.rb
196
197
  - lib/fast_jsonapi/instrumentation.rb
197
198
  - lib/fast_jsonapi/instrumentation/serializable_hash.rb
198
199
  - lib/fast_jsonapi/instrumentation/serialized_json.rb
@@ -200,8 +201,11 @@ files:
200
201
  - lib/fast_jsonapi/instrumentation/skylight/normalizers/base.rb
201
202
  - lib/fast_jsonapi/instrumentation/skylight/normalizers/serializable_hash.rb
202
203
  - lib/fast_jsonapi/instrumentation/skylight/normalizers/serialized_json.rb
204
+ - lib/fast_jsonapi/link.rb
203
205
  - lib/fast_jsonapi/multi_to_json.rb
204
206
  - lib/fast_jsonapi/object_serializer.rb
207
+ - lib/fast_jsonapi/railtie.rb
208
+ - lib/fast_jsonapi/relationship.rb
205
209
  - lib/fast_jsonapi/serialization_core.rb
206
210
  - lib/fast_jsonapi/version.rb
207
211
  - lib/generators/serializer/USAGE