fast_jsonapi 1.2 → 1.3

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: 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