smart_types 0.1.0.alpha4 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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.