fast_serializer_ruby 0.2.1 → 0.6.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.
@@ -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,32 +3,126 @@
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
36
+
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?
24
43
 
25
- cond = @opts[:if] || @opts[:unless]
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)
70
+ else
71
+ context.public_send(cond)
72
+ end
26
73
 
27
- res = context.instance_exec(resource, params, &cond)
28
74
  res = !res unless @opts[:unless].nil?
29
75
 
30
76
  res
31
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
32
126
  end
33
127
  end
34
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
- @key = key
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,22 +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
- super(resource, params) && include_relation?(params)
26
+ super && include_relation?(params)
18
27
  end
19
28
 
29
+ # @param params [Hash]
30
+ # @return [Boolean]
20
31
  def include_relation?(params)
32
+ include?(params) && !exclude?(params)
33
+ end
34
+
35
+ # @param params [Hash]
36
+ # @return [Boolean]
37
+ def exclude?(params)
38
+ return false if params[:exclude].nil?
39
+ return false if params[:exclude].empty?
40
+
41
+ params[:exclude_index].key?(key)
42
+ end
43
+
44
+ # @param params [Hash]
45
+ # @return [Boolean]
46
+ def include?(params)
21
47
  return true if params[:include].nil?
48
+ return false if params[:include].empty?
22
49
 
23
- params[:include].include?(key)
50
+ params[:include_index].key?(key)
24
51
  end
25
52
  end
26
53
  end
@@ -3,91 +3,125 @@
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
15
-
16
- if @params[:include]
17
- if @params[:include].any?
18
- @params[:include] = @params[:include].map(&:to_sym)
19
- end
20
- end
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
+ @params[:include_index] = {}
16
+ @params[:exclude_index] = {}
17
+
18
+ self.include = @params.delete(:include)
19
+ self.exclude = @params.delete(:exclude)
20
+ end
21
+
22
+ def include=(list)
23
+ return unless list
24
+ return if list.empty?
25
+
26
+ @params[:include] = list.map(&:to_sym)
27
+ @params[:include_index] = @params[:include].map { |key| [key, nil] }.to_h
21
28
  end
22
29
 
23
- # @param [Array] attribute_names
30
+ def exclude=(list)
31
+ return unless list
32
+ return if list.empty?
33
+
34
+ @params[:exclude] = list.map(&:to_sym)
35
+ @params[:exclude_index] = @params[:exclude].map { |key| [key, nil] }.to_h
36
+ end
37
+
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
24
43
  def attributes(*attribute_names)
25
44
  attribute_names.each do |attribute_name|
26
- serialization_schema.add_attribute JsonModel::Attribute.new(
27
- key: attribute_name,
28
- method: attribute_name
45
+ serialization_schema.add_attribute(
46
+ JsonModel::Attribute.new(key: attribute_name, method: attribute_name)
29
47
  )
30
48
  end
31
49
  end
32
50
 
33
- # @param [String] attribute_name
34
- # @param [Hash] opts - attribute options
35
- # @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
+ #
36
59
  def attribute(attribute_name, opts = {}, &block)
37
- serialization_schema.add_attribute JsonModel::Attribute.new(
38
- key: attribute_name,
39
- method: block,
40
- opts: opts
60
+ serialization_schema.add_attribute(
61
+ JsonModel::Attribute.new(
62
+ key: attribute_name,
63
+ method: block,
64
+ opts: opts
65
+ )
41
66
  )
42
67
  end
43
68
 
44
- # @param [String] attribute_name
45
- # @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
+ #
46
78
  def has_one(attribute_name, opts = {})
47
79
  serialization_schema.add_attribute JsonModel::HasOneRelationship.new(
48
- key: opts.delete(:key) || attribute_name,
49
- method: opts.delete(:method) || attribute_name,
50
- opts: opts,
51
- 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),
52
84
  serializer: opts.delete(:serializer)
53
85
  )
54
86
  end
55
87
 
56
88
  alias belongs_to has_one
57
89
 
58
- # @param [String] attribute_name
59
- # @param [Hash] opts - attribute options
90
+ # @param attribute_name [String]
91
+ # @param opts [Hash] attribute options
60
92
  def has_many(attribute_name, opts = {})
61
- serialization_schema.add_attribute JsonModel::HasManyRelationship.new(
62
- key: opts.delete(:key) || attribute_name,
63
- method: opts.delete(:method) || attribute_name,
64
- opts: opts,
65
- schema: opts.delete(:schema),
66
- 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
+ )
67
101
  )
68
102
  end
69
103
 
70
104
  # @param [String] attribute_name
71
105
  # @param [Hash] opts - attribute options
72
106
  def list(attribute_name, opts = {})
73
- serialization_schema.add_attribute JsonModel::Array.new(
74
- key: attribute_name,
75
- method: attribute_name,
76
- opts: opts,
77
- schema: opts.delete(:schema),
78
- 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
+ )
79
115
  )
80
116
  end
81
117
 
82
118
  # @param [String] root_key - a key under which serialization result is nested
83
119
  def root(root_key)
84
- self._root = root_key
120
+ @_root = root_key
85
121
  end
86
122
 
87
123
  def deep_copy
88
- schema = FastSerializer::Schema.new
89
- schema.params = params
90
- schema._root = _root
124
+ schema = FastSerializer::Schema.new(params, _root, strict)
91
125
 
92
126
  serialization_schema.attributes.each do |key, attribute|
93
127
  schema.serialization_schema.attributes[key] = attribute
@@ -97,20 +131,33 @@ module FastSerializer
97
131
  end
98
132
 
99
133
  def serialize_resource(resource, params = {}, context = self)
100
- _params_dup = self.params.merge(params).symbolize_keys
134
+ Utils.ref_merge(self.params, params)
135
+ _params_dup = FastSerializer::Utils.symbolize_keys(self.params)
101
136
  meta = _params_dup.delete(:meta)
102
137
 
103
- is_collection = resource.respond_to?(:size) && !resource.respond_to?(:each_pair)
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))
146
+
147
+ res = if is_collection
104
148
 
105
- _serialization_schema = if is_collection
106
- JsonModel::Array.new(schema: serialization_schema)
107
- else
108
- serialization_schema
109
- end
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
110
155
 
111
- res = _serialization_schema.serialize(resource, _params_dup, context)
112
- root = (_root || _params_dup.delete(:root))
113
- res = { root => res } if root && !root.empty?
156
+ else
157
+ serialization_schema.serialize(resource, _params_dup, context)
158
+ end
159
+
160
+ res = { root => res } if root && !root.empty?
114
161
 
115
162
  res[:meta] = meta if res.is_a?(Hash) && meta
116
163
 
@@ -118,52 +165,7 @@ module FastSerializer
118
165
  end
119
166
 
120
167
  def serialize_resource_to_json(resource, params = {}, context = self)
121
- FastSerializer.config.coder.dump(serialize_resource(resource, params))
122
- end
123
-
124
- module Mixin
125
-
126
- module ClassMethods
127
- attr_accessor :__schema__
128
-
129
- def inherited(subclass)
130
- subclass.__schema__ = self.__schema__.deep_copy
131
- end
132
-
133
- def method_missing(method, *args, &block)
134
- if __schema__.respond_to?(method)
135
- __schema__.public_send(method, *args, &block)
136
- else
137
- super
138
- end
139
- end
140
- end
141
-
142
- module InstanceMethods
143
- attr_accessor :resource, :params
144
-
145
- def initialize(resource, params = {})
146
- self.resource = resource
147
- self.params = params || {}
148
- end
149
-
150
- def serializable_hash
151
- self.class.__schema__.serialize_resource(resource, params, self)
152
- end
153
-
154
- def serialized_json
155
- self.class.__schema__.serialize_resource_to_json(resource, params, self)
156
- end
157
-
158
- alias as_json serializable_hash
159
- alias to_json serialized_json
160
- end
161
-
162
- def self.included(base)
163
- base.extend ClassMethods
164
- base.include InstanceMethods
165
- base.__schema__ = FastSerializer::Schema.new
166
- end
168
+ FastSerializer.config.coder.dump(serialize_resource(resource, params, context))
167
169
  end
168
170
  end
169
171
  end