sinclair 1.14.1 → 1.15.0

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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +1 -0
  3. data/README.md +37 -18
  4. data/config/yardstick.yml +10 -0
  5. data/lib/sinclair/caster/class_methods.rb +127 -0
  6. data/lib/sinclair/caster.rb +381 -0
  7. data/lib/sinclair/class_methods.rb +41 -0
  8. data/lib/sinclair/config_class.rb +4 -10
  9. data/lib/sinclair/method_definition/stringifier.rb +9 -7
  10. data/lib/sinclair/version.rb +1 -1
  11. data/lib/sinclair.rb +38 -21
  12. data/spec/integration/readme/sinclair/types_of_definition_spec.rb +8 -8
  13. data/spec/integration/yard/my_builder_spec.rb +8 -14
  14. data/spec/integration/yard/sinclair/add_method_spec.rb +11 -10
  15. data/spec/integration/yard/sinclair/caster/cast_spec.rb +57 -0
  16. data/spec/integration/yard/sinclair/caster/cast_with_spec.rb +54 -0
  17. data/spec/integration/yard/sinclair/caster/caster_for_spec.rb +16 -0
  18. data/spec/integration/yard/sinclair/caster/class_methods_spec.rb +17 -0
  19. data/spec/integration/yard/sinclair/class_methods/build_spec.rb +23 -0
  20. data/spec/integration/yard/sinclair/config_builder_spec.rb +6 -8
  21. data/spec/integration/yard/sinclair/config_class_spec.rb +7 -21
  22. data/spec/integration/yard/sinclair_spec.rb +14 -33
  23. data/spec/lib/sinclair/caster/class_methods_spec.rb +186 -0
  24. data/spec/lib/sinclair/caster_spec.rb +75 -0
  25. data/spec/lib/sinclair/class_methods_spec.rb +38 -0
  26. data/spec/support/models/enum_caster.rb +6 -0
  27. data/spec/support/models/enum_converter.rb +27 -0
  28. data/spec/support/models/hash_model.rb +11 -0
  29. data/spec/support/models/hash_person.rb +11 -0
  30. data/spec/support/models/http_json_model.rb +4 -6
  31. data/spec/support/models/math_caster.rb +17 -0
  32. data/spec/support/models/ruby_string_caster.rb +14 -0
  33. data/spec/support/models/string_parser.rb +9 -0
  34. metadata +20 -2
@@ -0,0 +1,186 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::Caster::ClassMethods do
6
+ subject(:caster) { Class.new(Sinclair::Caster) }
7
+
8
+ describe '.cast_with' do
9
+ let(:value) { instance_double('value', to_p: final_value) }
10
+ let(:final_value) { Random.rand(100) }
11
+
12
+ context 'when a proc is given' do
13
+ it do
14
+ expect { caster.cast_with(:problem, &:to_p) }
15
+ .not_to raise_error
16
+ end
17
+
18
+ context 'when casting is called' do
19
+ before { caster.cast_with(:problem, &:to_p) }
20
+
21
+ it 'returns the cast value' do
22
+ expect(caster.cast(value, :problem)).to eq(final_value)
23
+ end
24
+ end
25
+ end
26
+
27
+ context 'when a proc with two arguments is given' do
28
+ it do
29
+ expect { caster.cast_with(:problem) { |v, **_opts| v.to_p } }
30
+ .not_to raise_error
31
+ end
32
+
33
+ context 'when casting is called' do
34
+ before { caster.cast_with(:problem) { |v, sum:| v.to_p + sum } }
35
+
36
+ it 'returns the cast value' do
37
+ expect(caster.cast(value, :problem, sum: 2))
38
+ .to eq(final_value + 2)
39
+ end
40
+ end
41
+ end
42
+
43
+ context 'when a symbol is given' do
44
+ let(:instance) { Sinclair::Caster.new(&:to_p) }
45
+
46
+ it do
47
+ expect { caster.cast_with(:problem, instance) }
48
+ .not_to raise_error
49
+ end
50
+
51
+ context 'when casting is called' do
52
+ before { caster.cast_with(:problem, instance) }
53
+
54
+ it 'returns the cast value' do
55
+ expect(caster.cast(value, :problem)).to eq(final_value)
56
+ end
57
+ end
58
+ end
59
+
60
+ context 'when a caster is given is given' do
61
+ it do
62
+ expect { caster.cast_with(:problem, :to_p) }
63
+ .not_to raise_error
64
+ end
65
+
66
+ context 'when casting is called' do
67
+ before { caster.cast_with(:problem, :to_p) }
68
+
69
+ it 'returns the cast value' do
70
+ expect(caster.cast(value, :problem)).to eq(final_value)
71
+ end
72
+ end
73
+ end
74
+
75
+ context 'when key is a class' do
76
+ it do
77
+ expect { caster.cast_with(Integer, :to_i) }
78
+ .not_to raise_error
79
+ end
80
+
81
+ context 'when casting is called' do
82
+ before { caster.cast_with(Integer, :to_i) }
83
+
84
+ it 'returns the cast value' do
85
+ expect(caster.cast('10', Integer)).to eq(10)
86
+ end
87
+ end
88
+ end
89
+
90
+ context 'when key is a module' do
91
+ it do
92
+ expect { caster.cast_with(JSON) { |value| JSON.parse(value) } }
93
+ .not_to raise_error
94
+ end
95
+
96
+ context 'when casting is called' do
97
+ before { caster.cast_with(JSON) { |value| JSON.parse(value) } }
98
+
99
+ it 'returns the cast value' do
100
+ expect(caster.cast('{"key":"value"}', JSON))
101
+ .to eq({ 'key' => 'value' })
102
+ end
103
+ end
104
+ end
105
+
106
+ context 'when key is a superclass' do
107
+ it do
108
+ expect { caster.cast_with(Numeric, :to_i) }
109
+ .not_to raise_error
110
+ end
111
+
112
+ context 'when casting is called with the child class' do
113
+ before { caster.cast_with(Numeric, :to_i) }
114
+
115
+ it 'returns the cast value' do
116
+ expect(caster.cast('10', Integer)).to eq(10)
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ describe '.caster_for' do
123
+ context 'when the key has been defined with a symbol key' do
124
+ before { caster.cast_with(:problem, :to_p) }
125
+
126
+ it do
127
+ expect(caster.caster_for(:problem))
128
+ .to be_a(Sinclair::Caster)
129
+ end
130
+ end
131
+
132
+ context 'when the key has not been defined' do
133
+ it do
134
+ expect(caster.caster_for(:problem))
135
+ .to be_a(Sinclair::Caster)
136
+ end
137
+ end
138
+ end
139
+
140
+ describe '.cast' do
141
+ let(:value) { values.sample }
142
+ let(:values) do
143
+ [Random.rand, 'some string', { key: 10 }, Object.new, Class.new, [2, 3]]
144
+ end
145
+
146
+ context 'when klass is nil' do
147
+ it 'returns the value' do
148
+ expect(caster.cast(value, nil))
149
+ .to eq(value)
150
+ end
151
+ end
152
+
153
+ context 'when class is :string' do
154
+ it 'returns the value as string' do
155
+ expect(caster.cast(value, :string))
156
+ .to eq(value.to_s)
157
+ end
158
+ end
159
+
160
+ context 'when class is :integer' do
161
+ let(:value) { '10.5' }
162
+
163
+ it 'returns the value as integer' do
164
+ expect(caster.cast(value, :integer))
165
+ .to eq(10)
166
+ end
167
+ end
168
+
169
+ context 'when class is :float' do
170
+ let(:value) { '10.5' }
171
+
172
+ it 'returns the value as float' do
173
+ expect(caster.cast(value, :float))
174
+ .to eq(10.5)
175
+ end
176
+ end
177
+ end
178
+
179
+ describe '.master_caster!' do
180
+ it 'ignores superclass registered casters' do
181
+ expect { caster.master_caster! }
182
+ .to change { caster.cast('10', :integer) }
183
+ .from(10).to('10')
184
+ end
185
+ end
186
+ end
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::Caster do
6
+ subject(:caster) { caster_class.new(&method_name) }
7
+
8
+ let(:caster_class) { Class.new(described_class) }
9
+
10
+ describe '.cast' do
11
+ context 'when no options are given and the block accepts none' do
12
+ let(:method_name) { :to_s }
13
+
14
+ it 'uses the block to transform the value' do
15
+ expect(caster.cast(10)).to eq('10')
16
+ end
17
+ end
18
+
19
+ context 'when options are given and the block accepts none' do
20
+ let(:method_name) { :to_i }
21
+
22
+ it 'uses the block to transform the value' do
23
+ expect(caster.cast('10', extra: true)).to eq(10)
24
+ end
25
+ end
26
+
27
+ context 'when no options are given and the block accepts options' do
28
+ subject(:caster) do
29
+ caster_class.new do |value, sum: 5|
30
+ (value.to_i + sum).to_s
31
+ end
32
+ end
33
+
34
+ it 'uses the block to transform the value' do
35
+ expect(caster.cast('10')).to eq('15')
36
+ end
37
+ end
38
+
39
+ context 'when options are given and the block accepts options' do
40
+ subject(:caster) do
41
+ caster_class.new do |value, sum:|
42
+ (value.to_i + sum).to_s
43
+ end
44
+ end
45
+
46
+ it 'uses the options in the block' do
47
+ expect(caster.cast('10', sum: 5)).to eq('15')
48
+ end
49
+ end
50
+
51
+ context 'when no options are given and the block requires options' do
52
+ subject(:caster) do
53
+ caster_class.new do |value, sum:|
54
+ (value.to_i + sum).to_s
55
+ end
56
+ end
57
+
58
+ it do
59
+ expect { caster.cast('10') }.to raise_error(ArgumentError)
60
+ end
61
+ end
62
+
63
+ context 'when extra options are given and the block accepts options' do
64
+ subject(:caster) do
65
+ caster_class.new do |value, sum:|
66
+ (value.to_i + sum).to_s
67
+ end
68
+ end
69
+
70
+ it 'ignores extra options' do
71
+ expect(caster.cast('10', sum: 5, extra: true)).to eq('15')
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'spec_helper'
4
+
5
+ describe Sinclair::ClassMethods do
6
+ subject(:builder) { builder_class.new(dummy_class, options) }
7
+
8
+ let(:options) { {} }
9
+ let(:instance) { dummy_class.new }
10
+ let(:dummy_class) { Class.new }
11
+ let(:builder_class) { Sinclair }
12
+
13
+ describe '#build' do
14
+ let(:block) do
15
+ method_name = :some_method
16
+ value = 1
17
+
18
+ proc do
19
+ add_method(method_name) { value }
20
+ end
21
+ end
22
+
23
+ it 'executes the block and builds' do
24
+ expect { builder_class.build(dummy_class, options, &block) }
25
+ .to add_method(:some_method).to(dummy_class)
26
+ end
27
+
28
+ context 'when the method is built and called' do
29
+ before do
30
+ builder_class.build(dummy_class, options, &block)
31
+ end
32
+
33
+ it 'returns the value' do
34
+ expect(instance.some_method).to eq(1)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: false
2
+
3
+ class EnumCaster < Sinclair::Caster
4
+ cast_with(:hash, :to_h)
5
+ cast_with(:array, :to_a)
6
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: false
2
+
3
+ module EnumConverter
4
+ class << self
5
+ def to_hash(value)
6
+ return value if value.is_a?(Hash)
7
+
8
+ hash_caster.cast(value)
9
+ end
10
+
11
+ def to_array(value)
12
+ return value if value.is_a?(Array)
13
+
14
+ array_caster.cast(value)
15
+ end
16
+
17
+ private
18
+
19
+ def hash_caster
20
+ @hash_caster ||= EnumCaster.caster_for(:hash)
21
+ end
22
+
23
+ def array_caster
24
+ @array_caster ||= EnumCaster.caster_for(:array)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class HashModel
4
+ def initialize(hash)
5
+ hash.each do |attribute, value|
6
+ method_name = "#{attribute}="
7
+
8
+ send(method_name, value) if respond_to?(method_name)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ class HashPerson < HashModel
4
+ attr_accessor :name, :age
5
+
6
+ def ==(other)
7
+ return unless other.class == self.class
8
+
9
+ other.name == name && other.age == age
10
+ end
11
+ end
@@ -5,15 +5,13 @@ class HttpJsonModel
5
5
 
6
6
  class << self
7
7
  def parse(attribute, path: [])
8
- builder = Sinclair.new(self)
9
-
10
8
  keys = (path + [attribute]).map(&:to_s)
11
9
 
12
- builder.add_method(attribute) do
13
- keys.inject(hash) { |h, key| h[key] }
10
+ Sinclair.build(self) do
11
+ add_method(attribute) do
12
+ keys.inject(hash) { |h, key| h[key] }
13
+ end
14
14
  end
15
-
16
- builder.build
17
15
  end
18
16
  end
19
17
 
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class MathCaster < Sinclair::Caster
4
+ cast_with(:float, :to_f)
5
+
6
+ cast_with(:log) do |value, base: 10|
7
+ value = MathCaster.cast(value, :float)
8
+
9
+ Math.log(value, base)
10
+ end
11
+
12
+ cast_with(:exp) do |value, base: 10|
13
+ value = MathCaster.cast(value, :float)
14
+
15
+ base**value
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ class RubyStringCaster < Sinclair::Caster
4
+ master_caster!
5
+
6
+ cast_with(NilClass) { 'nil' }
7
+ cast_with(Symbol) { |value| ":#{value}" }
8
+ cast_with(String, :to_json)
9
+ cast_with(Object, :to_s)
10
+
11
+ def self.to_ruby_string(value)
12
+ cast(value, value.class)
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: false
2
+
3
+ class StringParser < Sinclair::Caster
4
+ master_caster!
5
+
6
+ cast_with(JSON) { |value| JSON.parse(value) }
7
+ cast_with(Integer, :to_i)
8
+ cast_with(Float, :to_f)
9
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sinclair
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.14.1
4
+ version: 1.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - DarthJee
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2023-04-25 00:00:00.000000000 Z
11
+ date: 2023-04-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -273,6 +273,9 @@ files:
273
273
  - config/yardstick.yml
274
274
  - docker-compose.yml
275
275
  - lib/sinclair.rb
276
+ - lib/sinclair/caster.rb
277
+ - lib/sinclair/caster/class_methods.rb
278
+ - lib/sinclair/class_methods.rb
276
279
  - lib/sinclair/comparable.rb
277
280
  - lib/sinclair/comparable/class_methods.rb
278
281
  - lib/sinclair/config.rb
@@ -338,6 +341,11 @@ files:
338
341
  - spec/integration/yard/my_builder_spec.rb
339
342
  - spec/integration/yard/sinclair/add_class_method_spec.rb
340
343
  - spec/integration/yard/sinclair/add_method_spec.rb
344
+ - spec/integration/yard/sinclair/caster/cast_spec.rb
345
+ - spec/integration/yard/sinclair/caster/cast_with_spec.rb
346
+ - spec/integration/yard/sinclair/caster/caster_for_spec.rb
347
+ - spec/integration/yard/sinclair/caster/class_methods_spec.rb
348
+ - spec/integration/yard/sinclair/class_methods/build_spec.rb
341
349
  - spec/integration/yard/sinclair/comparable_spec.rb
342
350
  - spec/integration/yard/sinclair/config_builder_spec.rb
343
351
  - spec/integration/yard/sinclair/config_class_spec.rb
@@ -359,6 +367,9 @@ files:
359
367
  - spec/integration/yard/sinclair/options_parser_spec.rb
360
368
  - spec/integration/yard/sinclair/options_spec.rb
361
369
  - spec/integration/yard/sinclair_spec.rb
370
+ - spec/lib/sinclair/caster/class_methods_spec.rb
371
+ - spec/lib/sinclair/caster_spec.rb
372
+ - spec/lib/sinclair/class_methods_spec.rb
362
373
  - spec/lib/sinclair/comparable_spec.rb
363
374
  - spec/lib/sinclair/config/methods_builder_spec.rb
364
375
  - spec/lib/sinclair/config_builder_spec.rb
@@ -417,7 +428,11 @@ files:
417
428
  - spec/support/models/dummy_config.rb
418
429
  - spec/support/models/dummy_configurable.rb
419
430
  - spec/support/models/dummy_options_parser.rb
431
+ - spec/support/models/enum_caster.rb
432
+ - spec/support/models/enum_converter.rb
420
433
  - spec/support/models/env_settings.rb
434
+ - spec/support/models/hash_model.rb
435
+ - spec/support/models/hash_person.rb
421
436
  - spec/support/models/host_config.rb
422
437
  - spec/support/models/http_json_model.rb
423
438
  - spec/support/models/http_person.rb
@@ -426,6 +441,7 @@ files:
426
441
  - spec/support/models/job.rb
427
442
  - spec/support/models/login_config.rb
428
443
  - spec/support/models/login_configurable.rb
444
+ - spec/support/models/math_caster.rb
429
445
  - spec/support/models/my_app_client.rb
430
446
  - spec/support/models/my_builder.rb
431
447
  - spec/support/models/my_class.rb
@@ -438,9 +454,11 @@ files:
438
454
  - spec/support/models/person.rb
439
455
  - spec/support/models/purchase.rb
440
456
  - spec/support/models/random_generator.rb
457
+ - spec/support/models/ruby_string_caster.rb
441
458
  - spec/support/models/server.rb
442
459
  - spec/support/models/server_config.rb
443
460
  - spec/support/models/service_client.rb
461
+ - spec/support/models/string_parser.rb
444
462
  - spec/support/models/tv.rb
445
463
  - spec/support/models/validator_builder.rb
446
464
  - spec/support/sample_model.rb