ree_lib 1.0.69 → 1.0.71

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: c746828c8b09f58c0406659649ef01ba7b7300f4b1f35f808f8e1f58a0dc4165
4
- data.tar.gz: c03ddd00be42dbbe2ee2e2660a105a84912d19ccb5251fd882bd88d04e5bd781
3
+ metadata.gz: 83750e368c52ed97daa01d069e57a184129c6bafb7bd7867aa8c71685562da16
4
+ data.tar.gz: 37ea4a0f4c852bed89692d47f5c86891f281a16f1e13f04faeb2670f57bfd173
5
5
  SHA512:
6
- metadata.gz: 5fa6ca950b18d4e6678610f8dd1b1d3c239090e18d7c914be33643e75d846332e041109d8cb2f6d301398e9eafeeb6897320bfac688c2defb427bc2bec6a4e5c
7
- data.tar.gz: 13405b30d0b4be4aba5e579f8bab5a410af29fe820a5aca37502ea6a7d356a5d5fdad315214fa70b8581927c7ee712607a101b191ec2d9fd383a4b4e1989f8a8
6
+ metadata.gz: ff00e69c9a2b54dd03eee000076e3aac5cb0236b53cdbba0ed669eba2d5918d1c14d88f0c8a6d8e58497a86e02208a0f5850fbef294e552ad2f994aaa83cf89c
7
+ data.tar.gz: bb9a5580e423c17d894d07a69896f39d0d26ef9fc21b8bd3871c505034516a20fa2c5bf6a95827a1733a4cb06a6e6fea7bb4dd0a0e4735f13eca1fb5f2087cb9
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ree_lib (1.0.69)
4
+ ree_lib (1.0.71)
5
5
  binding_of_caller (~> 1.0.0)
6
6
  i18n (~> 1.12.0)
7
7
  loofah (~> 2.18.0)
@@ -93,7 +93,7 @@ module ReeDao
93
93
  association = Association.new(self, parent_dao, list, **global_opts)
94
94
 
95
95
  if assoc_type == :field
96
- association.handle_field(assoc_name, __opts)
96
+ association.handle_field(__opts)
97
97
  else
98
98
  association.load(assoc_type, assoc_name, **get_assoc_opts(__opts), &block)
99
99
  end
@@ -48,7 +48,8 @@ class ReeDao::PgJsonb < ReeMapper::AbstractWrapper
48
48
  Float,
49
49
  String,
50
50
  Bool,
51
- NilClass
51
+ NilClass,
52
+ Rational,
52
53
  ]
53
54
  ).throws(ReeMapper::TypeError)
54
55
  def db_load(value, name:, role: nil, fields_filters: [])
@@ -26,3 +26,11 @@ module ReeDao
26
26
  ENV.has_key?("REE_DAO_SYNC_ASSOCIATIONS") && ENV["REE_DAO_SYNC_ASSOCIATIONS"] == "true"
27
27
  end
28
28
  end
29
+
30
+ # ReeEnum::Value#sql_literal is used to properly serialize enum values
31
+ # for database queries
32
+ class ReeEnum::Value
33
+ def sql_literal(*)
34
+ mapped_value.to_s
35
+ end
36
+ end
@@ -130,8 +130,7 @@ RSpec.describe :agg do
130
130
  end
131
131
 
132
132
  def some_method(list)
133
- # puts list.map(&:id)
134
- # puts list.map { _1.class.name }
133
+ list.each { _1.some_field = :some_value if _1.respond_to?(:some_field=) }
135
134
  end
136
135
 
137
136
  def passport_opts
@@ -978,4 +977,20 @@ RSpec.describe :agg do
978
977
  res = agg(users, users.where(organization_id: organization.id))
979
978
  expect(res.count).to eq(2)
980
979
  }
980
+
981
+ context "when sync mode enabled" do
982
+ it {
983
+ organization = ReeDaoAggTest::Organization.new(name: "Test Org")
984
+ organizations.put(organization)
985
+ user = ReeDaoAggTest::User.new(name: "John", age: 33, organization_id: organization.id)
986
+ users.put(user)
987
+
988
+ allow(user).to receive(:some_field=)
989
+ expect(user).to receive(:some_field=).with(:some_value)
990
+
991
+ ENV['REE_DAO_SYNC_ASSOCIATIONS'] = "true"
992
+ res = agg_users.([user])
993
+ ENV.delete('REE_DAO_SYNC_ASSOCIATIONS')
994
+ }
995
+ end
981
996
  end
@@ -29,6 +29,7 @@ RSpec.describe 'ReeDao::PgJsonb' do
29
29
  pg_jsonb? :number, integer
30
30
  pg_jsonb? :boolean, bool
31
31
  pg_jsonb? :any, any
32
+ pg_jsonb? :rational, rational
32
33
  end
33
34
  }
34
35
 
@@ -39,13 +40,15 @@ RSpec.describe 'ReeDao::PgJsonb' do
39
40
  numbers: [1, 2],
40
41
  figures: [{ coords: 'x=1,y=1' }],
41
42
  number: 1,
42
- boolean: true
43
+ boolean: true,
44
+ rational: Rational("1/3"),
43
45
  })).to eq({
44
46
  payload: { key: 'key' },
45
47
  numbers: [1, 2],
46
48
  figures: [{ coords: 'x=1,y=1' }],
47
49
  number: 1,
48
- boolean: true
50
+ boolean: true,
51
+ rational: "1/3",
49
52
  })
50
53
  }
51
54
 
@@ -69,13 +72,15 @@ RSpec.describe 'ReeDao::PgJsonb' do
69
72
  numbers: Sequel::Postgres::JSONBArray.new([1, 2]),
70
73
  figures: Sequel::Postgres::JSONBArray.new([{ coords: 'x=1,y=1' }]),
71
74
  number: 1,
72
- boolean: true
75
+ boolean: true,
76
+ rational: "1/3"
73
77
  })).to eq({
74
78
  payload: { key: 'key' },
75
79
  numbers: [1, 2],
76
80
  figures: [{ coords: 'x=1,y=1' }],
77
81
  number: 1,
78
- boolean: true
82
+ boolean: true,
83
+ rational: Rational("1/3"),
79
84
  })
80
85
  }
81
86
 
@@ -4,10 +4,10 @@ module ReeEnum::Contractable
4
4
  include Ree::Contracts::Truncatable
5
5
 
6
6
  def valid?(value)
7
- value.is_a?(ReeEnum::Value) && value.enum_name == self.enum_name && all.include?(value)
7
+ value.is_a?(ReeEnum::Value) && value.enum_name == get_enum_name && get_values.each.include?(value)
8
8
  end
9
9
 
10
10
  def message(value, name, lvl = 1)
11
- return "expected one of #{self.name}, got #{value.class} => #{truncate(value.inspect)}"
11
+ "expected one of #{self.name}, got #{value.class} => #{truncate(value.inspect)}"
12
12
  end
13
13
  end
@@ -52,28 +52,17 @@ module ReeEnum
52
52
  ] => ReeEnum::Value
53
53
  ).throws(ReeMapper::CoercionError)
54
54
  def cast(value, name:, role: nil)
55
- if value.is_a?(String)
56
- enum_val = @enum.values.all.detect { |v| v.to_s == value }
57
-
58
- if !enum_val
59
- raise ReeMapper::CoercionError, "`#{name}` should be one of #{@enum.values.all.map(&:to_s).inspect}"
60
- end
61
-
62
- enum_val
63
- elsif value.is_a?(Integer)
64
- enum_val = @enum.values.all.detect { |v| v.to_i == value }
65
-
66
- if !enum_val
67
- raise ReeMapper::CoercionError, "`#{name}` should be one of #{@enum.values.all.map(&:to_s).inspect}"
68
- end
69
-
70
- enum_val
71
- else
72
- enum_val = @enum.values.all.detect { |v| v == value }
73
- return enum_val if enum_val
55
+ enum_value = if value.is_a?(String)
56
+ @enum.get_values.by_value(value)
57
+ elsif value.is_a?(ReeEnum::Value)
58
+ @enum.get_values.each.find { _1 == value }
59
+ end
74
60
 
75
- raise ReeMapper::CoercionError, "`#{name}` should be one of #{@enum.values.all.map(&:to_s).inspect}"
61
+ if enum_value.nil?
62
+ raise ReeMapper::CoercionError, "`#{name}` should be one of #{enum_inspection}"
76
63
  end
64
+
65
+ enum_value
77
66
  end
78
67
 
79
68
  contract(
@@ -81,21 +70,38 @@ module ReeEnum
81
70
  Kwargs[
82
71
  name: String,
83
72
  role: Nilor[Symbol, ArrayOf[Symbol]]
84
- ] => Integer
73
+ ] => Or[Integer, String]
85
74
  )
86
75
  def db_dump(value, name:, role: nil)
87
- value.to_i
76
+ value.mapped_value
88
77
  end
89
78
 
90
79
  contract(
91
- Integer,
80
+ Or[Integer, String],
92
81
  Kwargs[
93
82
  name: String,
94
83
  role: Nilor[Symbol, ArrayOf[Symbol]]
95
84
  ] => ReeEnum::Value
96
- ).throws(ReeMapper::TypeError)
85
+ ).throws(ReeMapper::CoercionError)
97
86
  def db_load(value, name:, role: nil)
98
- cast(value, name: name, role: role)
87
+ enum_val = @enum.get_values.by_mapped_value(value)
88
+
89
+ if !enum_val
90
+ raise ReeMapper::CoercionError, "`#{name}` should be one of #{enum_inspection}"
91
+ end
92
+
93
+ enum_val
94
+ end
95
+
96
+ private
97
+
98
+ def enum_inspection
99
+ @enum_inspect ||= truncate(@enum.get_values.each.map(&:to_s).inspect)
100
+ end
101
+
102
+ def truncate(str, limit = 180)
103
+ return str if str.length <= limit
104
+ "#{str[0..limit]}..."
99
105
  end
100
106
  end
101
107
 
@@ -113,7 +119,7 @@ module ReeEnum
113
119
  ->(*) {
114
120
  {
115
121
  type: 'string',
116
- enum: values.all.map(&:to_s)
122
+ enum: get_values.each.map(&:to_s)
117
123
  }
118
124
  }
119
125
  )
@@ -128,7 +134,7 @@ module ReeEnum
128
134
  )
129
135
 
130
136
  mapper_factory.register_type(
131
- self.enum_name, type_for_mapper
137
+ self.get_enum_name, type_for_mapper
132
138
  )
133
139
  end
134
140
  end
@@ -5,20 +5,6 @@ require_relative 'values'
5
5
  require_relative 'contractable'
6
6
 
7
7
  module ReeEnum::Enumerable
8
- module CommonMethods
9
- def by_value(value)
10
- values.by_value(value)
11
- end
12
-
13
- def by_number(number)
14
- values.by_number(number)
15
- end
16
-
17
- def all
18
- values.all
19
- end
20
- end
21
-
22
8
  def self.included(base)
23
9
  base.extend(ClassMethods)
24
10
  end
@@ -26,43 +12,45 @@ module ReeEnum::Enumerable
26
12
  module ClassMethods
27
13
  include ReeEnum::Contractable
28
14
 
15
+ RESTRICTED_METHODS = [
16
+ :setup_enum, :get_values, :get_enum_name,
17
+ :val, :self, :class, :alias
18
+ ].freeze
19
+
29
20
  def setup_enum(enum_name)
30
21
  @values ||= ReeEnum::Values.new(self, enum_name)
31
22
  end
32
23
 
33
- def values
24
+ def get_values
34
25
  @values
35
26
  end
36
27
 
37
- def enum_name
38
- return if !@values
39
- @values.enum_name
28
+ def get_enum_name
29
+ @values&.enum_name
40
30
  end
41
31
 
42
- include CommonMethods
32
+ def val(value, mapped_value = value.to_s, method: value.to_sym)
33
+ value = value.to_s
43
34
 
44
- def val(value, number, label = nil)
45
- if value == :new
46
- raise ArgumentError.new(":new is not allowed as enum value")
35
+ if RESTRICTED_METHODS.include?(method)
36
+ raise ArgumentError.new("#{method.inspect} is not allowed as enum method")
47
37
  end
48
-
49
- enum_value = values.add(value, number: number, label: label)
50
38
 
51
- define_method "#{enum_value.value}" do
52
- by_value(enum_value.value)
39
+ enum_value = @values.add(value, mapped_value, method)
40
+
41
+ define_method(enum_value.method) do
42
+ get_values.by_value(enum_value.value)
53
43
  end
54
44
 
55
- define_singleton_method "#{enum_value.value}" do
56
- by_value(enum_value.value)
45
+ define_singleton_method(enum_value.method) do
46
+ get_values.by_value(enum_value.value)
57
47
  end
58
48
 
59
49
  enum_value
60
50
  end
61
51
  end
62
52
 
63
- def values
64
- self.class.values
53
+ def get_values
54
+ self.class.get_values
65
55
  end
66
-
67
- include CommonMethods
68
56
  end
@@ -1,60 +1,41 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class ReeEnum::Value
4
- attr_reader :enum_class, :enum_name, :value, :label, :number
4
+ attr_reader :enum_class, :enum_name, :value, :method, :mapped_value
5
5
 
6
- contract(Class, Symbol, Symbol, Integer, Nilor[String] => Any)
7
- def initialize(enum_class, enum_name, value, number, label)
6
+ contract(Class, Symbol, String, Or[Integer, String], Symbol => Any)
7
+ def initialize(enum_class, enum_name, value, mapped_value, method)
8
8
  @enum_class = enum_class
9
9
  @enum_name = enum_name
10
10
  @value = value
11
- @label = label
12
- @number = number
11
+ @method = method
12
+ @mapped_value = mapped_value
13
13
  end
14
14
 
15
15
  def to_s
16
- @value.to_s
17
- end
18
-
19
- def to_sym
20
- @value
21
- end
22
-
23
- def to_i
24
- @number
16
+ value
25
17
  end
26
18
 
27
19
  def as_json(*args)
28
20
  to_s
29
21
  end
30
22
 
31
- def label
32
- @label
33
- end
34
-
35
- contract(Or[ReeEnum::Value, Symbol, Integer, Any] => Bool)
23
+ contract(Or[ReeEnum::Value, String, Symbol, Integer, Any] => Bool)
36
24
  def ==(compare)
37
25
  if compare.is_a?(self.class)
38
- @value == compare.value
26
+ value == compare.value
27
+ elsif compare.is_a?(Symbol)
28
+ value == compare.to_s
29
+ elsif compare.is_a?(String)
30
+ value == compare || mapped_value == compare
31
+ elsif compare.is_a?(Integer)
32
+ mapped_value == compare
39
33
  else
40
- @value == compare || @number == compare
34
+ false
41
35
  end
42
36
  end
43
37
 
44
- contract(Or[ReeEnum::Value, String, Integer] => Bool)
45
- def <=>(other)
46
- if other.is_a?(self.class)
47
- @number <=> other.number
48
- elsif other.is_a?(Symbol)
49
- @value == other
50
- elsif other.is_a?(Integer)
51
- @number == other
52
- else
53
- raise ArgumentError.new("unable to compare ReeEnum::Value with other classes")
54
- end
55
- end
56
-
57
38
  def inspect
58
- "#{enum_class.name}##{@value.to_s}"
39
+ "#{enum_class.name}##{value}"
59
40
  end
60
41
  end
@@ -6,41 +6,67 @@ class ReeEnum::Values
6
6
  def initialize(klass, enum_name)
7
7
  @klass = klass
8
8
  @enum_name = enum_name
9
- @collection = {}
9
+ @collection = []
10
+ @collection_by_value = {}
11
+ @collection_by_mapped_value = {}
10
12
  end
11
13
 
12
- def all
13
- @collection.values.sort_by(&:number)
14
+ def to_a
15
+ @collection
14
16
  end
15
17
 
16
- contract(Symbol => ReeEnum::Value).throws(ArgumentError)
18
+ def each(&)
19
+ @collection.each(&)
20
+ end
21
+
22
+ contract(Or[Symbol, String] => Nilor[ReeEnum::Value])
17
23
  def by_value(value)
18
- type = @collection.values.detect {|c| c.value == value}
19
- type || (raise ArgumentError.new("constant for value #{value.inspect} is not found in #{self.inspect}"))
24
+ @collection_by_value[value.to_s]
25
+ end
26
+
27
+ contract(Or[Symbol, String] => ReeEnum::Value).throws(ArgumentError)
28
+ def by_value!(value)
29
+ by_value(value) ||
30
+ (raise ArgumentError.new("constant for value #{value.inspect} is not found in #{self.inspect}"))
31
+ end
32
+
33
+ contract(Or[Integer, String] => Nilor[ReeEnum::Value])
34
+ def by_mapped_value(mapped_value)
35
+ @collection_by_mapped_value[mapped_value]
20
36
  end
21
37
 
22
- contract(Integer => ReeEnum::Value).throws(ArgumentError)
23
- def by_number(number)
24
- type = @collection.values.detect {|c| c.number == number}
25
- type || (raise ArgumentError.new("constant for value #{number.inspect} is not found in #{self.inspect}"))
38
+ contract(Or[Integer, String] => ReeEnum::Value).throws(ArgumentError)
39
+ def by_mapped_value!(mapped_value)
40
+ by_mapped_value(mapped_value) ||
41
+ (raise ArgumentError.new("constant for value #{mapped_value.inspect} is not found in #{self.inspect}"))
26
42
  end
27
43
 
28
44
  def inspect
29
- @collection.values.map(&:inspect).inspect
45
+ @collection.map(&:inspect).inspect
30
46
  end
31
47
 
32
- contract(Symbol, Kwargs[number: Integer, label: Nilor[String]] => ReeEnum::Value)
33
- def add(value, number:, label: nil)
34
- if @collection.has_key?(value)
48
+ contract(String, Or[Integer, String], Symbol => ReeEnum::Value)
49
+ def add(value, mapped_value, method)
50
+ if @collection.any? { _1.method == method }
51
+ raise ArgumentError, "#{@klass}: method #{method.inspect} was already added"
52
+ end
53
+
54
+ if @collection_by_value.key?(value)
35
55
  raise ArgumentError, "#{@klass}: value #{value.inspect} was already added"
36
56
  end
37
57
 
38
- if @collection.values.any? {|v| v.number == number}
39
- raise ArgumentError, "number for #{value.inspect} was already added"
58
+ if @collection_by_mapped_value.key?(mapped_value)
59
+ raise ArgumentError, "#{@klass}: mapped_value(#{mapped_value.inspect}) for #{value.inspect} was already added"
40
60
  end
41
61
 
42
- @collection[value] = ReeEnum::Value.new(
43
- @klass, @enum_name, value, number, label
62
+ enum_value = ReeEnum::Value.new(
63
+ @klass, @enum_name, value, mapped_value, method
44
64
  )
65
+
66
+ @collection << enum_value
67
+ @collection_by_value[value] = enum_value
68
+ @collection_by_mapped_value[mapped_value] = enum_value
69
+
70
+ enum_value
45
71
  end
46
72
  end
@@ -32,11 +32,20 @@ RSpec.describe ReeEnum::DSL do
32
32
 
33
33
  enum :types
34
34
 
35
- val :account, 0
35
+ val :account
36
36
 
37
37
  register_as_mapper_type
38
38
  end
39
39
 
40
+ class Reflexives
41
+ include ReeEnum::DSL
42
+
43
+ enum :reflexives
44
+
45
+ val :self, method: :myself
46
+ val :yourself
47
+ end
48
+
40
49
  class TestMapper
41
50
  include ReeMapper::DSL
42
51
 
@@ -75,11 +84,11 @@ RSpec.describe ReeEnum::DSL do
75
84
  expect(o.first).to eq(0)
76
85
  expect(o.second).to eq(:second)
77
86
  expect(o.second).to eq(1)
78
- expect(o.by_value(:first)).to eq(o.first)
79
- expect(o.by_value(:second)).to eq(o.second)
80
- expect(o.by_number(0)).to eq(o.first)
81
- expect(o.by_number(1)).to eq(o.second)
82
- expect(o.all).to eq([o.first, o.second])
87
+ expect(o.get_values.by_value(:first)).to eq(o.first)
88
+ expect(o.get_values.by_value(:second)).to eq(o.second)
89
+ expect(o.get_values.by_mapped_value(0)).to eq(o.first)
90
+ expect(o.get_values.by_mapped_value(1)).to eq(o.second)
91
+ expect(o.get_values.to_a).to eq([o.first, o.second])
83
92
  end
84
93
 
85
94
  expect {
@@ -146,18 +155,20 @@ RSpec.describe ReeEnum::DSL do
146
155
  ).to eq(
147
156
  {
148
157
  state: 0,
149
- type: 0
158
+ type: "account"
150
159
  }
151
160
  )
152
161
 
153
162
  dto = mapper.db_load({
154
163
  state: 0,
155
- type: 0,
164
+ type: "account",
156
165
  })
157
166
 
158
167
  expect(dto.state).to eq(TestReeEnum::States.first)
159
168
  expect(dto.state).to be_a(ReeEnum::Value)
160
169
  expect(dto.type).to eq(TestReeEnum::Types.account)
161
170
  expect(dto.type).to be_a(ReeEnum::Value)
171
+
172
+ expect(TestReeEnum::Reflexives.myself).to eq(:self)
162
173
  }
163
174
  end
@@ -5,8 +5,8 @@ class ReeI18n::SetLocale
5
5
 
6
6
  fn :set_locale
7
7
 
8
- contract(Symbol => Symbol).throws(I18n::InvalidLocale)
8
+ contract(Or[Symbol, String] => Symbol).throws(I18n::InvalidLocale)
9
9
  def call(locale)
10
- I18n.locale = locale
10
+ I18n.locale = locale.to_sym
11
11
  end
12
12
  end
@@ -27,6 +27,7 @@ class ReeMapper::BuildMapperFactory
27
27
  klass.register_type(:integer, ReeMapper::Integer.new)
28
28
  klass.register_type(:string, ReeMapper::String.new)
29
29
  klass.register_type(:any, ReeMapper::Any.new)
30
+ klass.register_type(:rational, ReeMapper::Rational.new)
30
31
 
31
32
  klass.register_wrapper(:array, ReeMapper::Array)
32
33
 
@@ -12,8 +12,8 @@ class ReeMapper::Float < ReeMapper::AbstractType
12
12
 
13
13
  contract(Any, Kwargs[name: String, role: Nilor[Symbol, ArrayOf[Symbol]]] => Float).throws(ReeMapper::CoercionError, ReeMapper::TypeError)
14
14
  def cast(value, name:, role: nil)
15
- if value.is_a?(Float)
16
- value
15
+ if value.is_a?(Numeric)
16
+ value.to_f
17
17
  elsif value.is_a?(String)
18
18
  begin
19
19
  Float(value)
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ReeMapper::Rational < ReeMapper::AbstractType
4
+ contract(Any, Kwargs[name: String, role: Nilor[Symbol, ArrayOf[Symbol]]] => Rational).throws(ReeMapper::TypeError)
5
+ def serialize(value, name:, role: nil)
6
+ if value.is_a?(Rational)
7
+ value
8
+ else
9
+ raise ReeMapper::TypeError, "`#{name}` should be a rational"
10
+ end
11
+ end
12
+
13
+ contract(Any, Kwargs[name: String, role: Nilor[Symbol, ArrayOf[Symbol]]] => Rational).throws(ReeMapper::CoercionError, ReeMapper::TypeError)
14
+ def cast(value, name:, role: nil)
15
+ if value.is_a?(Rational)
16
+ value
17
+ elsif value.is_a?(String)
18
+ begin
19
+ Rational(value)
20
+ rescue ArgumentError, ZeroDivisionError => e
21
+ raise ReeMapper::CoercionError, "`#{name}` is invalid rational"
22
+ end
23
+ elsif value.is_a?(Numeric)
24
+ Rational(value)
25
+ else
26
+ raise ReeMapper::TypeError, "`#{name}` should be a rational"
27
+ end
28
+ end
29
+
30
+ contract(Any, Kwargs[name: String, role: Nilor[Symbol, ArrayOf[Symbol]]] => String)
31
+ def db_dump(value, name:, role: nil)
32
+ serialize(value, name: name, role: role).to_s
33
+ end
34
+
35
+ contract(Any, Kwargs[name: String, role: Nilor[Symbol, ArrayOf[Symbol]]] => Rational)
36
+ def db_load(value, name:, role: nil)
37
+ cast(value, name: name, role: role)
38
+ end
39
+ end
@@ -34,6 +34,7 @@ module ReeMapper
34
34
  require_relative 'ree_mapper/types/float'
35
35
  require_relative 'ree_mapper/types/integer'
36
36
  require_relative 'ree_mapper/types/string'
37
+ require_relative 'ree_mapper/types/rational'
37
38
 
38
39
  require_relative 'ree_mapper/strategy_outputs/strategy_output'
39
40
  require_relative 'ree_mapper/strategy_outputs/object_output'
@@ -32,7 +32,11 @@ RSpec.describe 'ReeMapper::Float' do
32
32
  }
33
33
 
34
34
  it {
35
- expect(mapper.db_load({ float: BigDecimal("1.1") })).to eq({ float: 1.1 })
35
+ expect(mapper.cast({ float: BigDecimal("1.1") })).to eq({ float: 1.1 })
36
+ }
37
+
38
+ it {
39
+ expect(mapper.cast({ float: 1 })).to eq({ float: 1.0 })
36
40
  }
37
41
 
38
42
  it {
@@ -40,7 +44,7 @@ RSpec.describe 'ReeMapper::Float' do
40
44
  }
41
45
 
42
46
  it {
43
- expect { mapper.db_load({ float: '1.1a' }) }.to raise_error(ReeMapper::CoercionError, '`float` is invalid float')
47
+ expect { mapper.cast({ float: '1.1a' }) }.to raise_error(ReeMapper::CoercionError, '`float` is invalid float')
44
48
  }
45
49
 
46
50
  it {
@@ -97,6 +101,10 @@ RSpec.describe 'ReeMapper::Float' do
97
101
  expect(mapper.db_load({ float: BigDecimal("1.1") })).to eq({ float: 1.1 })
98
102
  }
99
103
 
104
+ it {
105
+ expect(mapper.db_load({ float: 1 })).to eq({ float: 1.0 })
106
+ }
107
+
100
108
  it {
101
109
  expect { mapper.db_load({ float: 'a1.1' }) }.to raise_error(ReeMapper::CoercionError, '`float` is invalid float')
102
110
  }
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+ require 'bigdecimal'
3
+
4
+ RSpec.describe 'ReeMapper::Rational' do
5
+ link :build_mapper_factory, from: :ree_mapper
6
+ link :build_mapper_strategy, from: :ree_mapper
7
+
8
+ let(:mapper_factory) {
9
+ build_mapper_factory(
10
+ strategies: [
11
+ build_mapper_strategy(method: :cast, dto: Hash),
12
+ build_mapper_strategy(method: :serialize, dto: Hash),
13
+ build_mapper_strategy(method: :db_dump, dto: Hash),
14
+ build_mapper_strategy(method: :db_load, dto: Hash)
15
+ ]
16
+ )
17
+ }
18
+
19
+ let(:mapper) {
20
+ mapper_factory.call.use(:cast).use(:serialize).use(:db_dump).use(:db_load) {
21
+ rational :rational
22
+ }
23
+ }
24
+
25
+ describe '#cast' do
26
+ it {
27
+ expect(mapper.cast({ rational: Rational("1/3") })).to eq({ rational: Rational("1/3") })
28
+ }
29
+
30
+ it {
31
+ expect(mapper.cast({ rational: 0.33 })).to eq({ rational: Rational(0.33) })
32
+ }
33
+
34
+ it {
35
+ expect(mapper.cast({ rational: '0.33' })).to eq({ rational: Rational('0.33') })
36
+ }
37
+
38
+ it {
39
+ expect(mapper.cast({ rational: BigDecimal("0.33") })).to eq({ rational: Rational(BigDecimal("0.33")) })
40
+ }
41
+
42
+ it {
43
+ expect { mapper.cast({ rational: 'a333' }) }.to raise_error(ReeMapper::CoercionError, '`rational` is invalid rational')
44
+ }
45
+
46
+ it {
47
+ expect { mapper.cast({ rational: '333a' }) }.to raise_error(ReeMapper::CoercionError, '`rational` is invalid rational')
48
+ }
49
+
50
+ it {
51
+ expect { mapper.cast({ rational: Object.new }) }.to raise_error(ReeMapper::TypeError, "`rational` should be a rational")
52
+ }
53
+ end
54
+
55
+ describe '#serialize' do
56
+ it {
57
+ expect(mapper.serialize({ rational: Rational("1/3") })).to eq({ rational: Rational("1/3") })
58
+ }
59
+
60
+ it {
61
+ expect { mapper.serialize({ rational: '1/3' }) }.to raise_error(ReeMapper::TypeError, "`rational` should be a rational")
62
+ }
63
+
64
+ it {
65
+ expect { mapper.serialize({ rational: nil }) }.to raise_error(ReeMapper::TypeError, "`rational` should be a rational")
66
+ }
67
+
68
+ it {
69
+ expect { mapper.serialize({ rational: Object.new }) }.to raise_error(ReeMapper::TypeError, "`rational` should be a rational")
70
+ }
71
+ end
72
+
73
+ describe '#db_dump' do
74
+ it {
75
+ expect(mapper.db_dump({ rational: Rational("1/3") })).to eq({ rational: "1/3" })
76
+ }
77
+
78
+ it {
79
+ expect { mapper.db_dump({ rational: '1/3' }) }.to raise_error(ReeMapper::TypeError, "`rational` should be a rational")
80
+ }
81
+
82
+ it {
83
+ expect { mapper.db_dump({ rational: nil }) }.to raise_error(ReeMapper::TypeError, "`rational` should be a rational")
84
+ }
85
+
86
+ it {
87
+ expect { mapper.db_dump({ rational: Object.new }) }.to raise_error(ReeMapper::TypeError, "`rational` should be a rational")
88
+ }
89
+ end
90
+
91
+ describe '#db_load' do
92
+ it {
93
+ expect(mapper.db_load({ rational: Rational("1/3") })).to eq({ rational: Rational("1/3") })
94
+ }
95
+
96
+ it {
97
+ expect(mapper.db_load({ rational: 0.33 })).to eq({ rational: Rational(0.33) })
98
+ }
99
+
100
+ it {
101
+ expect(mapper.db_load({ rational: '0.33' })).to eq({ rational: Rational('0.33') })
102
+ }
103
+
104
+ it {
105
+ expect(mapper.db_load({ rational: BigDecimal("0.33") })).to eq({ rational: Rational(BigDecimal("0.33")) })
106
+ }
107
+
108
+ it {
109
+ expect { mapper.db_load({ rational: 'a333' }) }.to raise_error(ReeMapper::CoercionError, '`rational` is invalid rational')
110
+ }
111
+
112
+ it {
113
+ expect { mapper.db_load({ rational: '333a' }) }.to raise_error(ReeMapper::CoercionError, '`rational` is invalid rational')
114
+ }
115
+
116
+ it {
117
+ expect { mapper.db_load({ rational: Object.new }) }.to raise_error(ReeMapper::TypeError, "`rational` should be a rational")
118
+ }
119
+ end
120
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ReeLib
4
- VERSION = "1.0.69"
4
+ VERSION = "1.0.71"
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.69
4
+ version: 1.0.71
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-08-25 00:00:00.000000000 Z
11
+ date: 2023-08-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ree
@@ -1022,6 +1022,7 @@ files:
1022
1022
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/types/date_time.rb
1023
1023
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/types/float.rb
1024
1024
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/types/integer.rb
1025
+ - lib/ree_lib/packages/ree_mapper/package/ree_mapper/types/rational.rb
1025
1026
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/types/string.rb
1026
1027
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/types/time.rb
1027
1028
  - lib/ree_lib/packages/ree_mapper/package/ree_mapper/wrappers/abstract_wrapper.rb
@@ -1042,6 +1043,7 @@ files:
1042
1043
  - lib/ree_lib/packages/ree_mapper/spec/ree_mapper/types/float_spec.rb
1043
1044
  - lib/ree_lib/packages/ree_mapper/spec/ree_mapper/types/hash_spec.rb
1044
1045
  - lib/ree_lib/packages/ree_mapper/spec/ree_mapper/types/integer_spec.rb
1046
+ - lib/ree_lib/packages/ree_mapper/spec/ree_mapper/types/rational_spec.rb
1045
1047
  - lib/ree_lib/packages/ree_mapper/spec/ree_mapper/types/string_spec.rb
1046
1048
  - lib/ree_lib/packages/ree_mapper/spec/ree_mapper/types/time_spec.rb
1047
1049
  - lib/ree_lib/packages/ree_mapper/spec/ree_mapper/types/type_options_spec.rb