fast_serializer_ruby 0.4.1 → 0.6.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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