fields-serializer 0.4.0 → 0.5.0

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: 026a3b888bceb7ead4ec323dfb6a8f5d32b0530c
4
- data.tar.gz: 1465d275c0d1999835a1b16306c488343654e20d
3
+ metadata.gz: d97826f68957ac11395bfc95787ebc4562a49891
4
+ data.tar.gz: dba35d1851970293eb3fcf3ac8992cb8108bc801
5
5
  SHA512:
6
- metadata.gz: 72bc247fb4b38e878af011e3fa2a96f4e8bac7b57696dc2b48f9375ff462f04e6b49a9f2966f18a78c939a35720ded20e20b18439ef6d3adddedd2e88e8ba4f9
7
- data.tar.gz: 54127c8b3f4e09f5d7c04e01780862ce08de6368ff0dfde49be85f600437022968e0d2ee9167b674acd7e7ceecd51a3ffe734234aa29baf012ba9f32ecce8e5f
6
+ metadata.gz: 429db7023534cb8168d610708cca7705a3ec3127da0d390d2472b989f0645071f63907c3c21fc12990bacd10bea1c0e29c7535f8e774e2f680c4dbb150594818
7
+ data.tar.gz: 2a2b8473e52a57245db3333d18603bd759c094917a484391476c47e49de1ca70424e1ec3d6e739d55a34c659dccbfbca181d8e9646fc53d84597378ffd694569
@@ -17,10 +17,9 @@ module Fields
17
17
  fields = options.delete(:fields)
18
18
  model_class = options.delete(:model_class)
19
19
  if fields.present?
20
- includes = model_class.fields_to_includes(fields)
21
- query = query.includes(*includes)
22
- # options.merge!(include: model_class.fields_to_include(includes).join(","))
23
- options.merge!(include: includes)
20
+ query = query.includes(*model_class.fields_to_includes(fields))
21
+ options.merge!(each_serializer: model_class.fields_serializer(fields))
22
+ options.delete(:include)
24
23
  end
25
24
  render options.merge!(json: query.to_a)
26
25
  end
@@ -1,4 +1,5 @@
1
1
  require 'active_record'
2
+ require "active_model_serializers"
2
3
 
3
4
  module Fields
4
5
  module Serializer
@@ -13,57 +14,52 @@ module Fields
13
14
  #
14
15
  # BoilerPack.fields_to_includes("id,boiler.gas_safe_code") #=> ["boiler"]
15
16
  #
16
- def fields_to_includes(fields)
17
- nested_fields(fields).inject([{}]) do |result, attribute_structure|
18
- if attribute_structure.is_a?(Hash)
19
- result.first.deep_merge!(attribute_structure) { |_, u, v| u == v ? u : [u, v] }
20
- else
21
- result << attribute_structure unless result.first.dig(attribute_structure) || result.include?(attribute_structure)
22
- end
23
- result
24
- end.map(&:presence).compact
17
+ def fields_to_includes(*fields)
18
+ fields_to_tree(fields).to_includes
25
19
  end
26
20
 
27
- # Convert a list of fields (json_api notation) in a list of associations to be
28
- # added to a ActiveRecord Model.includes call
29
- #
30
- # Example:
31
- #
32
- # BoilerPack.fields_to_includes("id,boiler.gas_safe_code") #=> ["boiler"]
33
- #
34
- def fields_to_include(fields, root: nil)
35
- Array(fields).map do |field|
36
- if field.kind_of?(Hash) || field.kind_of?(Array)
37
- field.map { |k, v| fields_to_include(v, root: composite_field(root, k)) }
38
- else
39
- composite_field(root, field)
40
- end
41
- end.flatten
21
+ def fields_serializer(*fields)
22
+ create_serializer_class(fields_to_tree(fields).notation)
42
23
  end
43
24
 
44
- def nested_field(attribute_stack)
45
- parent = attribute_stack.first
46
- return unless association?(parent)
47
- parent_klass = reflections[parent].class_name.constantize
48
- { parent => parent_klass.nested_field(attribute_stack[1..-1]) }.compact.presence || parent
25
+ def create_serializer_class(fields)
26
+ Class.new(ActiveModel::Serializer) do
27
+ Array(fields).each do |field|
28
+ if field.kind_of?(Hash)
29
+ nested_association(field)
30
+ else
31
+ attribute field.to_sym unless association?(field)
32
+ end
33
+ end
34
+ end
49
35
  end
50
36
 
51
37
  private
52
38
 
53
- def array_fields(fields)
54
- Array(fields).map { |str| str.to_s.split(",").map(&:strip) }.flatten
39
+ def fields_to_tree(*fields)
40
+ array_fields(fields.flatten).inject(FieldsTree.new(self), &:merge!)
55
41
  end
56
42
 
57
- def association?(key)
58
- reflections.keys.include?(key)
43
+ # Calls:
44
+ # has_one :user, serializer: new_serializer_class_for_user_fields
45
+ # or
46
+ # belongs_to :user, serializer: new_serializer_class_for_user_fields
47
+ # or
48
+ # has_many :users, serializer: new_serializer_class_for_user_fields
49
+ #
50
+ def nested_association(fields)
51
+ fields.each do |association_name, nested_fields|
52
+ reflection = reflections[association_name]
53
+ send(reflection.macro, association_name.to_sym, serializer: reflection.klass.create_serializer_class(nested_fields))
54
+ end
59
55
  end
60
56
 
61
- def composite_field(*values)
62
- values.compact.join(".")
57
+ def array_fields(fields)
58
+ Array(fields).map { |str| str.to_s.split(",").map(&:strip) }.flatten.sort
63
59
  end
64
60
 
65
- def nested_fields(fields)
66
- array_fields(fields).map { |field| nested_field(field.split(".")) }.compact.uniq
61
+ def association?(key)
62
+ reflections.keys.include?(key)
67
63
  end
68
64
  end
69
65
  end
@@ -0,0 +1,92 @@
1
+ require 'active_record'
2
+ require "active_model_serializers"
3
+
4
+ module Fields
5
+ module Serializer
6
+
7
+ class FieldsTree
8
+ attr_reader :klass, :fields, :associations
9
+
10
+ def initialize(klass)
11
+ @klass = klass
12
+ @fields = []
13
+ @associations = {}
14
+ end
15
+
16
+ def presence
17
+ self if fields.present? || associations.present?
18
+ end
19
+
20
+ def merge!(join_field)
21
+ return self unless join_field.present?
22
+ parent, rest = join_field.to_s.split(".", 2)
23
+ if rest.blank?
24
+ fields << parent if !(existing_field?(parent) || association?(parent))
25
+ else
26
+ existing_association?(parent) ? associations[parent].merge!(rest) : add_association!(parent, rest)
27
+ end
28
+ self
29
+ end
30
+
31
+ def notation
32
+ if fields.present?
33
+ if associations.present?
34
+ fields.dup << associations_to_notation
35
+ else
36
+ fields.one? ? fields.first.dup : fields.dup
37
+ end
38
+ else
39
+ associations_to_notation.presence
40
+ end
41
+ end
42
+
43
+ def to_includes
44
+ to_includes = associations.inject([]) do |result, (k, v)|
45
+ v_includes = v.to_includes
46
+ if v_includes.present?
47
+ new_has_entry = { k => v_includes }
48
+ hash = result.find { |e| e.is_a?(Hash) }
49
+ hash ? hash.merge!(new_has_entry) : (result << new_has_entry)
50
+ result
51
+ else
52
+ result << k
53
+ end
54
+ end.presence
55
+ Array(to_includes).one? ? to_includes.first : to_includes
56
+ end
57
+
58
+ def to_s
59
+ notation.to_s
60
+ end
61
+
62
+ private
63
+
64
+ def add_association!(parent, rest)
65
+ if association?(parent)
66
+ nested_class = klass.reflections[parent].klass
67
+ nested_fields_tree = FieldsTree.new(nested_class).merge!(rest).presence
68
+ new_association = { parent => nested_fields_tree } if nested_fields_tree
69
+ associations.merge!(new_association) if new_association
70
+ end
71
+ end
72
+
73
+ def associations_to_notation
74
+ associations.inject({}) do |result, (k, v)|
75
+ result.merge!(k => v.notation)
76
+ end
77
+ end
78
+
79
+ def existing_association?(value)
80
+ !!associations[value]
81
+ end
82
+
83
+ def existing_field?(value)
84
+ fields.include?(value)
85
+ end
86
+
87
+ def association?(value)
88
+ klass.reflections.keys.include?(value)
89
+ end
90
+ end
91
+ end
92
+ end
@@ -1,5 +1,5 @@
1
1
  module Fields
2
2
  module Serializer
3
- VERSION = "0.4.0"
3
+ VERSION = "0.5.0"
4
4
  end
5
5
  end
@@ -4,7 +4,7 @@ require 'active_support/concern'
4
4
  require "fields/serializer/version"
5
5
  require "fields/serializer/active_record"
6
6
  require "fields/serializer/action_controller"
7
- require "fields/serializer/field_serializer"
7
+ require "fields/serializer/fields_tree"
8
8
 
9
9
  module Fields
10
10
  module Serializer
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fields-serializer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stuart Chinery
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: exe
12
12
  cert_chain: []
13
- date: 2017-12-19 00:00:00.000000000 Z
13
+ date: 2017-12-20 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: bundler
@@ -145,7 +145,7 @@ files:
145
145
  - lib/fields/serializer.rb
146
146
  - lib/fields/serializer/action_controller.rb
147
147
  - lib/fields/serializer/active_record.rb
148
- - lib/fields/serializer/field_serializer.rb
148
+ - lib/fields/serializer/fields_tree.rb
149
149
  - lib/fields/serializer/version.rb
150
150
  homepage: https://github.com/ltello/fields-serializer
151
151
  licenses:
@@ -1,56 +0,0 @@
1
- require "active_model_serializers"
2
-
3
- # This is a generic serializer intended to return a subset of a model's attributes.
4
- # It can be used with any model but does not currently support associations.
5
- #
6
- # Example usage:
7
- # render json: @region, serializer: FieldSerializer, fields: [:id, :title]
8
- #
9
- # > { "id": "5f19582d-ee28-4e89-9e3a-edc42a8b59e5", "title": "London" }
10
- #
11
- class FieldSerializer < ActiveModel::Serializer
12
- def attributes(*args)
13
- fields = Array(args.first).map { |str| str.to_s.split(",").map(&:strip) }.flatten
14
- adding_id do
15
- merging_attributes do
16
- fields.map { |field| create_attribute_structure(field.split("."), object) }
17
- end
18
- end
19
- end
20
-
21
- private
22
-
23
- def adding_id(&block)
24
- block.call.merge(id: object.id)
25
- end
26
-
27
- def create_attribute_structure(attribute_stack, model)
28
- return model unless model.present?
29
- if model.kind_of?(ActiveRecord::Relation)
30
- collection_attribute_structure(model, attribute_stack)
31
- else
32
- parent = attribute_stack.first
33
- if attribute_stack.count > 1
34
- attribute_structure(model, attribute_stack[1..-1], parent)
35
- else
36
- value_structure(model, parent)
37
- end
38
- end
39
- end
40
-
41
- def attribute_structure(model, attribute_stack, parent)
42
- { parent => create_attribute_structure(attribute_stack, model.send(parent)) }
43
- end
44
-
45
- def collection_attribute_structure(models, attribute_stack)
46
- models.map { |model| create_attribute_structure(attribute_stack, model) }.compact
47
- end
48
-
49
- def merging_attributes(&block)
50
- block.call.inject(:deep_merge!)
51
- end
52
-
53
- def value_structure(model, attribute_name)
54
- { attribute_name => model.send(attribute_name) }
55
- end
56
- end