smart_types 0.1.0.alpha4 → 0.3.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 (82) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +10 -1
  3. data/CHANGELOG.md +42 -0
  4. data/Gemfile.lock +77 -50
  5. data/README.md +424 -28
  6. data/Rakefile +1 -1
  7. data/bin/console +2 -2
  8. data/lib/smart_core/types.rb +2 -0
  9. data/lib/smart_core/types/errors.rb +16 -0
  10. data/lib/smart_core/types/primitive.rb +112 -20
  11. data/lib/smart_core/types/primitive/caster.rb +5 -2
  12. data/lib/smart_core/types/primitive/checker.rb +5 -2
  13. data/lib/smart_core/types/primitive/factory.rb +106 -8
  14. data/lib/smart_core/types/primitive/factory/definition_context.rb +142 -6
  15. data/lib/smart_core/types/primitive/factory/runtime_type_builder.rb +53 -0
  16. data/lib/smart_core/types/primitive/invariant_control.rb +67 -0
  17. data/lib/smart_core/types/primitive/invariant_control/chain.rb +61 -0
  18. data/lib/smart_core/types/primitive/invariant_control/chain/result.rb +64 -0
  19. data/lib/smart_core/types/primitive/invariant_control/factory.rb +54 -0
  20. data/lib/smart_core/types/primitive/invariant_control/factory/chain_definition_context.rb +39 -0
  21. data/lib/smart_core/types/primitive/invariant_control/result.rb +104 -0
  22. data/lib/smart_core/types/primitive/invariant_control/single.rb +57 -0
  23. data/lib/smart_core/types/primitive/invariant_control/single/result.rb +63 -0
  24. data/lib/smart_core/types/primitive/mult_factory.rb +59 -10
  25. data/lib/smart_core/types/primitive/mult_factory/definition_context.rb +27 -3
  26. data/lib/smart_core/types/primitive/mult_validator.rb +42 -0
  27. data/lib/smart_core/types/primitive/mult_validator/result.rb +8 -0
  28. data/lib/smart_core/types/primitive/nilable_factory.rb +31 -9
  29. data/lib/smart_core/types/primitive/nilable_validator.rb +83 -0
  30. data/lib/smart_core/types/primitive/nilable_validator/result.rb +78 -0
  31. data/lib/smart_core/types/primitive/runtime_attributes_checker.rb +77 -0
  32. data/lib/smart_core/types/primitive/sum_factory.rb +59 -10
  33. data/lib/smart_core/types/primitive/sum_factory/definition_context.rb +26 -2
  34. data/lib/smart_core/types/primitive/sum_validator.rb +117 -0
  35. data/lib/smart_core/types/primitive/sum_validator/result.rb +100 -0
  36. data/lib/smart_core/types/primitive/undefined_caster.rb +7 -5
  37. data/lib/smart_core/types/primitive/validator.rb +93 -0
  38. data/lib/smart_core/types/primitive/validator/result.rb +78 -0
  39. data/lib/smart_core/types/protocol.rb +7 -0
  40. data/lib/smart_core/types/protocol/instance_of.rb +19 -0
  41. data/lib/smart_core/types/system.rb +21 -5
  42. data/lib/smart_core/types/value.rb +16 -0
  43. data/lib/smart_core/types/value/array.rb +3 -0
  44. data/lib/smart_core/types/value/big_decimal.rb +31 -0
  45. data/lib/smart_core/types/value/boolean.rb +3 -0
  46. data/lib/smart_core/types/value/class.rb +3 -0
  47. data/lib/smart_core/types/value/comparable.rb +13 -0
  48. data/lib/smart_core/types/value/date.rb +24 -0
  49. data/lib/smart_core/types/value/date_time.rb +24 -0
  50. data/lib/smart_core/types/value/enumerable.rb +13 -0
  51. data/lib/smart_core/types/value/enumerator.rb +13 -0
  52. data/lib/smart_core/types/value/enumerator_chain.rb +13 -0
  53. data/lib/smart_core/types/value/float.rb +9 -2
  54. data/lib/smart_core/types/value/hash.rb +11 -1
  55. data/lib/smart_core/types/value/integer.rb +13 -3
  56. data/lib/smart_core/types/value/io.rb +13 -0
  57. data/lib/smart_core/types/value/method.rb +9 -0
  58. data/lib/smart_core/types/value/module.rb +3 -0
  59. data/lib/smart_core/types/value/nil.rb +3 -3
  60. data/lib/smart_core/types/value/numeric.rb +16 -3
  61. data/lib/smart_core/types/value/proc.rb +14 -1
  62. data/lib/smart_core/types/value/range.rb +9 -0
  63. data/lib/smart_core/types/value/rational.rb +13 -0
  64. data/lib/smart_core/types/value/set.rb +21 -0
  65. data/lib/smart_core/types/value/string.rb +10 -1
  66. data/lib/smart_core/types/value/string_io.rb +15 -0
  67. data/lib/smart_core/types/value/symbol.rb +10 -1
  68. data/lib/smart_core/types/value/text.rb +21 -4
  69. data/lib/smart_core/types/value/time.rb +24 -0
  70. data/lib/smart_core/types/value/time_based.rb +32 -0
  71. data/lib/smart_core/types/value/unbound_method.rb +9 -0
  72. data/lib/smart_core/types/variadic.rb +7 -0
  73. data/lib/smart_core/types/variadic/tuple.rb +23 -0
  74. data/lib/smart_core/types/version.rb +2 -2
  75. data/smart_types.gemspec +6 -5
  76. metadata +69 -23
  77. data/.travis.yml +0 -20
  78. data/lib/smart_core/types/primitive/mult_checker.rb +0 -31
  79. data/lib/smart_core/types/primitive/nilable_checker.rb +0 -37
  80. data/lib/smart_core/types/primitive/sum_checker.rb +0 -31
  81. data/lib/smart_core/types/system/definition_dsl.rb +0 -40
  82. data/lib/smart_core/types/system/producer_dsl.rb +0 -30
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 38c9d0410b4668ba347f5da8a6f76179406a96c53fa7af953cb21204f6c93a84
4
- data.tar.gz: ace2638b907add7049712113a67ad18ea277694c0335fceb3c0172d3056b6250
3
+ metadata.gz: fdcbf29c692c77b4968df1ad7cbc629164de818da1859fc871c776b8e94152b1
4
+ data.tar.gz: 3fe9ffe11f8d84b11a3b274a59c1d826a2d8a64eb9c289c1bf5166690d379f31
5
5
  SHA512:
6
- metadata.gz: c6019665e9da02262c097c9326f7e8e72ad698563dfe8e5bb2f45bf6cb0ea8f31ab3f31b3524328fd99f7405b8171bbd49178021a1377af822d3d78d54cfb5e0
7
- data.tar.gz: 28d7b45311fb04413264ec5ecd628989391b796008547a633297cf50e5e58c404c41248ef519d229ebfa5f3f204b2f99e06fa1dc35559ab6f501222d5fd0018a
6
+ metadata.gz: d8dd2d8a3e397c02caf8a6b32347105b2f2b860c3847a8f08d5713d5e1791845aa8a598a21b3dc8be63c526e63d323349e144366836518a74409c39904140eef
7
+ data.tar.gz: 5ead029c27b2b4aae1a6b509e0e268f92cf85fc34c207f5af76b4b79cdb72a494bb61d16a5e50d979c61a4d6b161983b45bd013207ca047f4b5852601c7bd088
@@ -5,7 +5,8 @@ inherit_gem:
5
5
  - lib/rubocop.rspec.yml
6
6
 
7
7
  AllCops:
8
- TargetRubyVersion: 2.4.9
8
+ TargetRubyVersion: 2.7.2
9
+ NewCops: enable
9
10
  Include:
10
11
  - lib/**/*.rb
11
12
  - spec/**/*.rb
@@ -13,3 +14,11 @@ AllCops:
13
14
  - Rakefile
14
15
  - smart_types.gemspec
15
16
  - bin/console
17
+
18
+ # NOTE: support for old ruby versions
19
+ Style/RedundantBegin:
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,44 @@
1
1
  # Changelog
2
2
  All notable changes to this project will be documented in this file.
3
+
4
+ ## [0.3.0] - 2020-12-22
5
+ ### Added
6
+ - Extended **Type Definition API**: support for **runtime attributes**:
7
+ - Type checkers, type casters and type invariants now receives runtime attributes (you can omit these);
8
+ - Type definitioning extended with `runtime_attribute_checker`-checker definition (runtime attributes validator);
9
+ - Types with incorrect runtime attributes will raise `SmartCore::Types::IncorrectRuntimeAttributesError` exception;
10
+ - Types which has no support for runtime attributes will raise `SmartCore::Types::RuntimeAttributesUnsupportedError` excpetion;
11
+ - All types by default has a method alias (`()`) which does not allow runtime attributes (for example: `SmartCore::Types::Value::String` has
12
+ a runtime-based alias `SmartCore::Types::Value::String()` which does not accept any attribute
13
+ (`SmartCore::Types::Value::String('test')` will raise `SmartCore::Types::RuntimeAttributesUnsupportedError` respectively));
14
+ - Extended Internal **Type Development API**:
15
+ - all types has a reference to it's type category;
16
+ - Brand new `SmartCore::Types::Protocol` type category and new types:
17
+ - `SmartCore::Types::Protocol::InstanceOf` (runtime-based type);
18
+ - Brand new `SmartCore::Types::Variadic` type category and new types:
19
+ - `SmartCore::Types::Variadic::Tuple` (runtime-based type);
20
+ - New types of `SmartCore::Types::Value` category:
21
+ - `SmartCore::Types::Value::Set` (based on `Set` type with a support for type casting);
22
+ - Support for BasicObject values inside type checkers that can not be checked correctly via `#is_a?/#kind_of?` before;
23
+
24
+ ### Changed
25
+ - Updated development dependencies;
26
+ - Drop `Travis CI` (TODO: migrate to `Github Actions`);
27
+ - **Ruby@2.4** is no longer supported;
28
+
29
+ ## [0.2.0] - 2020-11-21
30
+ ### Added
31
+ - Brand new **Type invariant API**:
32
+ - globally refactored validation logic (with backward compatability for `#valid?(value)` method);
33
+ - new type definition DSL: `.invariant(name)` and `.invariant_chain(name)`;
34
+ - chained invariants will be invoked according to the definition order (second invokation
35
+ depends on previous successful invariant check);
36
+ - new validation API: `validate(value)` (with `#errors` support based on invariant names);
37
+ - at this moment Invariant API is supported only by primitive types (type sum and type multiplication support coming soon);
38
+
39
+ ### Changed
40
+
41
+ - Updated `smart_engine` dependency (to `~> 0.7`) (need `SmartCore::Engine::Atom`);
42
+
43
+ ## [0.1.0] - 2020-05-05
44
+ - Release :)
@@ -1,77 +1,104 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- smart_types (0.1.0.alpha3)
5
- smart_engine (~> 0.5)
4
+ smart_types (0.3.0)
5
+ smart_engine (~> 0.10)
6
6
 
7
7
  GEM
8
8
  remote: https://rubygems.org/
9
9
  specs:
10
- armitage-rubocop (0.79.0)
11
- rubocop (= 0.79.0)
12
- rubocop-performance (= 1.5.2)
13
- rubocop-rails (= 2.4.1)
14
- rubocop-rake (= 0.5.0)
15
- rubocop-rspec (= 1.37.1)
16
- ast (2.4.0)
17
- diff-lcs (1.3)
18
- docile (1.3.2)
19
- jaro_winkler (1.5.4)
20
- json (2.3.0)
21
- parallel (1.19.1)
22
- parser (2.7.0.2)
23
- ast (~> 2.4.0)
24
- rack (2.1.1)
10
+ activesupport (6.1.0)
11
+ concurrent-ruby (~> 1.0, >= 1.0.2)
12
+ i18n (>= 1.6, < 2)
13
+ minitest (>= 5.1)
14
+ tzinfo (~> 2.0)
15
+ zeitwerk (~> 2.3)
16
+ armitage-rubocop (1.6.1)
17
+ rubocop (= 1.6.1)
18
+ rubocop-performance (= 1.9.1)
19
+ rubocop-rails (= 2.9.0)
20
+ rubocop-rake (= 0.5.1)
21
+ rubocop-rspec (= 2.0.1)
22
+ ast (2.4.1)
23
+ coderay (1.1.3)
24
+ concurrent-ruby (1.1.7)
25
+ diff-lcs (1.4.4)
26
+ docile (1.3.3)
27
+ i18n (1.8.5)
28
+ concurrent-ruby (~> 1.0)
29
+ method_source (1.0.0)
30
+ minitest (5.14.2)
31
+ parallel (1.20.1)
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)
25
38
  rainbow (3.0.0)
26
- rake (13.0.1)
27
- rspec (3.9.0)
28
- rspec-core (~> 3.9.0)
29
- rspec-expectations (~> 3.9.0)
30
- rspec-mocks (~> 3.9.0)
31
- rspec-core (3.9.1)
32
- rspec-support (~> 3.9.1)
33
- rspec-expectations (3.9.0)
39
+ rake (13.0.3)
40
+ regexp_parser (2.0.1)
41
+ rexml (3.2.4)
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)
34
49
  diff-lcs (>= 1.2.0, < 2.0)
35
- rspec-support (~> 3.9.0)
36
- rspec-mocks (3.9.1)
50
+ rspec-support (~> 3.10.0)
51
+ rspec-mocks (3.10.0)
37
52
  diff-lcs (>= 1.2.0, < 2.0)
38
- rspec-support (~> 3.9.0)
39
- rspec-support (3.9.2)
40
- rubocop (0.79.0)
41
- jaro_winkler (~> 1.5.1)
53
+ rspec-support (~> 3.10.0)
54
+ rspec-support (3.10.0)
55
+ rubocop (1.6.1)
42
56
  parallel (~> 1.10)
43
- parser (>= 2.7.0.1)
57
+ parser (>= 2.7.1.5)
44
58
  rainbow (>= 2.2.2, < 4.0)
59
+ regexp_parser (>= 1.8, < 3.0)
60
+ rexml
61
+ rubocop-ast (>= 1.2.0, < 2.0)
45
62
  ruby-progressbar (~> 1.7)
46
- unicode-display_width (>= 1.4.0, < 1.7)
47
- rubocop-performance (1.5.2)
48
- rubocop (>= 0.71.0)
49
- rubocop-rails (2.4.1)
63
+ unicode-display_width (>= 1.4.0, < 2.0)
64
+ rubocop-ast (1.3.0)
65
+ parser (>= 2.7.1.5)
66
+ rubocop-performance (1.9.1)
67
+ rubocop (>= 0.90.0, < 2.0)
68
+ rubocop-ast (>= 0.4.0)
69
+ rubocop-rails (2.9.0)
70
+ activesupport (>= 4.2.0)
50
71
  rack (>= 1.1)
51
- rubocop (>= 0.72.0)
52
- rubocop-rake (0.5.0)
72
+ rubocop (>= 0.90.0, < 2.0)
73
+ rubocop-rake (0.5.1)
53
74
  rubocop
54
- rubocop-rspec (1.37.1)
55
- rubocop (>= 0.68.1)
75
+ rubocop-rspec (2.0.1)
76
+ rubocop (~> 1.0)
77
+ rubocop-ast (>= 1.1.0)
56
78
  ruby-progressbar (1.10.1)
57
- simplecov (0.17.1)
79
+ simplecov (0.20.0)
58
80
  docile (~> 1.1)
59
- json (>= 1.8, < 3)
60
- simplecov-html (~> 0.10.0)
61
- simplecov-html (0.10.2)
62
- smart_engine (0.5.0)
63
- unicode-display_width (1.6.0)
81
+ simplecov-html (~> 0.11)
82
+ simplecov_json_formatter (~> 0.1)
83
+ simplecov-html (0.12.3)
84
+ simplecov_json_formatter (0.1.2)
85
+ smart_engine (0.10.0)
86
+ tzinfo (2.0.4)
87
+ concurrent-ruby (~> 1.0)
88
+ unicode-display_width (1.7.0)
89
+ zeitwerk (2.4.2)
64
90
 
65
91
  PLATFORMS
66
92
  ruby
67
93
 
68
94
  DEPENDENCIES
69
- armitage-rubocop (~> 0.78)
95
+ armitage-rubocop (~> 1.6)
70
96
  bundler (~> 2.1)
97
+ pry (~> 0.13)
71
98
  rake (~> 13.0)
72
- rspec (~> 3.9)
73
- simplecov (~> 0.17)
99
+ rspec (~> 3.10)
100
+ simplecov (~> 0.20)
74
101
  smart_types!
75
102
 
76
103
  BUNDLED WITH
77
- 2.1.2
104
+ 2.1.4
data/README.md CHANGED
@@ -1,13 +1,11 @@
1
- # SmartCore::Types &middot; [![Gem Version](https://badge.fury.io/rb/smart_types.svg)](https://badge.fury.io/rb/smart_types) [![Build Status](https://travis-ci.org/smart-rb/smart_types.svg?branch=master)](https://travis-ci.org/smart-rb/smart_types)
1
+ # SmartCore::Types &middot; [![Gem Version](https://badge.fury.io/rb/smart_types.svg)](https://badge.fury.io/rb/smart_types)
2
2
 
3
3
  > A set of objects that acts like types (type checking and type casting) with a support for basic type algebra.
4
4
 
5
- Full-featured type system for any ruby project. Supports custom type definitioning,
5
+ Minimalistic type system for any ruby project. Supports custom type definitioning,
6
6
  type validation, type casting and type categorizing. Provides a set of commonly used type
7
7
  categories and general purpose types. Has a flexible and simplest type definition toolchain.
8
8
 
9
- Just add and use :) Enjoy! :)
10
-
11
9
  ## Installation
12
10
 
13
11
  ```ruby
@@ -26,6 +24,25 @@ require 'smart_core/types'
26
24
 
27
25
  ---
28
26
 
27
+ ## Usage
28
+
29
+ - [Type interface](#type-interface)
30
+ - [Basic type algebra](#basic-type-algebra)
31
+ - [Supported types](#supported-types)
32
+ - [Nilable types](#nilable-types)
33
+ - [Custom type definition](#custom-type-definition)
34
+ - [Primitive type definition](#primitive-type-definition)
35
+ - [With type invariants](#with-type-invariants)
36
+ - [Type validation](#type-validation)
37
+ - [Type casting](#type-casting)
38
+ - [Roadmap](#roadmap)
39
+ - [Contributing](#contributing)
40
+ - [Build](#build)
41
+ - [License](#license)
42
+ - [Authors](#authors)
43
+
44
+ ---
45
+
29
46
  ## Type Interface
30
47
 
31
48
  ```ruby
@@ -39,70 +56,220 @@ type3 = type1 | type2
39
56
  type4 = type1 & type2
40
57
  ```
41
58
 
59
+ Types with runtime:
60
+
61
+ ```ruby
62
+ # get a type object with a custom runtime (instances of String or Symbol):
63
+ type = SmartCore::Types::Protocol::InstanceOf(::String, ::Symbol)
64
+ type.valid?(:test) # => true
65
+ type.valid?('test') # => true
66
+ type.valid?(123.456) # => false
67
+
68
+ # another type object with a custom runtime (tuple (String, Integer, Time)):
69
+ type = SmartCore::Types::Variadic::Tuple(::String, ::Integer, ::DateTime)
70
+ type.valid?(['test', 1, DateTime.new]) # => true
71
+ type.valid?([:test, 2]) # => false
72
+ ```
73
+
42
74
  ## Supported types
43
75
 
44
- - Primitive Value Types:
76
+ - Primitives
45
77
 
46
78
  ```ruby
47
79
  SmartCore::Types::Value::Any
80
+ SmartCore::Types::Value::Nil
48
81
  SmartCore::Types::Value::String
49
82
  SmartCore::Types::Value::Symbol
50
83
  SmartCore::Types::Value::Text
51
84
  SmartCore::Types::Value::Integer
52
85
  SmartCore::Types::Value::Float
53
86
  SmartCore::Types::Value::Numeric
87
+ SmartCore::Types::Value::BigDecimal
54
88
  SmartCore::Types::Value::Boolean
55
89
  SmartCore::Types::Value::Array
90
+ SmartCore::Types::Value::Set
56
91
  SmartCore::Types::Value::Hash
57
92
  SmartCore::Types::Value::Proc
58
93
  SmartCore::Types::Value::Class
59
94
  SmartCore::Types::Value::Module
95
+ SmartCore::Types::Value::Time
96
+ SmartCore::Types::Value::DateTime
97
+ SmartCore::Types::Value::Date
98
+ SmartCore::Types::Value::TimeBased
60
99
  ```
61
100
 
62
- ---
101
+ - Protocols:
63
102
 
64
- ## Nilable types
103
+ ```ruby
104
+ SmartCore::Types::Protocol::InstanceOf
105
+ ```
65
106
 
66
107
  ```ruby
67
- SmartCore::Types::Value::Any.nilable
68
- SmartCore::Types::Value::String.nilable
69
- SmartCore::Types::Value::Symbol.nilable
70
- SmartCore::Types::Value::Text.nilable
71
- SmartCore::Types::Value::Integer.nilable
72
- SmartCore::Types::Value::Float.nilable
73
- SmartCore::Types::Value::Numeric.nilable
74
- SmartCore::Types::Value::Boolean.nilable
75
- SmartCore::Types::Value::Array.nilable
76
- SmartCore::Types::Value::Hash.nilable
77
- SmartCore::Types::Value::Proc.nilable
78
- SmartCore::Types::Value::Class.nilable
79
- SmartCore::Types::Value::Module.nilable
108
+ # examples (SmartCore::Types::Protocol::InstanceOf):
109
+ SmartCore::Types::Protocol::InstanceOf(::Integer) # only integer
110
+ SmartCore::Types::Protocol::InstanceOf(::String, ::Symbol) # string or symbol
111
+ SmartCore::Types::Protocol::InstanceOf(::Time, ::DateTime, ::Date) # time or datetime or date
80
112
  ```
81
113
 
82
- ---
114
+ - Variadic:
83
115
 
84
- ## Constrained types
116
+ ```ruby
117
+ SmartCore::Types::Variadic::Tuple
118
+ ```
85
119
 
86
120
  ```ruby
87
- # documentation is coming
121
+ # examples (SmartCore::Types::Variadic::Tuple):
122
+ SmartCore::Types::Variadic::Tuple(::String, ::Integer, ::Time) # array with signature [<string>, <integer>, <time>]
123
+ SmartCore::Types::Variadic::Tuple(::Symbol, ::Float) # array with signature [<symbol>, <float>]
88
124
  ```
89
125
 
90
126
  ---
91
127
 
92
- ## Type validation and type casting
128
+ ## Nilable types
129
+
130
+ - invoke `.nilable` on any type object:
93
131
 
94
132
  ```ruby
95
- # documentation is coming
133
+ SmartCore::Types::Value::String.nilable
134
+ # -- or --
135
+ SmartCore::Types::Value::Time.nilable
136
+ # and etc.
96
137
  ```
97
138
 
98
139
  ---
99
140
 
100
141
  ## Custom type definition
101
142
 
143
+ Type definition is a composition of:
144
+
145
+ - type checker (required);
146
+ - type caster (optional);
147
+ - type invariants (optional);
148
+ - type invariant chains (optional);
149
+
150
+ Invariant is a custom validation block that will work as a logical value checker. You can have as much invariants as you want.
151
+
152
+ Type invariants does not depends on each other (invariant defined out from chain does not depends on other invariants);
153
+
154
+ Invariants inside invariant chains will be invoked in order they was defined and each internal invariant depends on the valid previous invairant check.
155
+
156
+ **!IMPORTANT!** Type sum and type multiplication does not support invariant checking and custom invariant definitioning at this moment.
157
+ Type sum and type mult ignores type invariants in their validation logic (currently this functionality in development yet).
158
+
159
+ Invariant checking is a special validation layer (see [#type validation](#type-validation) readme section). Invariant error code pattern:
160
+ - for invariant chains: `TypeName.invariant_chain_name.invariant_name`;
161
+ - for single invariant: `TypeName.invariant_name`;
162
+
163
+ #### Primitive type definition
164
+
102
165
  ```ruby
103
166
  # documentation is coming
104
167
 
105
168
  # example:
169
+ SmartCore::Types::Value.define_type(:String) do |type|
170
+ type.define_checker do |value, runtime_attrs| # runtime attributes are optional
171
+ value.is_a?(::String)
172
+ end
173
+
174
+ type.define_caster do |value, runtime_attrs| # runtime attributes are optional
175
+ value.to_s
176
+ end
177
+ end
178
+
179
+ # get a type object:
180
+ SmartCore::Types::Value::String
181
+ # --- or ---
182
+ SmartCore::Types::Value::String() # without runtime attributes
183
+ # --- or ---
184
+ SmartCore::Types::Value::String('some_attr', :another_attr) # with runtime attributes
185
+
186
+ # work with type object: see documentation below
187
+ ```
188
+
189
+ #### With type invariants
190
+
191
+ ```ruby
192
+ SmartCore::Types::Value.define_type(:String) do |type|
193
+ type.define_checker do |value, runtime_attrs|
194
+ value.is_a?(::String)
195
+ end
196
+
197
+ type.define_caster do |value, runtime_attrs|
198
+ value.to_s
199
+ end
200
+
201
+ # NOTE:
202
+ # invariant defined out from chain does not depends on other invariants
203
+ type.invariant(:uncensored_content) do |value, runtime_attrs|
204
+ !value.include?('uncensored_word')
205
+ end
206
+
207
+ type.invariant(:filled) do |value, runtime_attrs|
208
+ value != ''
209
+ end
210
+
211
+ type.invariant_chain(:password) do
212
+ invariant(:should_present) do |value, runtime_attrs|
213
+ value != ''
214
+ end
215
+
216
+ invariant(:should_have_numbers) do |value, runtime_attrs|
217
+ v.match?(/[0-9]+/)
218
+ end
219
+
220
+ # NOTE:
221
+ # inside a chain each next invariant invokation
222
+ # depends on previous successful invariant check
223
+ end
224
+ end
225
+ ```
226
+
227
+ ---
228
+
229
+ ## Type validation
230
+
231
+ Type validation reflects on two APIs:
232
+
233
+ - type checker ([how to define type checkers](#custom-type-definition));
234
+ - type invariants (invariants and invariant chains) ([how to define type invariants](#custom-type-definition));
235
+
236
+ Type invariants does not depends on each other (invariant defined out from the chain does not depends on other invariants);
237
+
238
+ Invariants inside invariant chains will be invoked in order they was defined and each internal invariant depends on the valid previous invairant check.
239
+
240
+ **!IMPORTANT!** Type sum and type multiplication does not support invariant checking and custom invariant definitioning at this moment.
241
+ Type sum and type mult ignores type invariants in their validation logic (currently this functionality in development yet).
242
+
243
+ Invariant checking is a special validation layer (see [#type validation](#type-validation) readme section) and represents a set of error codes in result object;
244
+
245
+ Type valdiation interface:
246
+
247
+ - `valid?(value)` - validates value and returns `true` or `false`;
248
+ - returns `ture` only if the type checker returns `true` and all invariants are valid;
249
+ - `validate(value)` - validates value and returns the monadic result object:
250
+ - `SmartCore::Types::Primitive::Validator::Result` for primitive types;
251
+ - `SmartCore::Types::Primitive::SumValidator::Result` for sum-based types;
252
+ - `SmartCore::Types::Primitive::MultValidator::Result` for mult-based types;
253
+ - `SmartCore::Types::Primitive::NilableValidator::Result` for nilable types;
254
+ - `validate!(value)` - validates value and returns nothing (for successful validation) or
255
+ raises an exception (`SmartCore::Types::TypeError`) (for unsuccessful validation);
256
+
257
+ Validation result object interface:
258
+
259
+ - `#success?` / `#failure?` (`#success?` is a combination of `valid_check? && valid_invariants?`; `#failure?` - is an opposite of `#success?`);
260
+ - `#valid_check?` (valid type checker or not);
261
+ - `#valid_invariants?` (`false` if at least one invariant is invalid);
262
+ - `#errors` (the same as `#invariant_errors` and the same as `#error_codes`) - an array of failed invariant names;
263
+ - error code patterns:
264
+ - for invariant chains: `TypeName.invariant_chain_name.invariant_name`;
265
+ - for single invariant: `TypeName.invariant_name`;
266
+ - `#checked_value` (the same as `#value`) - checked value :)
267
+
268
+ ---
269
+
270
+ Imagine that we have `String` type like this:
271
+
272
+ ```ruby
106
273
  SmartCore::Types::Value.define_type(:String) do |type|
107
274
  type.define_checker do |value|
108
275
  value.is_a?(::String)
@@ -111,27 +278,238 @@ SmartCore::Types::Value.define_type(:String) do |type|
111
278
  type.define_caster do |value|
112
279
  value.to_s
113
280
  end
281
+
282
+ type.invariant(:uncensored_content) do |value|
283
+ !value.include?('uncensored_word')
284
+ end
285
+
286
+ type.invariant(:filled) do |value|
287
+ value != ''
288
+ end
289
+
290
+ type.invariant_chain(:password) do
291
+ invariant(:should_present) { |value| value != '' }
292
+ invariant(:should_have_numbers) { |value| v.match?(/[0-9]+/) }
293
+ end
114
294
  end
115
295
  ```
116
296
 
297
+ Validation interface and usage:
298
+
299
+ ```ruby
300
+ SmartCore::Types::Value::String.valid?('test123') # => true
301
+ SmartCore::Types::Value::String.valid?(123.45) # => false
302
+ ```
303
+
304
+ ```ruby
305
+ result = SmartCore::Types::Value::String.validate('test')
306
+
307
+ result.checked_value # => 'test'
308
+ # --- same as: ---
309
+ result.value
310
+
311
+ result.success? # => false (valid_check? && valid_invariants?)
312
+ result.failure? # => true
313
+
314
+ result.valid_check? # => true
315
+ result.valid_invariants? # => false
316
+
317
+ # invariant errors:
318
+ result.errors # => ['String.password.should_have_numbers']
319
+ # -- same as: ---
320
+ result.invariant_errors
321
+ # -- same as: ---
322
+ result.error_codes
323
+ ```
324
+
325
+ ```ruby
326
+ result = SmartCore::Types::Value::String.validate('test1234')
327
+ result.success? # => true
328
+ result.errors # => []
329
+ ```
330
+
331
+ ```ruby
332
+ SmartCore::Types::Value::String.validate!('test') # => SmartCore::Types::TypeError
333
+ ```
334
+
117
335
  ---
118
336
 
119
- ## Basic type algebra
337
+ ## Type casting
120
338
 
121
339
  ```ruby
122
- # documentation is coming
340
+ SmartCore::Types::Value::String.cast(123) # => "123"
341
+ SmartCore::Types::Value::Float.cast('55') # => 55.0
123
342
  ```
124
343
 
125
344
  ---
126
345
 
127
- ## Type system integration
346
+ ## Basic type algebra
347
+
348
+ > (type sum and type multiplication does not support invariants at this moment (in development yet));
128
349
 
129
350
  ```ruby
130
351
  # documentation is coming
352
+
353
+ # how to define primitive type sum:
354
+ SmartCore::Types::Value::Text = SmartCore::Types::Value::String | SmartCore::Types::Value::Symbol
355
+ SmartCore::Types::Value::Numeric = SmartCore::Types::Value::Float | SmartCore::Types::Value::Integer
356
+
357
+ # how to define primitive type multiplication:
358
+ SmartCore::Types::Value::CryptoString = SmartCore::Types::Value::NumberdString & SmartCore::Types::Value::SymbolicString
131
359
  ```
132
360
 
133
361
  ---
134
362
 
363
+ ## Roadmap
364
+
365
+ - migrate to `Github Actions`;
366
+
367
+ - support for `block`-attribute in runtime attributes;
368
+
369
+ - type configuration:
370
+
371
+ ```ruby
372
+ SmartCore::Types::Value.type(:Time) do |type|
373
+ type.configuration do |config| # config definition
374
+ setting :iso, :rfc2822
375
+ # TODO: think about a more convinient DSL
376
+ end
377
+
378
+ type.define_caster do |value, config| # config usage
379
+ case config.standard
380
+ when :rfc2822
381
+ ::Time.rfc2822(value)
382
+ else
383
+ # ...
384
+ end
385
+ end
386
+ end
387
+ ```
388
+
389
+ - pipelined type caster definition for the sum-based types:
390
+
391
+ ```ruby
392
+ SmartCore::Types::Value::TimeLike = SmartCore::Types::System.type_sum(
393
+ SmartCore::Types::Time,
394
+ SmartCore::Types::DateTime,
395
+ SmartCore::Types::Date,
396
+ ) do |type|
397
+ type.define_caster(:pipelined) # try Time.cast => try DateTime.cast => try Date.cast
398
+ end
399
+ ```
400
+
401
+ - namespaced type errors:
402
+
403
+ ```ruby
404
+ # before:
405
+ SmartCore::Types::Value::Boolean.validate!(123)
406
+ # => SmartCore::Types::TypeError
407
+ SmartCore::Types::Value::Class.cast(123)
408
+ # => SmartCore::Types::TypeCastingError
409
+
410
+ # after:
411
+ SmartCore::Types::Value::Boolean.validate!(123)
412
+ # => SmartCore::Types::Value::Boolean::TypeError
413
+ # (inheritance tree: Types::Value::<Type>::TypeError => Types::Value::TypeError => Types::TypeError)
414
+
415
+ SmartCore::Types::Value::Class.cast(123)
416
+ # => SmartCore::Types::Value::Class::TypeCastingError
417
+ # (inheritance tree: the same as above)
418
+ ```
419
+
420
+ - type refinements:
421
+
422
+ ```ruby
423
+ SmartCore::Types::Value::Time.refine_checker do |value, original_checker|
424
+ # new type checker
425
+ end
426
+
427
+ SmartCore::Types::Value::Time.refine_caster do |value, original_caster|
428
+ # new type caster
429
+ end
430
+
431
+ SmartCore::Types::Value::Time.refine_runtime_attributes_checker do |value, original_checker|
432
+ # new runtime attribute checker
433
+ end
434
+
435
+ SmartCore::Types::Value::Time.refine_invariant(:name) do |value|
436
+ # new invariant
437
+ end
438
+
439
+ SmartCore::Types::Value::Time.refine_invariant_chain(:chain_name) do
440
+ # new invariant chain
441
+ end
442
+ ```
443
+
444
+ - options for type casters:
445
+
446
+ ```ruby
447
+ SmartCore::Types::Value.define_type(:Date) do |type|
448
+ type.define_caster do |value, options = {}| # options goes here
449
+ iso = options.fetch(:iso, nil)
450
+ iso ? ::Date.pasre(value, iso) : ::Date.parse(value)
451
+ end
452
+ end
453
+
454
+ # usage:
455
+ SmartCore::Types::Value::Date.cast('2020-01-01', { iso: :rfc3339 })
456
+ ```
457
+
458
+ - new types:
459
+
460
+ ```ruby
461
+ SmartCore::Types::Value::Method
462
+ SmartCore::Types::Value::UnboundMethod
463
+ SmartCore::Types::Value::Enumerable
464
+ SmartCore::Types::Value::Comparable
465
+ SmartCore::Types::Value::Enumerator
466
+ SmartCore::Types::Value::EnumeratorChain
467
+ SmartCore::Types::Value::Range
468
+ SmartCore::Types::Value::Rational
469
+ SmartCore::Types::Value::SortedSet
470
+ SmartCore::Types::Value::IO
471
+ SmartCore::Types::Value::StringIO
472
+ SmartCore::Types::Value::BasicObject
473
+ SmartCore::Types::Struct::Schema
474
+ SmartCore::Types::Struct::JSONSchema
475
+ SmartCore::Types::Struct::StrictArray
476
+ SmartCore::Types::Struct::StrictHash
477
+ SmartCore::Types::Struct::Map
478
+ SmartCore::Types::Variadic::Enum
479
+ SmartCore::Types::Protocol::Interface
480
+ SmartCore::Types::Protocol::Ancestors
481
+ SmartCore::Types::Protocol::Enumerable
482
+ SmartCore::Types::Protocol::Comparable
483
+ SmartCore::Types::Protocol::Forwardable
484
+ SmartCore::Types::Protocol::Callable
485
+ ```
486
+
487
+ - `#sum` alias for `|` and `#mult` alias for `&` (with a support for type name definition and other API);
488
+
489
+ - type category in invariant error codes:
490
+ ```ruby
491
+ # before:
492
+ 'String.password.should_contain_numbers' # `String` type from `Value` category
493
+
494
+ # after:
495
+ 'Value.String.password.should_contain_numbers' # `Value::String`
496
+ ```
497
+
498
+ - support for type of empty non-defined type (`SmartCore::Types::Primitive::Undefined`);
499
+ - constrained types;
500
+ - moudle-based type system integration;
501
+ - constructor implementation and support;
502
+ - support for invariant checking (and custom definitioning) in sum-types;
503
+ - to provide a type comparability and compatability between all passed types
504
+ you should provide `type.reconcilable { |value, *types| .... }` setting;
505
+ - `type.reconcilable` should be accesible for type sum and type mult definitions;
506
+ - (**preliminarily**) invariants of the concrete passed type should be valid for sucessful invariant check;
507
+ - support for invariant checking (and definitioning) in mult-types;
508
+ - to provide a type comparability and compatability between all passed types
509
+ you should provide `type.reconcilable { |value, *types| .... }` setting;
510
+ - `type.reconcilable` should be accesible for type sum and type mult definitions;
511
+ - (**preliminarily**) all invariants of all types should be valid for sucessful invariant check;
512
+
135
513
  ## Contributing
136
514
 
137
515
  - Fork it ( https://github.com/smart-rb/smart_types )
@@ -140,6 +518,24 @@ end
140
518
  - Push to the branch (`git push origin feature/my-new-feature`)
141
519
  - Create new Pull Request
142
520
 
521
+ ## Build
522
+
523
+ - run tests:
524
+
525
+ ```shell
526
+ bundle exec rake rspec
527
+ # --- or ---
528
+ bundle exec rspec
529
+ ```
530
+
531
+ - run code stye linting:
532
+
533
+ ```shell
534
+ bundle exec rake rubocop
535
+ # --- or ---
536
+ bundle exec rubocop
537
+ ```
538
+
143
539
  ## License
144
540
 
145
541
  Released under MIT License.