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