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.
- 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 · [![Gem Version](https://badge.fury.io/rb/smart_types.svg)](https://badge.fury.io/rb/smart_types)
|
1
|
+
# SmartCore::Types · [![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
|
-
|
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.
|