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.
- checksums.yaml +4 -4
- data/.rubocop.yml +10 -1
- data/CHANGELOG.md +46 -0
- data/Gemfile.lock +80 -52
- data/README.md +437 -27
- data/Rakefile +1 -1
- data/bin/console +2 -2
- data/lib/smart_core/types.rb +2 -0
- data/lib/smart_core/types/errors.rb +16 -0
- data/lib/smart_core/types/primitive.rb +109 -24
- data/lib/smart_core/types/primitive/caster.rb +5 -2
- data/lib/smart_core/types/primitive/checker.rb +5 -2
- data/lib/smart_core/types/primitive/factory.rb +105 -8
- data/lib/smart_core/types/primitive/factory/definition_context.rb +142 -6
- data/lib/smart_core/types/primitive/factory/runtime_type_builder.rb +53 -0
- data/lib/smart_core/types/primitive/invariant_control.rb +67 -0
- data/lib/smart_core/types/primitive/invariant_control/chain.rb +61 -0
- data/lib/smart_core/types/primitive/invariant_control/chain/result.rb +64 -0
- data/lib/smart_core/types/primitive/invariant_control/factory.rb +54 -0
- data/lib/smart_core/types/primitive/invariant_control/factory/chain_definition_context.rb +39 -0
- data/lib/smart_core/types/primitive/invariant_control/result.rb +104 -0
- data/lib/smart_core/types/primitive/invariant_control/single.rb +57 -0
- data/lib/smart_core/types/primitive/invariant_control/single/result.rb +63 -0
- data/lib/smart_core/types/primitive/mult_factory.rb +59 -10
- data/lib/smart_core/types/primitive/mult_factory/definition_context.rb +27 -3
- data/lib/smart_core/types/primitive/mult_validator.rb +42 -0
- data/lib/smart_core/types/primitive/mult_validator/result.rb +8 -0
- data/lib/smart_core/types/primitive/nilable_factory.rb +31 -9
- data/lib/smart_core/types/primitive/nilable_validator.rb +83 -0
- data/lib/smart_core/types/primitive/nilable_validator/result.rb +78 -0
- data/lib/smart_core/types/primitive/runtime_attributes_checker.rb +77 -0
- data/lib/smart_core/types/primitive/sum_factory.rb +59 -10
- data/lib/smart_core/types/primitive/sum_factory/definition_context.rb +26 -2
- data/lib/smart_core/types/primitive/sum_validator.rb +117 -0
- data/lib/smart_core/types/primitive/sum_validator/result.rb +100 -0
- data/lib/smart_core/types/primitive/undefined_caster.rb +7 -5
- data/lib/smart_core/types/primitive/validator.rb +93 -0
- data/lib/smart_core/types/primitive/validator/result.rb +78 -0
- data/lib/smart_core/types/protocol.rb +7 -0
- data/lib/smart_core/types/protocol/instance_of.rb +19 -0
- data/lib/smart_core/types/system.rb +21 -5
- data/lib/smart_core/types/value.rb +16 -0
- data/lib/smart_core/types/value/array.rb +3 -0
- data/lib/smart_core/types/value/big_decimal.rb +31 -0
- data/lib/smart_core/types/value/boolean.rb +3 -0
- data/lib/smart_core/types/value/class.rb +3 -0
- data/lib/smart_core/types/value/comparable.rb +13 -0
- data/lib/smart_core/types/value/date.rb +24 -0
- data/lib/smart_core/types/value/date_time.rb +24 -0
- data/lib/smart_core/types/value/enumerable.rb +13 -0
- data/lib/smart_core/types/value/enumerator.rb +13 -0
- data/lib/smart_core/types/value/enumerator_chain.rb +13 -0
- data/lib/smart_core/types/value/float.rb +9 -2
- data/lib/smart_core/types/value/hash.rb +11 -1
- data/lib/smart_core/types/value/integer.rb +13 -3
- data/lib/smart_core/types/value/io.rb +13 -0
- data/lib/smart_core/types/value/method.rb +9 -0
- data/lib/smart_core/types/value/module.rb +3 -0
- data/lib/smart_core/types/value/nil.rb +3 -3
- data/lib/smart_core/types/value/numeric.rb +16 -3
- data/lib/smart_core/types/value/proc.rb +14 -1
- data/lib/smart_core/types/value/range.rb +9 -0
- data/lib/smart_core/types/value/rational.rb +13 -0
- data/lib/smart_core/types/value/set.rb +21 -0
- data/lib/smart_core/types/value/string.rb +10 -1
- data/lib/smart_core/types/value/string_io.rb +15 -0
- data/lib/smart_core/types/value/symbol.rb +10 -1
- data/lib/smart_core/types/value/text.rb +21 -4
- data/lib/smart_core/types/value/time.rb +24 -0
- data/lib/smart_core/types/value/time_based.rb +32 -0
- data/lib/smart_core/types/value/unbound_method.rb +9 -0
- data/lib/smart_core/types/variadic.rb +7 -0
- data/lib/smart_core/types/variadic/tuple.rb +23 -0
- data/lib/smart_core/types/version.rb +2 -2
- data/smart_types.gemspec +6 -5
- metadata +69 -23
- data/.travis.yml +0 -20
- data/lib/smart_core/types/primitive/mult_checker.rb +0 -31
- data/lib/smart_core/types/primitive/nilable_checker.rb +0 -37
- data/lib/smart_core/types/primitive/sum_checker.rb +0 -31
- data/lib/smart_core/types/system/definition_dsl.rb +0 -40
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a77f7989b137f4e3e7c319ad0f825f4484329ec4ff87ad04d31b9decae9bdce7
|
4
|
+
data.tar.gz: 7e05552fb246042a12a708089065f0916eac5565bcc4de61e4bbea5cb4d76c21
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 800dc2c99f6eeee87f522576483cb7583e8f63a21a98aa5ed6b7106499406336d31b4414a4146a33bd3be251268cf3f916236662398199c2e17ba5c11c4ab0d6
|
7
|
+
data.tar.gz: b771a4a428498a0bad62fc8f2cf67728117f3d47822da01086e70f386f822c4e001bb73ad609bc9ef049d06743ee3dfe13744db4d13964d4def5b058a323ad66
|
data/.rubocop.yml
CHANGED
@@ -5,7 +5,8 @@ inherit_gem:
|
|
5
5
|
- lib/rubocop.rspec.yml
|
6
6
|
|
7
7
|
AllCops:
|
8
|
-
TargetRubyVersion:
|
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
|
data/CHANGELOG.md
CHANGED
@@ -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 :)
|
data/Gemfile.lock
CHANGED
@@ -1,77 +1,105 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
smart_types (0.
|
5
|
-
smart_engine (~> 0.
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
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.
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
rspec-
|
31
|
-
|
32
|
-
rspec-
|
33
|
-
rspec-
|
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.
|
36
|
-
rspec-mocks (3.
|
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.
|
39
|
-
rspec-support (3.
|
40
|
-
rubocop (
|
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.
|
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, <
|
47
|
-
rubocop-
|
48
|
-
|
49
|
-
rubocop-
|
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.
|
52
|
-
rubocop-rake (0.5.
|
72
|
+
rubocop (>= 0.90.0, < 2.0)
|
73
|
+
rubocop-rake (0.5.1)
|
53
74
|
rubocop
|
54
|
-
rubocop-rspec (1.
|
55
|
-
rubocop (
|
56
|
-
|
57
|
-
|
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
|
-
|
60
|
-
|
61
|
-
simplecov-html (0.
|
62
|
-
|
63
|
-
|
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
|
-
|
92
|
+
x86_64-darwin-19
|
93
|
+
x86_64-darwin-20
|
67
94
|
|
68
95
|
DEPENDENCIES
|
69
|
-
armitage-rubocop (~>
|
96
|
+
armitage-rubocop (~> 1.7)
|
70
97
|
bundler (~> 2.1)
|
98
|
+
pry (~> 0.13)
|
71
99
|
rake (~> 13.0)
|
72
|
-
rspec (~> 3.
|
73
|
-
simplecov (~> 0.
|
100
|
+
rspec (~> 3.10)
|
101
|
+
simplecov (~> 0.21)
|
74
102
|
smart_types!
|
75
103
|
|
76
104
|
BUNDLED WITH
|
77
|
-
2.
|
105
|
+
2.2.3
|
data/README.md
CHANGED
@@ -1,13 +1,11 @@
|
|
1
|
-
# SmartCore::Types · [](https://badge.fury.io/rb/smart_types)
|
1
|
+
# SmartCore::Types · [](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
|
-
|
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
|
-
##
|
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
|
-
|
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
|
-
|
105
|
+
#### Protocols:
|
65
106
|
|
66
107
|
```ruby
|
67
|
-
SmartCore::Types::
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
SmartCore::Types::
|
72
|
-
SmartCore::Types::
|
73
|
-
SmartCore::Types::
|
74
|
-
SmartCore::Types::
|
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
|
-
|
120
|
+
#### Variadic:
|
85
121
|
|
86
122
|
```ruby
|
87
|
-
|
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
|
-
##
|
134
|
+
## Nilable types
|
135
|
+
|
136
|
+
- invoke `.nilable` on any type object:
|
93
137
|
|
94
138
|
```ruby
|
95
|
-
|
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
|
-
##
|
343
|
+
## Type casting
|
120
344
|
|
121
345
|
```ruby
|
122
|
-
#
|
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
|
-
##
|
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.
|