smart_types 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -1
  3. data/CHANGELOG.md +17 -0
  4. data/Gemfile.lock +59 -48
  5. data/README.md +178 -31
  6. data/Rakefile +0 -1
  7. data/bin/console +2 -2
  8. data/lib/smart_core/types/primitive.rb +54 -15
  9. data/lib/smart_core/types/primitive/factory.rb +46 -7
  10. data/lib/smart_core/types/primitive/factory/definition_context.rb +114 -4
  11. data/lib/smart_core/types/primitive/invariant_control.rb +64 -0
  12. data/lib/smart_core/types/primitive/invariant_control/chain.rb +58 -0
  13. data/lib/smart_core/types/primitive/invariant_control/chain/result.rb +64 -0
  14. data/lib/smart_core/types/primitive/invariant_control/factory.rb +54 -0
  15. data/lib/smart_core/types/primitive/invariant_control/factory/chain_definition_context.rb +39 -0
  16. data/lib/smart_core/types/primitive/invariant_control/result.rb +104 -0
  17. data/lib/smart_core/types/primitive/invariant_control/single.rb +54 -0
  18. data/lib/smart_core/types/primitive/invariant_control/single/result.rb +63 -0
  19. data/lib/smart_core/types/primitive/mult_factory.rb +25 -10
  20. data/lib/smart_core/types/primitive/mult_factory/definition_context.rb +4 -2
  21. data/lib/smart_core/types/primitive/mult_validator.rb +34 -0
  22. data/lib/smart_core/types/primitive/mult_validator/result.rb +8 -0
  23. data/lib/smart_core/types/primitive/nilable_factory.rb +24 -9
  24. data/lib/smart_core/types/primitive/nilable_validator.rb +83 -0
  25. data/lib/smart_core/types/primitive/nilable_validator/result.rb +78 -0
  26. data/lib/smart_core/types/primitive/sum_factory.rb +25 -10
  27. data/lib/smart_core/types/primitive/sum_factory/definition_context.rb +3 -1
  28. data/lib/smart_core/types/primitive/sum_validator.rb +101 -0
  29. data/lib/smart_core/types/primitive/sum_validator/result.rb +100 -0
  30. data/lib/smart_core/types/primitive/validator.rb +84 -0
  31. data/lib/smart_core/types/primitive/validator/result.rb +78 -0
  32. data/lib/smart_core/types/value.rb +9 -0
  33. data/lib/smart_core/types/value/enumerator.rb +13 -0
  34. data/lib/smart_core/types/value/enumerator_chain.rb +13 -0
  35. data/lib/smart_core/types/value/io.rb +13 -0
  36. data/lib/smart_core/types/value/method.rb +9 -0
  37. data/lib/smart_core/types/value/nil.rb +0 -2
  38. data/lib/smart_core/types/value/range.rb +9 -0
  39. data/lib/smart_core/types/value/rational.rb +13 -0
  40. data/lib/smart_core/types/value/set.rb +13 -0
  41. data/lib/smart_core/types/value/string_io.rb +13 -0
  42. data/lib/smart_core/types/value/unbound_method.rb +9 -0
  43. data/lib/smart_core/types/version.rb +2 -1
  44. data/smart_types.gemspec +5 -4
  45. metadata +53 -17
  46. data/lib/smart_core/types/primitive/mult_checker.rb +0 -31
  47. data/lib/smart_core/types/primitive/nilable_checker.rb +0 -37
  48. data/lib/smart_core/types/primitive/sum_checker.rb +0 -31
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 12c87a28e4cb88c84907c5d811253c4cb70e3e4aad462010c47c1a6d88c21d81
4
- data.tar.gz: af0155909d10f02369aa5b60c97de4b0b91fbfad92ba79d1f94b5b3333ede373
3
+ metadata.gz: d5f0c7f2b54ceaa7fe20e85d7d280b64b8310eb5f4fa802d11052074e6b10f96
4
+ data.tar.gz: 021c3c6194fb4f54246f4a44a7c548ce9da0790a66b95e0a060547e73efeaf3f
5
5
  SHA512:
6
- metadata.gz: aa5eb3001d7909b30e9fe2df27b92bff6ee22d9e92ef27f37dfbe11c18f968ee932cf0e064ecb2db808d8a0913d3c5fe7650d0d7b15af51bbe4591afeef39670
7
- data.tar.gz: e0fa90c1783113d8d702414596cf001e0e54b786c6d21248e52f5e2401901eddebb09d182349abccda301998782242fa02d4a94e61eb2a3f301fe982a7d5ceff
6
+ metadata.gz: 6e2fc86bcea2292570d2c51e9b95ead6ecc30f8251db75a61fda088c1a84bdb15cea65fdc478728869fb5e5e56b7cddb774938cae38c60e4dee70197e6c1c7f3
7
+ data.tar.gz: c7b27e2f70f3f8eac851f63365b3287a2c7aeb8f7f999679daa715d5ff7de93b32bd6204ea67f6af2c2ea1e5ccd569027051674c9266c62ff7d425c55692619a
@@ -5,7 +5,8 @@ inherit_gem:
5
5
  - lib/rubocop.rspec.yml
6
6
 
7
7
  AllCops:
8
- TargetRubyVersion: 2.7.1
8
+ TargetRubyVersion: 2.7.2
9
+ NewCops: enable
9
10
  Include:
10
11
  - lib/**/*.rb
11
12
  - spec/**/*.rb
@@ -17,3 +18,7 @@ AllCops:
17
18
  # NOTE: support for old ruby versions
18
19
  Style/RedundantBegin:
19
20
  Enabled: false
21
+
22
+ # NOTE: used only in specs and it is ok in specs
23
+ Lint/EmptyBlock:
24
+ Enabled: false
@@ -1,2 +1,19 @@
1
1
  # Changelog
2
2
  All notable changes to this project will be documented in this file.
3
+
4
+ ## [0.2.0] - 2020-11-21
5
+ ### Added
6
+ - Brand new **Type invariant API**:
7
+ - globally refactored validation logic (with backward compatability for `#valid?(value)` method);
8
+ - new type definition DSL: `.invariant(name)` and `.invariant_chain(name)`;
9
+ - chained invariants will be invoked according to the definition order (second invokation
10
+ depends on previous successful invariant check);
11
+ - new validation API: `validate(value)` (with `#errors` support based on invariant names);
12
+ - at this moment Invariant API is supported only by primitive types (type sum and type multiplication support coming soon);
13
+
14
+ ### Changed
15
+
16
+ - Updated `smart_engine` dependency (to `~> 0.7`) (need `SmartCore::Engine::Atom`);
17
+
18
+ ## [0.1.0] - 2020-05-05
19
+ - Release :)
@@ -1,91 +1,102 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- smart_types (0.1.0)
5
- smart_engine (~> 0.6)
4
+ smart_types (0.2.0)
5
+ smart_engine (~> 0.7)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- activesupport (6.0.2.2)
10
+ activesupport (6.0.3.4)
11
11
  concurrent-ruby (~> 1.0, >= 1.0.2)
12
12
  i18n (>= 0.7, < 2)
13
13
  minitest (~> 5.1)
14
14
  tzinfo (~> 1.1)
15
- zeitwerk (~> 2.2)
16
- armitage-rubocop (0.82.0.2)
17
- rubocop (= 0.82.0)
18
- rubocop-performance (= 1.5.2)
19
- rubocop-rails (= 2.5.2)
15
+ zeitwerk (~> 2.2, >= 2.2.2)
16
+ armitage-rubocop (1.3.1.1)
17
+ rubocop (= 1.3.1)
18
+ rubocop-performance (= 1.9.0)
19
+ rubocop-rails (= 2.8.1)
20
20
  rubocop-rake (= 0.5.1)
21
- rubocop-rspec (= 1.39.0)
22
- ast (2.4.0)
23
- concurrent-ruby (1.1.6)
24
- diff-lcs (1.3)
21
+ rubocop-rspec (= 2.0.0)
22
+ ast (2.4.1)
23
+ coderay (1.1.3)
24
+ concurrent-ruby (1.1.7)
25
+ diff-lcs (1.4.4)
25
26
  docile (1.3.2)
26
- i18n (1.8.2)
27
+ i18n (1.8.5)
27
28
  concurrent-ruby (~> 1.0)
28
- jaro_winkler (1.5.4)
29
- minitest (5.14.0)
30
- parallel (1.19.1)
31
- parser (2.7.1.2)
32
- ast (~> 2.4.0)
33
- rack (2.2.2)
29
+ method_source (1.0.0)
30
+ minitest (5.14.2)
31
+ parallel (1.20.0)
32
+ parser (2.7.2.0)
33
+ ast (~> 2.4.1)
34
+ pry (0.13.1)
35
+ coderay (~> 1.1)
36
+ method_source (~> 1.0)
37
+ rack (2.2.3)
34
38
  rainbow (3.0.0)
35
39
  rake (13.0.1)
40
+ regexp_parser (1.8.2)
36
41
  rexml (3.2.4)
37
- rspec (3.9.0)
38
- rspec-core (~> 3.9.0)
39
- rspec-expectations (~> 3.9.0)
40
- rspec-mocks (~> 3.9.0)
41
- rspec-core (3.9.2)
42
- rspec-support (~> 3.9.3)
43
- rspec-expectations (3.9.1)
42
+ rspec (3.10.0)
43
+ rspec-core (~> 3.10.0)
44
+ rspec-expectations (~> 3.10.0)
45
+ rspec-mocks (~> 3.10.0)
46
+ rspec-core (3.10.0)
47
+ rspec-support (~> 3.10.0)
48
+ rspec-expectations (3.10.0)
44
49
  diff-lcs (>= 1.2.0, < 2.0)
45
- rspec-support (~> 3.9.0)
46
- rspec-mocks (3.9.1)
50
+ rspec-support (~> 3.10.0)
51
+ rspec-mocks (3.10.0)
47
52
  diff-lcs (>= 1.2.0, < 2.0)
48
- rspec-support (~> 3.9.0)
49
- rspec-support (3.9.3)
50
- rubocop (0.82.0)
51
- jaro_winkler (~> 1.5.1)
53
+ rspec-support (~> 3.10.0)
54
+ rspec-support (3.10.0)
55
+ rubocop (1.3.1)
52
56
  parallel (~> 1.10)
53
- parser (>= 2.7.0.1)
57
+ parser (>= 2.7.1.5)
54
58
  rainbow (>= 2.2.2, < 4.0)
59
+ regexp_parser (>= 1.8)
55
60
  rexml
61
+ rubocop-ast (>= 1.1.1)
56
62
  ruby-progressbar (~> 1.7)
57
63
  unicode-display_width (>= 1.4.0, < 2.0)
58
- rubocop-performance (1.5.2)
59
- rubocop (>= 0.71.0)
60
- rubocop-rails (2.5.2)
61
- activesupport
64
+ rubocop-ast (1.1.1)
65
+ parser (>= 2.7.1.5)
66
+ rubocop-performance (1.9.0)
67
+ rubocop (>= 0.90.0, < 2.0)
68
+ rubocop-ast (>= 0.4.0)
69
+ rubocop-rails (2.8.1)
70
+ activesupport (>= 4.2.0)
62
71
  rack (>= 1.1)
63
- rubocop (>= 0.72.0)
72
+ rubocop (>= 0.87.0)
64
73
  rubocop-rake (0.5.1)
65
74
  rubocop
66
- rubocop-rspec (1.39.0)
67
- rubocop (>= 0.68.1)
75
+ rubocop-rspec (2.0.0)
76
+ rubocop (~> 1.0)
77
+ rubocop-ast (>= 1.1.0)
68
78
  ruby-progressbar (1.10.1)
69
- simplecov (0.18.5)
79
+ simplecov (0.19.1)
70
80
  docile (~> 1.1)
71
81
  simplecov-html (~> 0.11)
72
- simplecov-html (0.12.2)
73
- smart_engine (0.6.0)
82
+ simplecov-html (0.12.3)
83
+ smart_engine (0.8.0)
74
84
  thread_safe (0.3.6)
75
- tzinfo (1.2.7)
85
+ tzinfo (1.2.8)
76
86
  thread_safe (~> 0.1)
77
87
  unicode-display_width (1.7.0)
78
- zeitwerk (2.3.0)
88
+ zeitwerk (2.4.1)
79
89
 
80
90
  PLATFORMS
81
91
  ruby
82
92
 
83
93
  DEPENDENCIES
84
- armitage-rubocop (~> 0.82)
94
+ armitage-rubocop (~> 1.3)
85
95
  bundler (~> 2.1)
96
+ pry (~> 0.13)
86
97
  rake (~> 13.0)
87
- rspec (~> 3.9)
88
- simplecov (~> 0.18)
98
+ rspec (~> 3.10)
99
+ simplecov (~> 0.19)
89
100
  smart_types!
90
101
 
91
102
  BUNDLED WITH
data/README.md CHANGED
@@ -39,7 +39,7 @@ type4 = type1 & type2
39
39
 
40
40
  ## Supported types
41
41
 
42
- - Primitive Value Types:
42
+ - Primitives
43
43
 
44
44
  ```ruby
45
45
  SmartCore::Types::Value::Any
@@ -67,45 +67,40 @@ SmartCore::Types::Value::TimeBased
67
67
 
68
68
  ## Nilable types
69
69
 
70
+ - invoke `.nilable` on any type object:
71
+
70
72
  ```ruby
71
- SmartCore::Types::Value::Any.nilable
72
- SmartCore::Types::Value::Nil.nilable
73
73
  SmartCore::Types::Value::String.nilable
74
- SmartCore::Types::Value::Symbol.nilable
75
- SmartCore::Types::Value::Text.nilable
76
- SmartCore::Types::Value::Integer.nilable
77
- SmartCore::Types::Value::Float.nilable
78
- SmartCore::Types::Value::Numeric.nilable
79
- SmartCore::Types::Value::BigDecimal.nilable
80
- SmartCore::Types::Value::Boolean.nilable
81
- SmartCore::Types::Value::Array.nilable
82
- SmartCore::Types::Value::Hash.nilable
83
- SmartCore::Types::Value::Proc.nilable
84
- SmartCore::Types::Value::Class.nilable
85
- SmartCore::Types::Value::Module.nilable
86
- SmartCore::Types::Value::Time.nilable.nilable
87
- SmartCore::Types::Value::DateTime.nilable
88
- SmartCore::Types::Value::Date.nilable
89
- SmartCore::Types::Value::TimeBased
74
+ # -- or --
75
+ SmartCore::Types::Value::Time.nilable
76
+ # and etc.
90
77
  ```
91
78
 
92
79
  ---
93
80
 
94
- ## Type validation and type casting
81
+ ## Custom type definition
95
82
 
96
- ```ruby
97
- # documentation is coming
83
+ Type definition is a composition of:
98
84
 
99
- SmartCore::Types::Value::String.valid?('test') # => true
100
- SmartCore::Types::Value::String.valid?(123.45) # => false
85
+ - type checker (required);
86
+ - type caster (optional);
87
+ - type invariants (optional);
88
+ - type invariant chains (optional);
101
89
 
102
- SmartCore::Types::Value::String.cast(123) # => "123"
103
- SmartCore::Types::Value::Float.cast('55') # => 55.0
104
- ```
90
+ Invariant is a custom validation block that will work as a logical value checker. You can have as much invariants as you want.
105
91
 
106
- ---
92
+ Type invariants does not depends on each other (invariant defined out from chain does not depends on other invariants);
107
93
 
108
- ## Custom type definition
94
+ Invariants inside invariant chains will be invoked in order they was defined and each internal invariant depends on the valid previous invairant check.
95
+
96
+ **!IMPORTANT!** Type sum and type multiplication does not support invariant checking and custom invariant definitioning at this moment.
97
+ Type sum and type mult ignores type invariants in their validation logic (currently this functionality in development yet).
98
+
99
+ Invariant checking is a special validation layer (see [#type validation](#type-validation) readme section). Invariant error code pattern:
100
+ - for invariant chains: `TypeName.invariant_chain_name.invariant_name`;
101
+ - for single invariant: `TypeName.invariant_name`;
102
+
103
+ #### Primitive type definition
109
104
 
110
105
  ```ruby
111
106
  # documentation is coming
@@ -122,12 +117,140 @@ SmartCore::Types::Value.define_type(:String) do |type|
122
117
  end
123
118
  ```
124
119
 
120
+ #### With type invariants
121
+
122
+ ```ruby
123
+ SmartCore::Types::Value.define_type(:String) do |type|
124
+ type.define_checker do |value|
125
+ value.is_a?(::String)
126
+ end
127
+
128
+ type.define_caster do |value|
129
+ value.to_s
130
+ end
131
+
132
+ # NOTE:
133
+ # invariant defined out from chain does not depends on other invariants
134
+ type.invariant(:uncensored_content) do |value|
135
+ !value.include?('uncensored_word')
136
+ end
137
+
138
+ type.invariant(:filled) do |value|
139
+ value != ''
140
+ end
141
+
142
+ type.invariant_chain(:password) do
143
+ invariant(:should_present) { |value| value != '' }
144
+ invariant(:should_have_numbers) { |value| v.match?(/[0-9]+/) }
145
+ # NOTE:
146
+ # inside a chain each next invariant invokation
147
+ # depends on previous successful invariant check
148
+ end
149
+ end
150
+ ```
151
+
152
+ ---
153
+
154
+ ## Type validation
155
+
156
+ Type validation reflects on two APIs:
157
+
158
+ - type checker ([how to define type checkers](#custom-type-definition));
159
+ - type invariants (invariants and invariant chains) ([how to define type invariants](#custom-type-definition));
160
+
161
+ Type invariants does not depends on each other (invariant defined out from the chain does not depends on other invariants);
162
+
163
+ Invariants inside invariant chains will be invoked in order they was defined and each internal invariant depends on the valid previous invairant check.
164
+
165
+ **!IMPORTANT!** Type sum and type multiplication does not support invariant checking and custom invariant definitioning at this moment.
166
+ Type sum and type mult ignores type invariants in their validation logic (currently this functionality in development yet).
167
+
168
+ Invariant checking is a special validation layer (see [#type validation](#type-validation) readme section) and represents a set of error codes in result object;
169
+
170
+ Type valdiation interface:
171
+
172
+ - `valid?(value)` - validates value and returns `true` or `false`;
173
+ - returns `ture` only if the type checker returns `true` and all invariants are valid;
174
+ - `validate(value)` - validates value and returns the monadic result object:
175
+ - `SmartCore::Types::Primitive::Validator::Result` for primitive types;
176
+ - `SmartCore::Types::Primitive::SumValidator::Result` for sum-based types;
177
+ - `SmartCore::Types::Primitive::MultValidator::Result` for mult-based types;
178
+ - `SmartCore::Types::Primitive::NilableValidator::Result` for nilable types;
179
+ - `validate!(value)` - validates value and returns nothing (for successful validation) or
180
+ raises an exception (`SmartCore::Types::TypeError`) (for unsuccessful validation);
181
+
182
+ Validation result object interface:
183
+
184
+ - `#success?` / `#failure?` (`#success?` is a combination of `valid_check? && valid_invariants?`; `#failure?` - is an opposite of `#success?`);
185
+ - `#valid_check?` (valid type checker or not);
186
+ - `#valid_invariants?` (`false` if at least one invariant is invalid);
187
+ - `#errors` (the same as `#invariant_errors` and the same as `#error_codes`) - an array of failed invariant names;
188
+ - error code patterns:
189
+ - for invariant chains: `TypeName.invariant_chain_name.invariant_name`;
190
+ - for single invariant: `TypeName.invariant_name`;
191
+ - `#checked_value` (the same as `#value`) - checked value :)
192
+
193
+
194
+ ```ruby
195
+ SmartCore::Types::Value::String.valid?('test123') # => true
196
+ SmartCore::Types::Value::String.valid?(123.45) # => false
197
+ ```
198
+
199
+ ```ruby
200
+ result = SmartCore::Types::Value::String.validate('test')
201
+
202
+ result.checked_value # => 'test'
203
+ # --- same as: ---
204
+ result.value
205
+
206
+ result.success? # => false (valid_check? && valid_invariants?)
207
+ result.failure? # => true
208
+
209
+ result.valid_check? # => true
210
+ result.valid_invariants? # => false
211
+
212
+ # invariant errors:
213
+ result.errors # => ['String.password.should_have_numbers']
214
+ # -- same as: ---
215
+ result.invariant_errors
216
+ # -- same as: ---
217
+ result.error_codes
218
+ ```
219
+
220
+ ```ruby
221
+ result = SmartCore::Types::Value::String.validate('test1234')
222
+ result.success? # => true
223
+ result.errors # => []
224
+ ```
225
+
226
+ ```ruby
227
+ SmartCore::Types::Value::String.validate!('test') # => SmartCore::Types::TypeError
228
+ ```
229
+
230
+ ---
231
+
232
+ ## Type casting
233
+
234
+ ```
235
+ SmartCore::Types::Value::String.cast(123) # => "123"
236
+ SmartCore::Types::Value::Float.cast('55') # => 55.0
237
+ ```
238
+
125
239
  ---
126
240
 
127
241
  ## Basic type algebra
128
242
 
243
+ > (type sum and type multiplication does not support invariants at this moment (in development yet));
244
+
129
245
  ```ruby
130
246
  # documentation is coming
247
+
248
+ # how to define primitive type sum:
249
+ SmartCore::Types::Value::Text = SmartCore::Types::Value::String | SmartCore::Types::Value::Symbol
250
+ SmartCore::Types::Value::Numeric = SmartCore::Types::Value::Float | SmartCore::Types::Value::Integer
251
+
252
+ # how to define primitive type multiplication:
253
+ SmartCore::Types::Value::CryptoString = SmartCore::Types::Value::NumberdString & SmartCore::Types::Value::SymbolicString
131
254
  ```
132
255
 
133
256
  ---
@@ -195,6 +318,9 @@ end
195
318
  SmartCore::Types::Value::Time.refine_caster do |value, original_caster|
196
319
  # new type caster
197
320
  end
321
+
322
+ # .refine_invariant
323
+ # .refine_invariant_chain
198
324
  ```
199
325
 
200
326
  - options for type casters:
@@ -218,7 +344,16 @@ SmartCore::Types::Value::Method
218
344
  SmartCore::Types::Value::UnboundMethod
219
345
  SmartCore::Types::Value::Enumerable
220
346
  SmartCore::Types::Value::Comparable
347
+ SmartCore::Types::Value::Enumerator
348
+ SmartCore::Types::Value::EnumeratorChain
349
+ SmartCore::Types::Value::Range
350
+ SmartCore::Types::Value::Rational
351
+ SmartCore::Types::Value::Set
352
+ SmartCore::Types::Value::SortedSet
353
+ SmartCore::Types::Value::IO
354
+ SmartCore::Types::Value::StringIO
221
355
  SmartCore::Types::Struct::Schema
356
+ SmartCore::Types::Struct::JSONSchema
222
357
  SmartCore::Types::Struct::StrictArray
223
358
  SmartCore::Types::Struct::StrictHash
224
359
  SmartCore::Types::Struct::Map
@@ -230,11 +365,23 @@ SmartCore::Types::Protocol::Ancestors
230
365
  SmartCore::Types::Protocol::Enumerable
231
366
  SmartCore::Types::Protocol::Comparable
232
367
  SmartCore::Types::Protocol::Forwardable
368
+ SmartCore::Types::Protocol::Callable
233
369
  ```
234
370
 
371
+ - support for type of empty non-defined type (`SmartCore::Types::Primitive::Undefined`);
235
372
  - constrained types;
236
-
237
- - module-based integration;
373
+ - moudle-based type system integration;
374
+ - constructor implementation and support;
375
+ - support for invariant checking (and custom definitioning) in sum-types;
376
+ - to provide a type comparability and compatability between all passed types
377
+ you should provide `type.reconcilable { |value, *types| .... }` setting;
378
+ - `type.reconcilable` should be accesible for type sum and type mult definitions;
379
+ - (**preliminarily**) invariants of the concrete passed type should be valid for sucessful invariant check;
380
+ - support for invariant checking (and definitioning) in mult-types;
381
+ - to provide a type comparability and compatability between all passed types
382
+ you should provide `type.reconcilable { |value, *types| .... }` setting;
383
+ - `type.reconcilable` should be accesible for type sum and type mult definitions;
384
+ - (**preliminarily**) all invariants of all types should be valid for sucessful invariant check;
238
385
 
239
386
  ## Contributing
240
387