ree_lib 1.0.35 → 1.0.36

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 190a7da138d373c34c6e7636265ccce86c37e6ad5fd0c02b4e3c81c7e376393a
4
- data.tar.gz: ad6b89d186eec9a9006cc6d217ee82d6f20a34428bd052953aac7e2624a6b41c
3
+ metadata.gz: 61b5c4cb5d7eef9fc932ebe3a674c5c752cb82d035248910a8a3da7a1ffe45b5
4
+ data.tar.gz: d604e93726df4c19ebfe4c244a9d91da836e2a587d8599969bd9e5980a4e07d9
5
5
  SHA512:
6
- metadata.gz: 45199483809ad7917a207699df3acd7689db5dd246dfdce7910a7427a4d100750196a903b51aba2e24d0594141a01b8086e0767f9427b555fa2bdece22e83491
7
- data.tar.gz: d20668755d3da265a0ec0351220dfe9604cfaf21ebbb78833b093445afd01280e7b6e7d83ebba428b3808c346754fb1debcbed29197098f136c3b1a30bb24369
6
+ metadata.gz: e05879958d07507dce29039623c4c88a5eb4b49ea7c9eee56b86346f004cbea2f455a42464142cc91d04aeefb07e65d3f2436073a3e58bcfd9434b6512cc8d31
7
+ data.tar.gz: c5888ac8151e1016bde7705fdbdc13bc4f814007401ff23d9da9bac160810ca6e1b92008015b507e90a110035848189d6d291093b7fea42ca1df7d6c5f962c24
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ree_lib (1.0.35)
4
+ ree_lib (1.0.36)
5
5
  binding_of_caller (~> 1.0.0)
6
6
  i18n (~> 1.12.0)
7
7
  loofah (~> 2.18.0)
@@ -36,6 +36,8 @@ GEM
36
36
  crass (~> 1.0.2)
37
37
  nokogiri (>= 1.5.9)
38
38
  msgpack (1.6.0)
39
+ nokogiri (1.14.2-x86_64-darwin)
40
+ racc (~> 1.4)
39
41
  nokogiri (1.14.2-x86_64-linux)
40
42
  racc (~> 1.4)
41
43
  oj (3.13.23)
@@ -44,7 +46,7 @@ GEM
44
46
  racc (1.6.2)
45
47
  rainbow (3.1.1)
46
48
  rake (13.0.6)
47
- ree (1.0.23)
49
+ ree (1.0.26)
48
50
  commander (~> 4.6.0)
49
51
  rexml (3.2.5)
50
52
  rollbar (3.3.3)
@@ -18,13 +18,13 @@ class ReeMapper::BuildMapperFactory
18
18
  @strategies = strategies
19
19
  }
20
20
 
21
- klass.register(:bool, Mapper.build(strategies, ReeMapper::Bool.new))
22
- klass.register(:date_time, Mapper.build(strategies, ReeMapper::DateTime.new))
23
- klass.register(:time, Mapper.build(strategies, ReeMapper::Time.new))
24
- klass.register(:date, Mapper.build(strategies, ReeMapper::Date.new))
25
- klass.register(:float, Mapper.build(strategies, ReeMapper::Float.new))
26
- klass.register(:integer, Mapper.build(strategies, ReeMapper::Integer.new))
27
- klass.register(:string, Mapper.build(strategies, ReeMapper::String.new))
21
+ klass.register_type(:bool, ReeMapper::Bool.new)
22
+ klass.register_type(:date_time, ReeMapper::DateTime.new)
23
+ klass.register_type(:time, ReeMapper::Time.new)
24
+ klass.register_type(:date, ReeMapper::Date.new)
25
+ klass.register_type(:float, ReeMapper::Float.new)
26
+ klass.register_type(:integer, ReeMapper::Integer.new)
27
+ klass.register_type(:string, ReeMapper::String.new)
28
28
 
29
29
  klass
30
30
  end
@@ -7,7 +7,7 @@ class ReeMapper::Mapper
7
7
  strategies.each do |strategy|
8
8
  method = strategy.method
9
9
  next if type.respond_to?(method)
10
- raise ReeMapper::UnsupportedTypeError, "type #{type.name} should implement method `#{method}`"
10
+ raise ReeMapper::UnsupportedTypeError, "type #{type.inspect} should implement method `#{method}`"
11
11
  end
12
12
  end
13
13
 
@@ -113,7 +113,7 @@ class ReeMapper::Mapper
113
113
 
114
114
  contract(Symbol => Class).throws(ArgumentError)
115
115
  def dto(strategy_method)
116
- strategy = strategies.detect { _1.method == strategy_method }
116
+ strategy = find_strategy(strategy_method)
117
117
  raise ArgumentError, "there is no :#{strategy_method} strategy" unless strategy
118
118
  strategy.dto
119
119
  end
@@ -124,4 +124,9 @@ class ReeMapper::Mapper
124
124
  strategies.each { _1.prepare_dto(fields.keys) }
125
125
  nil
126
126
  end
127
+
128
+ contract(Symbol => Nilor[ReeMapper::MapperStrategy])
129
+ def find_strategy(strategy_method)
130
+ strategies.detect { _1.method == strategy_method }
131
+ end
127
132
  end
@@ -5,35 +5,42 @@ class ReeMapper::MapperFactory
5
5
  attr_reader :types, :strategies
6
6
  end
7
7
 
8
- contract(Symbol, Any => Class).throws(ArgumentError)
9
- def self.register_type(name, object_type)
10
- register(
8
+ contract(Symbol => Nilor[ReeMapper::MapperStrategy])
9
+ def self.find_strategy(strategy_method)
10
+ strategies.detect { _1.method == strategy_method }
11
+ end
12
+
13
+ contract(Symbol, ReeMapper::AbstractType, Kwargs[strategies: ArrayOf[ReeMapper::MapperStrategy]] => Class)
14
+ def self.register_type(name, object_type, strategies: self.strategies)
15
+ register_mapper(
11
16
  name,
12
17
  ReeMapper::Mapper.build(strategies, object_type)
13
18
  )
14
19
  end
15
20
 
16
- contract(Symbol, Any => Class).throws(ArgumentError)
17
- def self.register(name, type)
18
- raise ArgumentError, "name of mapper type should not include `?`" if name.to_s.end_with?('?')
19
- raise ArgumentError, "type :#{name} already registered" if types.key?(name)
20
- raise ArgumentError, "method :#{name} already defined" if method_defined?(name)
21
+ contract(Symbol, ReeMapper::Mapper => Class).throws(ArgumentError)
22
+ def self.register_mapper(name, type)
23
+ raise ArgumentError, "name of mapper type should not end with `?`" if name.to_s.end_with?('?')
24
+
25
+ defined_strategy_method = types[name]&.flat_map(&:strategy_methods)&.detect { type.find_strategy(_1) }
26
+ raise ArgumentError, "type :#{name} with `#{defined_strategy_method}` strategy already registered" if defined_strategy_method
27
+ raise ArgumentError, "method :#{name} already defined" if !types.key?(name) && method_defined?(name)
21
28
 
22
29
  type = type.dup
23
30
  type.name = name
24
31
  type.freeze
25
- types[name] = type
32
+ types[name] ||= []
33
+ types[name] << type
26
34
 
27
35
  class_eval(<<~RUBY, __FILE__, __LINE__ + 1)
28
36
  def #{name}(field_name = nil, optional: false, **opts)
29
37
  raise ReeMapper::Error, "invalid DSL usage" unless @mapper
30
38
  raise ArgumentError, "array item can't be optional" if field_name.nil? && optional
31
39
 
32
- type = self.class.types.fetch(:#{name})
40
+ type = self.class.types.fetch(:#{name}).detect { (@mapper.strategy_methods - _1.strategy_methods).empty? }
33
41
 
34
- @mapper.strategy_methods.each do |method|
35
- next if type.respond_to?(method)
36
- raise ReeMapper::UnsupportedTypeError, "type :#{name} should implement method `\#{method}`"
42
+ unless type
43
+ raise ReeMapper::UnsupportedTypeError, "type :#{name} should implement `\#{@mapper.strategy_methods.join(', ')}`"
37
44
  end
38
45
 
39
46
  return ReeMapper::Field.new(type, optional: optional, **opts) unless field_name
@@ -22,7 +22,7 @@ class ReeMapper::MapperFactoryProxy
22
22
  if strategy_or_method.is_a?(ReeMapper::MapperStrategy)
23
23
  strategy = strategy_or_method
24
24
  else
25
- strategy = mapper_factory.strategies.detect { _1.method == strategy_or_method }
25
+ strategy = mapper_factory.find_strategy(strategy_or_method)
26
26
  raise ArgumentError, "MapperFactory strategy :#{strategy_or_method} not found" unless strategy
27
27
  strategy = strategy.dup
28
28
  strategy.dto = dto if dto
@@ -36,7 +36,7 @@ class ReeMapper::MapperFactoryProxy
36
36
  mapper_factory.new(mapper).instance_exec(&blk)
37
37
  mapper.prepare_dto
38
38
 
39
- mapper_factory.register(register_as, mapper) if register_as
39
+ mapper_factory.register_mapper(register_as, mapper) if register_as
40
40
 
41
41
  after_build&.call(mapper)
42
42
 
@@ -1,17 +1,2 @@
1
1
  class ReeMapper::AbstractType
2
- def serialize(value, name:, role: nil)
3
- raise NotImplementedError
4
- end
5
-
6
- def cast(value, name:, role: nil)
7
- raise NotImplementedError
8
- end
9
-
10
- def db_dump(value, name:, role: nil)
11
- raise NotImplementedError
12
- end
13
-
14
- def db_load(value, name:, role: nil)
15
- raise NotImplementedError
16
- end
17
2
  end
@@ -20,6 +20,8 @@ class ReeMapper::Float < ReeMapper::AbstractType
20
20
  rescue ArgumentError => e
21
21
  raise ReeMapper::CoercionError, "`#{name}` is invalid float"
22
22
  end
23
+ elsif defined?(BigDecimal) && value.is_a?(BigDecimal)
24
+ value.to_f
23
25
  else
24
26
  raise ReeMapper::TypeError, "`#{name}` should be a float"
25
27
  end
@@ -160,7 +160,7 @@ Create `mapper_factory.rb` file to declare `MapperFactory` class.
160
160
  build_mapper_strategy(method: :db_load, dto: Object)
161
161
  ])
162
162
 
163
- mapper_factory.register(:cart_user, user_caster)
163
+ mapper_factory.register_mapper(:cart_user, user_caster)
164
164
 
165
165
  mapper_factory
166
166
  end
@@ -5,35 +5,76 @@ RSpec.describe ReeMapper::MapperFactory do
5
5
  link :build_mapper_factory, from: :ree_mapper
6
6
  link :build_mapper_strategy, from: :ree_mapper
7
7
 
8
- let(:mapper_factory) {
9
- build_mapper_factory(strategies: [
10
- build_mapper_strategy(method: :cast, dto: Hash)
11
- ])
12
- }
8
+ let(:cast_strategy) { build_mapper_strategy(method: :cast, dto: Hash) }
9
+ let(:serialize_strategy) { build_mapper_strategy(method: :serialize, dto: Hash) }
10
+ let(:mapper_factory) { build_mapper_factory(strategies: [cast_strategy, serialize_strategy]) }
11
+
12
+ describe '.register_type' do
13
+ let(:mapper_type) {
14
+ Class.new(ReeMapper::AbstractType) do
15
+ def cast(*); :value end
16
+ def serialize(*); end
17
+ end
18
+ }
13
19
 
14
- describe '.register' do
15
20
  it {
16
- mapper_factory.register(:new_type, ReeMapper::Mapper.build([], ReeMapper::AbstractType.new))
17
- expect(mapper_factory.instance_methods).to include(:new_type)
21
+ mapper_factory.register_type(:new_type, mapper_type.new)
22
+ expect(
23
+ mapper_factory.call.use(:cast) { new_type :val }.cast({ val: :any })
24
+ ).to eq({ val: :value })
18
25
  }
19
26
 
20
- it 'raise an error if the type is already registered' do
21
- mapper_factory.register(:new_type, ReeMapper::Mapper.build([], ReeMapper::AbstractType.new))
27
+ it {
28
+ mapper_factory.register_type(:new_type, mapper_type.new, strategies: [cast_strategy])
29
+ expect(
30
+ mapper_factory.call.use(:cast) { new_type :val }.cast({ val: :any })
31
+ ).to eq({ val: :value })
32
+ }
33
+ end
34
+
35
+ describe '.register_mapper' do
36
+ let(:serializer) { mapper_factory.call.use(:serialize) { integer :id } }
37
+
38
+ it {
39
+ mapper_factory.register_mapper(:new_type, serializer)
40
+
41
+ expect(
42
+ mapper_factory.call.use(:serialize) { new_type :val }.serialize({ val: { id: 1 } })
43
+ ).to eq({ val: { id: 1 } })
44
+ }
45
+
46
+ it 'allow to register caster and serializer with same name' do
47
+ caster = mapper_factory.call.use(:cast) { string :name }
48
+
49
+ mapper_factory.register_mapper(:new_type, serializer)
50
+ mapper_factory.register_mapper(:new_type, caster)
51
+
52
+ expect(
53
+ mapper_factory.call.use(:serialize) { new_type :val }.serialize({ val: { id: 1 } })
54
+ ).to eq({ val: { id: 1 } })
55
+
56
+ expect(
57
+ mapper_factory.call.use(:cast) { new_type :val }.cast({ val: { name: '1' } })
58
+ ).to eq({ val: { name: '1' } })
59
+ end
60
+
61
+ it 'raise an error if the mapper is already registered' do
62
+ mapper_factory.register_mapper(:new_type, serializer)
22
63
 
23
64
  expect {
24
- mapper_factory.register(:new_type, ReeMapper::Mapper.build([], ReeMapper::AbstractType.new))
25
- }.to raise_error(ArgumentError, 'type :new_type already registered')
65
+ mapper_factory.register_mapper(:new_type, serializer)
66
+ }.to raise_error(ArgumentError, 'type :new_type with `serialize` strategy already registered')
26
67
  end
27
68
 
28
- it 'raise an error if the type is ended by ?' do
69
+ it 'raise an error if the mapper name is ended by ?' do
29
70
  expect {
30
- mapper_factory.register(:new_type?, ReeMapper::Mapper.build([], ReeMapper::AbstractType.new))
31
- }.to raise_error(ArgumentError)
71
+ mapper_factory.register_mapper(:new_type?, serializer)
72
+ }.to raise_error(ArgumentError, 'name of mapper type should not end with `?`')
32
73
  end
33
74
 
34
- it 'raise an error if the type method is already registered' do
75
+ it 'raise an error if the mapper name is reserved' do
35
76
  expect {
36
- mapper_factory.register(:array, ReeMapper::Mapper.build([], ReeMapper::AbstractType.new))
77
+ mapper_factory.register_mapper(:array, serializer)
37
78
  }.to raise_error(ArgumentError, 'method :array already defined')
38
79
  end
39
80
  end
@@ -48,13 +89,17 @@ RSpec.describe ReeMapper::MapperFactory do
48
89
  }
49
90
 
50
91
  it {
51
- mapper_factory.register(:new_type, ReeMapper::Mapper.build([], ReeMapper::AbstractType.new))
92
+ serializer = mapper_factory.call.use(:serialize) do
93
+ integer :my_field
94
+ end
95
+
96
+ mapper_factory.register_mapper(:new_type, serializer)
52
97
 
53
98
  expect {
54
99
  mapper_factory.call.use(:cast) do
55
100
  new_type :settings
56
101
  end
57
- }.to raise_error(ReeMapper::UnsupportedTypeError)
102
+ }.to raise_error(ReeMapper::UnsupportedTypeError, 'type :new_type should implement `cast`')
58
103
  }
59
104
 
60
105
  it {
@@ -62,7 +107,9 @@ RSpec.describe ReeMapper::MapperFactory do
62
107
  integer :id
63
108
  end
64
109
 
65
- expect(mapper_factory.instance_methods).to include(:user)
110
+ expect(
111
+ mapper_factory.call.use(:cast) { user :user }.cast({ user: { id: 1 } })
112
+ ).to eq({ user: { id: 1 } })
66
113
  }
67
114
 
68
115
  it {
@@ -80,4 +127,14 @@ RSpec.describe ReeMapper::MapperFactory do
80
127
  }.to raise_error(ReeMapper::ArgumentError, "mapper should contain at least one field")
81
128
  }
82
129
  end
130
+
131
+ describe '.find_strategy' do
132
+ it {
133
+ expect(mapper_factory.find_strategy(:cast)).to eq(cast_strategy)
134
+ }
135
+
136
+ it {
137
+ expect(mapper_factory.find_strategy(:unknown)).to be_nil
138
+ }
139
+ end
83
140
  end
@@ -92,7 +92,7 @@ RSpec.describe ReeMapper::Mapper do
92
92
  )
93
93
  }
94
94
 
95
- it {
95
+ it {
96
96
  expect(mapper.cast({ my_field: 1, hsh: { nested_field: 1 } })).to be_a(Struct)
97
97
  }
98
98
  end
@@ -166,4 +166,28 @@ RSpec.describe ReeMapper::Mapper do
166
166
  expect { mapper.dto(:db_dump) }.to raise_error(ArgumentError, "there is no :db_dump strategy")
167
167
  }
168
168
  end
169
+
170
+ describe '#find_strategy' do
171
+ let(:mapper_factory) {
172
+ build_mapper_factory(
173
+ strategies: [
174
+ build_mapper_strategy(method: :cast),
175
+ build_mapper_strategy(method: :serialize),
176
+ ]
177
+ )
178
+ }
179
+ let(:mapper) {
180
+ mapper_factory.call.use(:cast) do
181
+ integer :my_field
182
+ end
183
+ }
184
+
185
+ it {
186
+ expect(mapper.find_strategy(:cast)).to be_a(ReeMapper::MapperStrategy)
187
+ }
188
+
189
+ it {
190
+ expect(mapper.find_strategy(:serialize)).to be_nil
191
+ }
192
+ end
169
193
  end
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+ require 'bigdecimal'
2
3
 
3
4
  RSpec.describe 'ReeMapper::Float' do
4
5
  link :build_mapper_factory, from: :ree_mapper
@@ -30,6 +31,10 @@ RSpec.describe 'ReeMapper::Float' do
30
31
  expect(mapper.cast({ float: '1.1' })).to eq({ float: 1.1 })
31
32
  }
32
33
 
34
+ it {
35
+ expect(mapper.db_load({ float: BigDecimal("1.1") })).to eq({ float: 1.1 })
36
+ }
37
+
33
38
  it {
34
39
  expect { mapper.cast({ float: 'a1.1' }) }.to raise_error(ReeMapper::CoercionError, '`float` is invalid float')
35
40
  }
@@ -88,6 +93,10 @@ RSpec.describe 'ReeMapper::Float' do
88
93
  expect(mapper.db_load({ float: '1.1' })).to eq({ float: 1.1 })
89
94
  }
90
95
 
96
+ it {
97
+ expect(mapper.db_load({ float: BigDecimal("1.1") })).to eq({ float: 1.1 })
98
+ }
99
+
91
100
  it {
92
101
  expect { mapper.db_load({ float: 'a1.1' }) }.to raise_error(ReeMapper::CoercionError, '`float` is invalid float')
93
102
  }
@@ -6,7 +6,7 @@ class ReeObject::ToHash
6
6
  fn :to_hash do
7
7
  def_error { RecursiveObjectErr }
8
8
  end
9
-
9
+
10
10
  BASIC_TYPES = [
11
11
  Date, Time, Numeric, String, FalseClass, TrueClass, NilClass, Symbol,
12
12
  Module, Class
@@ -57,8 +57,8 @@ class ReeObject::ToHash
57
57
  raise RecursiveObjectErr, "Recursive object found: #{obj}"
58
58
  end
59
59
 
60
- cache[obj.object_id] = acc
61
-
60
+ cache[obj.object_id] = true
61
+
62
62
  obj.instance_variables.each do |var|
63
63
  key_name = var.to_s.delete("@")
64
64
  key_sym = key_name.to_sym
@@ -69,6 +69,8 @@ class ReeObject::ToHash
69
69
  acc[key] = recursively_convert(value, {}, cache)
70
70
  end
71
71
 
72
+ cache.delete(obj.object_id)
73
+
72
74
  acc
73
75
  end
74
76
  end
@@ -167,6 +167,11 @@ RSpec.describe :to_hash do
167
167
  to_hash(obj)
168
168
  }.to raise_error(ReeObject::ToHash::RecursiveObjectErr, /Recursive object found: /)
169
169
  }
170
+
171
+ it {
172
+ obj = obj_klass.new
173
+ expect(to_hash([obj, obj])).to eq([{}, {}])
174
+ }
170
175
  end
171
176
  end
172
177
  end
@@ -11,16 +11,13 @@ RSpec.describe :build_serializer_schema do
11
11
 
12
12
  build_mapper_factory(
13
13
  strategies: strategies
14
- ).register(
14
+ ).register_type(
15
15
  :unregistered_type,
16
- ReeMapper::Mapper.build(
17
- strategies,
18
- Class.new(ReeMapper::AbstractType) do
19
- def serialize(val, role: nil)
20
- val
21
- end
22
- end.new
23
- )
16
+ Class.new(ReeMapper::AbstractType) do
17
+ def serialize(val, role: nil)
18
+ val
19
+ end
20
+ end.new
24
21
  )
25
22
  }
26
23
 
@@ -118,10 +115,10 @@ RSpec.describe :build_serializer_schema do
118
115
  test_unregistered_type: {},
119
116
  partial_value: {
120
117
  type: 'object',
121
- properties: {
118
+ properties: {
122
119
  nested_partial_value: {
123
120
  type: 'object',
124
- properties: {
121
+ properties: {
125
122
  included: { type: 'string' }
126
123
  }
127
124
  }
@@ -15,13 +15,8 @@ RSpec.describe :register_type do
15
15
  build_mapper_strategy(method: :serialize, dto: Hash),
16
16
  ]
17
17
 
18
- build_mapper_factory(strategies: strategies).register(
19
- :my_type,
20
- ReeMapper::Mapper.build(
21
- strategies,
22
- ReeSwagger::MyType.new
23
- )
24
- )
18
+ build_mapper_factory(strategies: strategies)
19
+ .register_type(:my_type, ReeSwagger::MyType.new)
25
20
  }
26
21
 
27
22
  let(:mapper) {
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ReeLib
4
- VERSION = "1.0.35"
4
+ VERSION = "1.0.36"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ree_lib
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.35
4
+ version: 1.0.36
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ruslan Gatiyatov
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-03-06 00:00:00.000000000 Z
11
+ date: 2023-03-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ree