fast_serializer_ruby 0.4.1 → 0.6.4

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.
@@ -3,7 +3,11 @@
3
3
  module FastSerializer
4
4
  module JsonModel
5
5
  class Array < Relationship
6
- def serialize(resources, params = {}, context = nil)
6
+ # @param resource [Object]
7
+ # @param params [Hash]
8
+ # @param context [Hash]
9
+ # @return [Array]
10
+ def serialize(resources, params, context)
7
11
  return if resources.nil?
8
12
 
9
13
  if @serializer_klass
@@ -13,7 +17,7 @@ module FastSerializer
13
17
  end
14
18
  end
15
19
 
16
- def included?(_resources, _params = {}, context = nil)
20
+ def included?(*)
17
21
  true
18
22
  end
19
23
  end
@@ -3,33 +3,70 @@
3
3
  module FastSerializer
4
4
  module JsonModel
5
5
  class Attribute < Node
6
- def serialize(resource, params = {}, context = nil)
7
- context ||= self
6
+ attr_accessor :mixin,
7
+ :method_name,
8
+ :method_arity,
9
+ :cond,
10
+ :cond_arity,
11
+ :cond_method_name,
12
+ :injected
8
13
 
9
- if method.is_a?(Proc)
14
+ def initialize(*)
15
+ super
10
16
 
11
- if method.arity.abs == 1
12
- context.instance_exec(resource, &method)
13
- else
14
- context.instance_exec(resource, params, &method)
15
- end
17
+ @mixin = nil
18
+ @method_name = nil
19
+ @injected = false
20
+ @cond_method_name = nil
21
+ @cond = nil
22
+ @cond = @opts[:if] || @opts[:unless] || @cond
16
23
 
17
- else
18
- resource.public_send(method)
19
- end
24
+ init_with_proc if method.is_a?(Proc)
25
+ init_with_cond if !cond.nil? && cond.is_a?(Proc)
26
+ end
27
+
28
+ def injectable?
29
+ !mixin.nil?
20
30
  end
21
31
 
22
- def included?(resource, params, context = nil)
23
- return true if @opts[:if].nil? && @opts[:unless].nil?
32
+ def inject(context)
33
+ context.include(mixin)
34
+ self.injected = true
35
+ end
24
36
 
25
- cond = @opts[:if] || @opts[:unless]
37
+ # @param resource [Object]
38
+ # @param params [Hash]
39
+ # @param context [Hash]
40
+ # @return [Object]
41
+ def serialize(resource, params, context)
42
+ can_execute_on_mixin = injected && !method_name.nil? && !context.nil?
26
43
 
27
- res = if cond.is_a?(Proc)
28
- if cond.arity.abs == 1
29
- context.instance_exec(resource, &cond)
30
- else
31
- context.instance_exec(resource, params, &cond)
32
- end
44
+ val = if can_execute_on_mixin
45
+ call_method_on_context(context, method_name, method_arity, resource, params)
46
+ elsif method.is_a?(Proc)
47
+ call_proc_binding_to_context(context, method, method_arity, resource, params)
48
+ else
49
+ resource.public_send(method)
50
+ end
51
+
52
+ val.freeze
53
+
54
+ val
55
+ end
56
+
57
+ # @param resource [Object]
58
+ # @param params [Hash]
59
+ # @param context [Hash]
60
+ # @return [Boolean]
61
+ def included?(resource, params, context)
62
+ return true if cond.nil?
63
+
64
+ can_execute_on_mixin = injected && !cond_method_name.nil? && !context.nil?
65
+
66
+ res = if can_execute_on_mixin
67
+ call_method_on_context(context, cond_method_name, cond_arity, resource, params)
68
+ elsif cond.is_a?(Proc)
69
+ call_proc_binding_to_context(context, cond, cond_arity, resource, params)
33
70
  else
34
71
  context.public_send(cond)
35
72
  end
@@ -38,6 +75,54 @@ module FastSerializer
38
75
 
39
76
  res
40
77
  end
78
+
79
+ private
80
+
81
+ def init_with_cond
82
+ @cond_method_name = "__#{key}_cond__"
83
+ @cond_arity = cond.arity.abs
84
+ @mixin ||= Module.new
85
+
86
+ if RUBY_VERSION <= '2.5.0'
87
+ @mixin.redefine_method @cond_method_name, &cond
88
+ else
89
+ @mixin.define_method @cond_method_name, &cond
90
+ end
91
+ end
92
+
93
+ def init_with_proc
94
+ @method_name = "__#{key}__"
95
+ @method_arity = method.arity.abs
96
+ @mixin = Module.new
97
+
98
+ if RUBY_VERSION <= '2.5.0'
99
+ @mixin.redefine_method @method_name, &method
100
+ else
101
+ @mixin.define_method @method_name, &method
102
+ end
103
+ end
104
+
105
+ def call_proc_binding_to_context(context, prc, arity, resource, params)
106
+ case arity
107
+ when 1
108
+ context.instance_exec(resource, &prc)
109
+ when 2
110
+ context.instance_exec(resource, params, &prc)
111
+ when 0
112
+ context.instance_exec(&prc)
113
+ end
114
+ end
115
+
116
+ def call_method_on_context(context, method_name, arity, resource, params)
117
+ case arity
118
+ when 0
119
+ context.public_send(method_name)
120
+ when 1
121
+ context.public_send(method_name, resource)
122
+ when 2
123
+ context.public_send(method_name, resource, params)
124
+ end
125
+ end
41
126
  end
42
127
  end
43
128
  end
@@ -3,16 +3,18 @@
3
3
  module FastSerializer
4
4
  module JsonModel
5
5
  class HasManyRelationship < Relationship
6
- def serialize(resource, params = {}, context = nil)
6
+ # @param resource [Object]
7
+ # @param params [Hash]
8
+ # @return [Array<Hash>]
9
+ def serialize(resource, params, _context)
7
10
  collection = resource.public_send(method)
8
11
  return if collection.nil?
9
12
 
10
13
  if @serializer_klass
11
14
  @serializer_klass.new(collection, params).serializable_hash
12
15
  elsif @schema
13
- collection.map { |resource| @schema.serialize_resource(resource, params) }
16
+ collection.map { |entry| @schema.serialize_resource(entry, params) }
14
17
  end
15
-
16
18
  end
17
19
  end
18
20
  end
@@ -3,7 +3,10 @@
3
3
  module FastSerializer
4
4
  module JsonModel
5
5
  class HasOneRelationship < Relationship
6
- def serialize(resource, params = {}, context = nil)
6
+ # @param resource [Object]
7
+ # @param params [Hash]
8
+ # @return [Hash]
9
+ def serialize(resource, params, _)
7
10
  relation = resource.public_send(method)
8
11
 
9
12
  if @serializer_klass
@@ -5,17 +5,25 @@ module FastSerializer
5
5
  class Node
6
6
  attr_accessor :key, :method, :context
7
7
 
8
+ # @param key [String]
9
+ # @param method [String]
10
+ # @param opts [Hash]
8
11
  def initialize(key: nil, method: nil, opts: {}, **_)
9
12
  @key = key&.to_sym
10
13
  @method = method || key
11
14
  @opts = opts || {}
12
15
  end
13
16
 
14
- def serialize(_resource, _params = {}, context = nil)
17
+ # @return [Boolean]
18
+ def injectable?
19
+ false
20
+ end
21
+
22
+ def serialize(_resource, _params, _context = nil)
15
23
  raise NotImplementedError
16
24
  end
17
25
 
18
- def included?(_resource, _params = {}, context = nil)
26
+ def included?(_resource, _params, _context = nil)
19
27
  raise NotImplementedError
20
28
  end
21
29
  end
@@ -5,27 +5,37 @@ module FastSerializer
5
5
  class Object < Node
6
6
  attr_accessor :attributes
7
7
 
8
- def initialize(*args)
8
+ def initialize(args = {})
9
9
  super
10
10
  @attributes = {}
11
11
  end
12
12
 
13
+ # @param attribute [FastSerializer::JsonModel::Node]
13
14
  def add_attribute(attribute)
14
15
  attributes[attribute.key] = attribute
15
16
  end
16
17
 
17
- def serialize(resource, params = {}, context = nil)
18
+ # @param resource [Object]
19
+ # @param params [Hash]
20
+ # @param context [Hash]
21
+ # @return [Hash]
22
+ def serialize(resource, params, context)
18
23
  return if resource.nil?
19
24
 
20
- attributes.values.each_with_object({}) do |attribute, res|
21
- next res unless attribute.included?(resource, params, context)
25
+ result = {}
26
+
27
+ attributes.each do |_, attribute|
28
+ next unless attribute.included?(resource, params, context)
22
29
 
23
30
  val = attribute.serialize(resource, params, context)
24
- res[attribute.key] = val
31
+ result[attribute.key] = val
25
32
  end
33
+
34
+ result
26
35
  end
27
36
 
28
- def included?(_resource, _params = {}, context = nil)
37
+ # @return [Boolean]
38
+ def included?(*)
29
39
  true
30
40
  end
31
41
  end
@@ -5,32 +5,49 @@ module FastSerializer
5
5
  class Relationship < Attribute
6
6
  attr_accessor :serialization_schema
7
7
 
8
- def initialize(key: nil, method: nil, opts: {}, serializer: nil, schema: nil)
8
+ # @param serializer [FastSerializer::Schema::Mixin]
9
+ # @param schema [FastSerializer::Schema]
10
+ def initialize(serializer: nil, schema: nil, **)
9
11
  super
12
+
10
13
  @serializer_klass = serializer
11
14
  @schema = schema
12
15
 
13
- raise ArgumentError, "must provide serializer or schema" if @serializer_klass.nil? && @schema.nil?
16
+ if @serializer_klass.nil? && @schema.nil?
17
+ raise ArgumentError, 'must provide serializer or schema'
18
+ end
14
19
  end
15
20
 
21
+ # @param resource [Object]
22
+ # @param params [Hash]
23
+ # @param context [Hash]
24
+ # @return [Boolean]
16
25
  def included?(resource, params, context)
17
26
  super && include_relation?(params)
18
27
  end
19
28
 
29
+ # @param params [Hash]
30
+ # @return [Boolean]
20
31
  def include_relation?(params)
21
32
  include?(params) && !exclude?(params)
22
33
  end
23
34
 
35
+ # @param params [Hash]
36
+ # @return [Boolean]
24
37
  def exclude?(params)
25
38
  return false if params[:exclude].nil?
26
39
  return false if params[:exclude].empty?
40
+
27
41
  params[:exclude_index].key?(key)
28
42
  end
29
43
 
44
+ # @param params [Hash]
45
+ # @return [Boolean]
30
46
  def include?(params)
31
47
  return true if params[:include].nil?
32
48
  return false if params[:include].empty?
33
- params[:include_index].has_key?(key)
49
+
50
+ params[:include_index].key?(key)
34
51
  end
35
52
  end
36
53
  end
@@ -3,15 +3,15 @@
3
3
  require 'forwardable'
4
4
 
5
5
  module FastSerializer
6
-
7
6
  class Schema
8
- attr_accessor :_root, :serialization_schema, :params
9
-
10
- def initialize(params = {})
11
- @root = nil
12
- @serialization_schema = JsonModel::Object.new
13
- @params = (params || {}).symbolize_keys
14
- @params[:self] = self
7
+ attr_reader :_root, :serialization_schema, :params, :strict
8
+
9
+ def initialize(params = {}, root = nil, strict = nil)
10
+ @root = root
11
+ @strict = strict || FastSerializer.config.strict
12
+ @serialization_schema = FastSerializer::JsonModel::Object.new
13
+ @params = FastSerializer::Utils.symbolize_keys(params || {})
14
+ @params[:self] = self
15
15
  @params[:include_index] = {}
16
16
  @params[:exclude_index] = {}
17
17
 
@@ -20,92 +20,108 @@ module FastSerializer
20
20
  end
21
21
 
22
22
  def include=(list)
23
- return if !list
24
-
23
+ return unless list
24
+ return if list.empty?
25
25
 
26
- if list.any?
27
- @params[:include] = list.map(&:to_sym)
28
- @params[:include_index] = @params[:include].map { |key| [key, nil] }.to_h
29
- end
26
+ @params[:include] = list.map(&:to_sym)
27
+ @params[:include_index] = @params[:include].map { |key| [key, nil] }.to_h
30
28
  end
31
29
 
32
30
  def exclude=(list)
33
- return if !list
31
+ return unless list
32
+ return if list.empty?
34
33
 
35
- if list.any?
36
- @params[:exclude] = list.map(&:to_sym)
37
- @params[:exclude_index] = @params[:exclude].map { |key| [key, nil] }.to_h
38
- end
34
+ @params[:exclude] = list.map(&:to_sym)
35
+ @params[:exclude_index] = @params[:exclude].map { |key| [key, nil] }.to_h
39
36
  end
40
37
 
41
- # @param [Array] attribute_names
38
+ # Defines a list of attributes for serialization
39
+ #
40
+ # @param attribute_names [Array<String, Symbol>] a list of attributes to serialize
41
+ # each of these attributes value is fetched calling a corresponding method from a resource instance
42
+ # passed to the serializer
42
43
  def attributes(*attribute_names)
43
44
  attribute_names.each do |attribute_name|
44
- serialization_schema.add_attribute JsonModel::Attribute.new(
45
- key: attribute_name,
46
- method: attribute_name
45
+ serialization_schema.add_attribute(
46
+ JsonModel::Attribute.new(key: attribute_name, method: attribute_name)
47
47
  )
48
48
  end
49
49
  end
50
50
 
51
- # @param [String] attribute_name
52
- # @param [Hash] opts - attribute options
53
- # @param [Proc] block - result is used as the attribute value
51
+ # Defines an attribute for serialization
52
+ #
53
+ # @param attribute_name [String, Symbol] an attribute name
54
+ # @param opts [Hash] attribute options
55
+ # @option opts [Proc] :if conditional clause. accepts a proc/lambda which has to return a boolean
56
+ # @option opts [Proc] :unless (see opts:if)
57
+ # @param block [Proc] result is used as the attribute value
58
+ #
54
59
  def attribute(attribute_name, opts = {}, &block)
55
- serialization_schema.add_attribute JsonModel::Attribute.new(
56
- key: attribute_name,
57
- method: block,
58
- opts: opts
60
+ serialization_schema.add_attribute(
61
+ JsonModel::Attribute.new(
62
+ key: attribute_name,
63
+ method: block,
64
+ opts: opts
65
+ )
59
66
  )
60
67
  end
61
68
 
62
- # @param [String] attribute_name
63
- # @param [Hash] opts - attribute options
69
+ # Defines an attribute for serialization
70
+ #
71
+ # @param attribute_name [String, Symbol] an attribute name
72
+ # @param opts [Hash] attribute options
73
+ # @option opts [Proc] :if conditional clause. accepts a proc/lambda which has to return a boolean
74
+ # @option opts [Proc] :unless (see opts:if)
75
+ # @option opts [FastSerializer::Schema::Mixin, nil] :serializer a serializer class with injected module or a inherited class
76
+ # @option opts [FastSerializer::Schema] :schema
77
+ #
64
78
  def has_one(attribute_name, opts = {})
65
79
  serialization_schema.add_attribute JsonModel::HasOneRelationship.new(
66
- key: opts.delete(:key) || attribute_name,
67
- method: opts.delete(:method) || attribute_name,
68
- opts: opts,
69
- schema: opts.delete(:schema),
80
+ key: opts.delete(:key) || attribute_name,
81
+ method: opts.delete(:method) || attribute_name,
82
+ opts: opts,
83
+ schema: opts.delete(:schema),
70
84
  serializer: opts.delete(:serializer)
71
85
  )
72
86
  end
73
87
 
74
88
  alias belongs_to has_one
75
89
 
76
- # @param [String] attribute_name
77
- # @param [Hash] opts - attribute options
90
+ # @param attribute_name [String]
91
+ # @param opts [Hash] attribute options
78
92
  def has_many(attribute_name, opts = {})
79
- serialization_schema.add_attribute JsonModel::HasManyRelationship.new(
80
- key: opts.delete(:key) || attribute_name,
81
- method: opts.delete(:method) || attribute_name,
82
- opts: opts,
83
- schema: opts.delete(:schema),
84
- serializer: opts.delete(:serializer),
93
+ serialization_schema.add_attribute(
94
+ JsonModel::HasManyRelationship.new(
95
+ key: opts.delete(:key) || attribute_name,
96
+ method: opts.delete(:method) || attribute_name,
97
+ opts: opts,
98
+ schema: opts.delete(:schema),
99
+ serializer: opts.delete(:serializer)
100
+ )
85
101
  )
86
102
  end
87
103
 
88
104
  # @param [String] attribute_name
89
105
  # @param [Hash] opts - attribute options
90
106
  def list(attribute_name, opts = {})
91
- serialization_schema.add_attribute JsonModel::Array.new(
92
- key: attribute_name,
93
- method: attribute_name,
94
- opts: opts,
95
- schema: opts.delete(:schema),
96
- serializer: opts.delete(:serializer)
107
+ serialization_schema.add_attribute(
108
+ JsonModel::Array.new(
109
+ key: attribute_name,
110
+ method: attribute_name,
111
+ opts: opts,
112
+ schema: opts.delete(:schema),
113
+ serializer: opts.delete(:serializer)
114
+ )
97
115
  )
98
116
  end
99
117
 
100
118
  # @param [String] root_key - a key under which serialization result is nested
101
119
  def root(root_key)
102
- self._root = root_key
120
+ @_root = root_key
103
121
  end
104
122
 
105
123
  def deep_copy
106
- schema = FastSerializer::Schema.new
107
- schema.params = params
108
- schema._root = _root
124
+ schema = FastSerializer::Schema.new(params, _root, strict)
109
125
 
110
126
  serialization_schema.attributes.each do |key, attribute|
111
127
  schema.serialization_schema.attributes[key] = attribute
@@ -115,21 +131,33 @@ module FastSerializer
115
131
  end
116
132
 
117
133
  def serialize_resource(resource, params = {}, context = self)
118
- _params_dup = self.params.merge(params).symbolize_keys
134
+ Utils.ref_merge(self.params, params)
135
+ _params_dup = FastSerializer::Utils.symbolize_keys(self.params)
119
136
  meta = _params_dup.delete(:meta)
120
137
 
121
- is_collection = resource.respond_to?(:size) && !resource.respond_to?(:each_pair)
122
- is_collection = params.delete(:is_collection) if params.has_key?(:is_collection)
138
+ is_collection = if _params_dup.key?(:is_collection)
139
+ _params_dup.delete(:is_collection)
140
+ params.delete(:is_collection)
141
+ else
142
+ resource.respond_to?(:each) && !resource.respond_to?(:each_pair)
143
+ end
144
+
145
+ root = (_root || _params_dup.delete(:root))
123
146
 
124
- _serialization_schema = if is_collection
125
- JsonModel::Array.new(schema: serialization_schema)
126
- else
127
- serialization_schema
128
- end
147
+ res = if is_collection
129
148
 
130
- res = _serialization_schema.serialize(resource, _params_dup, context)
131
- root = (_root || _params_dup.delete(:root))
132
- res = { root => res } if root && !root.empty?
149
+ if !context.is_a?(self.class)
150
+ # need to bind context
151
+ resource.map { |entry| context.class.new(entry, _params_dup).serializable_hash }
152
+ else
153
+ JsonModel::Array.new(schema: serialization_schema).serialize(resource, _params_dup, context)
154
+ end
155
+
156
+ else
157
+ serialization_schema.serialize(resource, _params_dup, context)
158
+ end
159
+
160
+ res = { root => res } if root && !root.empty?
133
161
 
134
162
  res[:meta] = meta if res.is_a?(Hash) && meta
135
163
 
@@ -139,52 +167,5 @@ module FastSerializer
139
167
  def serialize_resource_to_json(resource, params = {}, context = self)
140
168
  FastSerializer.config.coder.dump(serialize_resource(resource, params, context))
141
169
  end
142
-
143
- module Mixin
144
-
145
- module ClassMethods
146
- attr_accessor :__schema__
147
-
148
- def inherited(subclass)
149
- subclass.__schema__ = self.__schema__.deep_copy
150
- end
151
-
152
- def method_missing(method, *args, &block)
153
- if __schema__.respond_to?(method)
154
- __schema__.public_send(method, *args, &block)
155
- else
156
- super
157
- end
158
- end
159
- end
160
-
161
- module InstanceMethods
162
- attr_accessor :resource, :params
163
-
164
- def initialize(resource, params = {})
165
- self.resource = resource
166
- self.params = params || {}
167
- end
168
-
169
- def serializable_hash(opts = {})
170
- self.params = params.merge(opts)
171
- self.class.__schema__.serialize_resource(resource, params, self)
172
- end
173
-
174
- def serialized_json(opts = {})
175
- self.params = params.merge(opts)
176
- self.class.__schema__.serialize_resource_to_json(resource, params, self)
177
- end
178
-
179
- alias as_json serializable_hash
180
- alias to_json serialized_json
181
- end
182
-
183
- def self.included(base)
184
- base.extend ClassMethods
185
- base.include InstanceMethods
186
- base.__schema__ = FastSerializer::Schema.new
187
- end
188
- end
189
170
  end
190
171
  end