smart_types 0.1.0.alpha5 → 0.4.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 +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.