kind 5.2.0 → 5.6.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e522116072626ac76a87df9351e292f31a0f96cefa1bbc07d9e83c50198a25a8
4
- data.tar.gz: 529cd2b05c6767c2553ce9fa1e157cef564e7fc0092874839c6714e0df797240
3
+ metadata.gz: b72fec7c07a6b3178717f30c81ecdbb6375cc1fc625c1a3fa1d39611e4dc3960
4
+ data.tar.gz: efec52f15083b538c99546f8d5e1c6b095c6c5c243379fc21365bae99a176e60
5
5
  SHA512:
6
- metadata.gz: 206cacdc59d7288b4db022dac5c45fe95649845bb0093fbee14eb86444b145651407847b380b831c8267190f1d515b7c775810549c6ee73df61059f1d484129a
7
- data.tar.gz: ff97ce3f297254fd3932b853588c6d1f47a931c16065b5961e06fc4fe219ec6672db0fe16e1d0ffef4b34d25c6e9c35cc28c655b3cc539b519e916f60162ba04
6
+ metadata.gz: fad78e10047e8fdd21e4c56ddcc5c8620b1ad6018f3fdfb7af097b84be23bb73c3c549cd519bec3af7980c4df89fbc5b9db2796510c518a4ad9d9b9165ef6e02
7
+ data.tar.gz: 3d0fa5b46405e4a766285c6e3fd95e3568c5bb1875e310941a3dd592cd99372893c7aab6cfe91a321fc6a843c966ab5fb7414e726e903021b16b92dfc03bac1e
data/.travis.sh CHANGED
@@ -21,6 +21,8 @@ function run_basic_tests {
21
21
  eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/{functional/*_test,functional_test}.rb'"
22
22
  eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/either/*_test.rb'"
23
23
  eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/result/*_test.rb'"
24
+
25
+ eval "KIND_STRICT=t $bundle_cmd exec rake test TEST='test/kind/strict_disabled_test.rb'"
24
26
  }
25
27
 
26
28
  function run_with_bundler {
data/CHANGELOG.md CHANGED
@@ -3,73 +3,83 @@
3
3
  This project follows [semver 2.0.0](http://semver.org/spec/v2.0.0.html) and the recommendations of [keepachangelog.com](http://keepachangelog.com/).
4
4
 
5
5
  - [Unreleased](#unreleased)
6
- - [5.2.0 (2021-03-17)](#520-2021-03-17)
6
+ - [5.6.0 (2021-05-14)](#560-2021-05-14)
7
7
  - [Added](#added)
8
+ - [5.5.0 (2021-04-05)](#550-2021-04-05)
9
+ - [Added](#added-1)
10
+ - [5.4.1 (2021-03-26)](#541-2021-03-26)
11
+ - [Fixed](#fixed)
12
+ - [5.4.0 (2021-03-25)](#540-2021-03-25)
13
+ - [Added](#added-2)
14
+ - [5.3.0 (2021-03-23)](#530-2021-03-23)
15
+ - [Added](#added-3)
16
+ - [5.2.0 (2021-03-17)](#520-2021-03-17)
17
+ - [Added](#added-4)
8
18
  - [Deprecated](#deprecated)
9
19
  - [Changes](#changes)
10
20
  - [5.1.0 (2021-02-23)](#510-2021-02-23)
11
- - [Added](#added-1)
21
+ - [Added](#added-5)
12
22
  - [Deprecated](#deprecated-1)
13
23
  - [5.0.0 (2021-02-22)](#500-2021-02-22)
14
24
  - [Breaking Changes](#breaking-changes)
15
25
  - [Removed](#removed)
16
26
  - [4.1.0 (2021-02-22)](#410-2021-02-22)
17
- - [Added](#added-2)
27
+ - [Added](#added-6)
18
28
  - [4.0.0 (2021-02-22)](#400-2021-02-22)
19
- - [Added](#added-3)
29
+ - [Added](#added-7)
20
30
  - [Deprecated](#deprecated-2)
21
- - [Fixed](#fixed)
31
+ - [Fixed](#fixed-1)
22
32
  - [3.1.0 (2020-07-08)](#310-2020-07-08)
23
- - [Added](#added-4)
33
+ - [Added](#added-8)
24
34
  - [3.0.0 (2020-06-25)](#300-2020-06-25)
25
35
  - [Breaking Changes](#breaking-changes-1)
26
- - [Added](#added-5)
36
+ - [Added](#added-9)
27
37
  - [2.3.0 (2020-06-24)](#230-2020-06-24)
28
- - [Added](#added-6)
38
+ - [Added](#added-10)
29
39
  - [2.2.0 (2020-06-23)](#220-2020-06-23)
30
- - [Added](#added-7)
40
+ - [Added](#added-11)
31
41
  - [2.1.0 (2020-05-12)](#210-2020-05-12)
32
- - [Added](#added-8)
42
+ - [Added](#added-12)
33
43
  - [Breaking Changes](#breaking-changes-2)
34
44
  - [2.0.0 (2020-05-07)](#200-2020-05-07)
35
- - [Added](#added-9)
45
+ - [Added](#added-13)
36
46
  - [Breaking Changes](#breaking-changes-3)
37
47
  - [Removed](#removed-1)
38
48
  - [1.9.0 (2020-05-06)](#190-2020-05-06)
39
- - [Added](#added-10)
49
+ - [Added](#added-14)
40
50
  - [1.8.0 (2020-05-03)](#180-2020-05-03)
41
- - [Added](#added-11)
51
+ - [Added](#added-15)
42
52
  - [1.7.0 (2020-05-03)](#170-2020-05-03)
43
- - [Fixed](#fixed-1)
53
+ - [Fixed](#fixed-2)
44
54
  - [1.6.0 (2020-04-17)](#160-2020-04-17)
45
- - [Added](#added-12)
55
+ - [Added](#added-16)
46
56
  - [Changes](#changes-1)
47
57
  - [1.5.0 (2020-04-12)](#150-2020-04-12)
48
- - [Added](#added-13)
58
+ - [Added](#added-17)
49
59
  - [1.4.0 (2020-04-12)](#140-2020-04-12)
50
- - [Added](#added-14)
60
+ - [Added](#added-18)
51
61
  - [1.3.0 (2020-04-12)](#130-2020-04-12)
52
- - [Added](#added-15)
62
+ - [Added](#added-19)
53
63
  - [1.2.0 (2020-04-12)](#120-2020-04-12)
54
- - [Added](#added-16)
64
+ - [Added](#added-20)
55
65
  - [1.1.0 (2020-04-09)](#110-2020-04-09)
56
- - [Added](#added-17)
57
- - [Fixed](#fixed-2)
66
+ - [Added](#added-21)
67
+ - [Fixed](#fixed-3)
58
68
  - [1.0.0 (2020-03-16)](#100-2020-03-16)
59
- - [Added](#added-18)
69
+ - [Added](#added-22)
60
70
  - [0.6.0 (2020-01-06)](#060-2020-01-06)
61
- - [Added](#added-19)
71
+ - [Added](#added-23)
62
72
  - [0.5.0 (2020-01-04)](#050-2020-01-04)
63
- - [Added](#added-20)
73
+ - [Added](#added-24)
64
74
  - [0.4.0 (2020-01-03)](#040-2020-01-03)
65
- - [Added](#added-21)
75
+ - [Added](#added-25)
66
76
  - [0.3.0 (2020-01-03)](#030-2020-01-03)
67
- - [Added](#added-22)
77
+ - [Added](#added-26)
68
78
  - [Breaking Changes](#breaking-changes-4)
69
79
  - [0.2.0 (2020-01-02)](#020-2020-01-02)
70
- - [Added](#added-23)
80
+ - [Added](#added-27)
71
81
  - [0.1.0 (2019-12-26)](#010-2019-12-26)
72
- - [Added](#added-24)
82
+ - [Added](#added-28)
73
83
 
74
84
  ## Unreleased
75
85
 
@@ -81,6 +91,162 @@ This project follows [semver 2.0.0](http://semver.org/spec/v2.0.0.html) and the
81
91
  ### Fixed
82
92
  -->
83
93
 
94
+ 5.6.0 (2021-05-14)
95
+ ------------------
96
+
97
+ ### Added
98
+
99
+ * [#57](https://github.com/serradura/kind/pull/57) - Allow the usage of `nil` to define union types.
100
+ ```ruby
101
+ (Kind::String | nil) === '' # true
102
+ (Kind::String | nil) === nil # true
103
+
104
+ (Kind::String | nil) === {} # false
105
+ ```
106
+
107
+ [⬆️  Back to Top](#changelog-)
108
+
109
+ 5.5.0 (2021-04-05)
110
+ ------------------
111
+
112
+ ### Added
113
+
114
+ * [#56](https://github.com/serradura/kind/pull/56) - Add `Kind.or_nil()`.
115
+ ```ruby
116
+ Kind.or_nil(String, 1) # nil
117
+
118
+ Kind.or_nil(String, '') # ""
119
+
120
+ # --
121
+
122
+ filled_string = ->(value) { value.is_a?(String) && !value.empty? }
123
+
124
+ Kind.or_nil(filled_string, 1) # nil
125
+ Kind.or_nil(filled_string, '') # nil
126
+
127
+ Kind.or_nil(filled_string, '1') # "1"
128
+ ```
129
+
130
+ [⬆️  Back to Top](#changelog-)
131
+
132
+ 5.4.1 (2021-03-26)
133
+ ------------------
134
+
135
+ ### Fixed
136
+
137
+ * [#55](https://github.com/serradura/kind/pull/55) - Fix `Kind::Either::Left#value_or` and `Kind::Result::Failure#value_or` by allowing them to receive the value of the monad in a call using a block.
138
+
139
+ [⬆️  Back to Top](#changelog-)
140
+
141
+ 5.4.0 (2021-03-25)
142
+ ------------------
143
+
144
+ ### Added
145
+
146
+ * [#54](https://github.com/serradura/kind/pull/54) - Add `Kind::Functional::Steps` to allow the usage of `Step`, `Map`, `Try`, `Tee`, `Check`, `Success` and `Failure` in any kind of object.
147
+ ```ruby
148
+ # Usage in classes' instances
149
+
150
+ class BaseJob
151
+ def self.perform_now(input); new.perform(input); end
152
+
153
+ def perform(input); raise NotImplementedError; end
154
+ end
155
+
156
+ class CreateUserJob < BaseJob
157
+ include Kind::Functional::Steps
158
+
159
+ def perform(input)
160
+ validate(input) \
161
+ >> Step(:create) \
162
+ >> Step(:welcome_email)
163
+ end
164
+
165
+ private
166
+
167
+ def validate(input)
168
+ # Success() or Failure()
169
+ end
170
+
171
+ def create(input)
172
+ # Success() or Failure()
173
+ end
174
+
175
+ def welcome_email(email)
176
+ # Success() or Failure()
177
+ end
178
+ end
179
+
180
+ # Usage in modules (singleton methods)
181
+
182
+ module CreateUser
183
+ extend self, Kind::Functional::Steps
184
+
185
+ def perform(input)
186
+ Step!(:validate, input) \
187
+ >> Step(:create) \
188
+ >> Step(:welcome_email)
189
+ end
190
+
191
+ private
192
+
193
+ def validate(input)
194
+ # Success() or Failure()
195
+ end
196
+
197
+ def create(input)
198
+ # Success() or Failure()
199
+ end
200
+
201
+ def welcome_email(email)
202
+ # Success() or Failure()
203
+ end
204
+ end
205
+ ```
206
+
207
+ [⬆️ &nbsp;Back to Top](#changelog-)
208
+
209
+ 5.3.0 (2021-03-23)
210
+ ------------------
211
+
212
+ ### Added
213
+
214
+ * [#53](https://github.com/serradura/kind/pull/53) - Allow `Kind::Result#map` and `Kind::Result#map!` receive a callable as an argument.
215
+
216
+ * [#53](https://github.com/serradura/kind/pull/53) - Add `|` and `>>` as an alias of `Kind::Result#map!`.
217
+
218
+ * [#53](https://github.com/serradura/kind/pull/53) - Add step adapters for `Kind::Action` and `Kind::Functional::Action`. This is the list of methods: `Step`, `Map`, `Try`, `Tee`, `Check`.
219
+ ```ruby
220
+ module CreateUser
221
+ extend Kind::Functional::Action
222
+
223
+ def call!(input)
224
+ Step!(:validate, input) \
225
+ | Step(:create)
226
+ end
227
+
228
+ private
229
+
230
+ def validate(input)
231
+ # returns Success(valid_data) or Failure(validation)
232
+ end
233
+
234
+ def create(input)
235
+ # returns Success(user)
236
+ end
237
+ end
238
+ ```
239
+
240
+ * [#53](https://github.com/serradura/kind/pull/53) - Add `kind/strict/disabled` to turn off all of the strict validations and optimize the runtime. Use case: As strict validation is useful in development, this mechanism could be used to optimize the runtime in production. List of methods that will be disabled:
241
+ * `Kind.of()`
242
+ * `Kind.of_class()`
243
+ * `Kind.of_module()`
244
+ * `Kind.of_module_or_class()`
245
+ * `Kind::<Type>[1]`
246
+ * `Kind::NotNil[1]`
247
+
248
+ [⬆️ &nbsp;Back to Top](#changelog-)
249
+
84
250
  5.2.0 (2021-03-17)
85
251
  ------------------
86
252
 
@@ -254,6 +420,8 @@ This project follows [semver 2.0.0](http://semver.org/spec/v2.0.0.html) and the
254
420
  p number # 0
255
421
  ```
256
422
 
423
+ [⬆️ &nbsp;Back to Top](#changelog-)
424
+
257
425
  * [#46](https://github.com/serradura/kind/pull/46) - Add `Kind::Either` (either monad). This is basically the same as the `Maybe` monad, but with `Some` called `Right` and `None` called `Left`. But this time, `Left` is also allowed to have an underlying value. This module isn't loaded by default, so you will need to require it.
258
426
  ```ruby
259
427
  require 'kind/either'
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <p align="center">
2
2
  <h1 align="center">🤷 kind</h1>
3
- <p align="center"><i>A simple type system (at runtime) for Ruby - free of dependencies.</i></p>
3
+ <p align="center"><i>A development toolkit for Ruby with several small/cohesive abstractions to empower your development workflow - It's totally free of dependencies.</i></p>
4
4
  </p>
5
5
 
6
6
  <p align="center">
@@ -31,16 +31,18 @@
31
31
 
32
32
  **Motivation:**
33
33
 
34
- As a creator of Ruby gems, I have a common need that I have to handle in many of my projects: type checking of method arguments.
34
+ This project was born to help me with a simple task, create a light and fast type checker (at runtime) for Ruby. The initial idea was to have something to raise an exception when a method or function (procs) received a wrong input.
35
35
 
36
- One of the goals of this project is to do simple type checking like `"some string".is_a?(String)`, but, exposing useful abstractions around this. e.g: [Kind.\<Type\> methods](#verifying-the-kind-of-some-object), [active model validations](#kindvalidator-activemodelvalidations), [maybe monad](#kindmaybe).
36
+ But through time it was natural the addition of more features to improve the development workflow, like monads ([`Kind::Maybe`](#kindmaybe), `Kind::Either` / `Kind::Result`), enums (`Kind::Enum`), immutable objects (`Kind::ImmutableAttributes`), [type validation via ActiveModel::Validation](#kindvalidator-activemodelvalidations), and several abstractions to help the implementation of business logic (`Kind::Functional::Steps`, `Kind::Functional::Action`, `Kind::Action`).
37
+
38
+ So, I invite you to check out these features to see how they could be useful for you. Enjoy!
37
39
 
38
40
  ## Documentation <!-- omit in toc -->
39
41
 
40
42
  Version | Documentation
41
43
  ---------- | -------------
42
44
  unreleased | https://github.com/serradura/kind/blob/main/README.md
43
- 5.2.0 | https://github.com/serradura/kind/blob/v5.x/README.md
45
+ 5.6.0 | https://github.com/serradura/kind/blob/v5.x/README.md
44
46
  4.1.0 | https://github.com/serradura/kind/blob/v4.x/README.md
45
47
  3.1.0 | https://github.com/serradura/kind/blob/v3.x/README.md
46
48
  2.3.0 | https://github.com/serradura/kind/blob/v2.x/README.md
@@ -120,14 +122,14 @@ unreleased | https://github.com/serradura/kind/blob/main/README.md
120
122
 
121
123
  ## Compatibility
122
124
 
123
- | kind | branch | ruby | activemodel |
124
- | -------------- | ------- | -------- | -------------- |
125
- | unreleased | main | >= 2.1.0 | >= 3.2, <= 6.1 |
126
- | 5.2.0 | v5.x | >= 2.1.0 | >= 3.2, <= 6.1 |
127
- | 4.1.0 | v4.x | >= 2.2.0 | >= 3.2, <= 6.1 |
128
- | 3.1.0 | v3.x | >= 2.2.0 | >= 3.2, <= 6.1 |
129
- | 2.3.0 | v2.x | >= 2.2.0 | >= 3.2, <= 6.0 |
130
- | 1.9.0 | v1.x | >= 2.2.0 | >= 3.2, <= 6.0 |
125
+ | kind | branch | ruby | activemodel |
126
+ | -------------- | ------- | ------------------ | -------------- |
127
+ | unreleased | main | >= 2.1.0, <= 3.0.0 | >= 3.2, < 7.0 |
128
+ | 5.6.0 | v5.x | >= 2.1.0, <= 3.0.0 | >= 3.2, < 7.0 |
129
+ | 4.1.0 | v4.x | >= 2.2.0, <= 3.0.0 | >= 3.2, < 7.0 |
130
+ | 3.1.0 | v3.x | >= 2.2.0, <= 2.7 | >= 3.2, < 7.0 |
131
+ | 2.3.0 | v2.x | >= 2.2.0, <= 2.7 | >= 3.2, <= 6.0 |
132
+ | 1.9.0 | v1.x | >= 2.2.0, <= 2.7 | >= 3.2, <= 6.0 |
131
133
 
132
134
  > Note: The activemodel is an optional dependency, it is related with the [Kind::Validator](#kindvalidator-activemodelvalidations).
133
135
 
data/kind.gemspec CHANGED
@@ -6,8 +6,8 @@ Gem::Specification.new do |spec|
6
6
  spec.authors = ['Rodrigo Serradura']
7
7
  spec.email = ['rodrigo.serradura@gmail.com']
8
8
 
9
- spec.summary = %q{A simple type system (at runtime) for Ruby.}
10
- spec.description = %q{A simple type system (at runtime) for Ruby - free of dependencies.}
9
+ spec.summary = %q{A development toolkit for Ruby with several small/cohesive abstractions to empower your development workflow.}
10
+ spec.description = %q{A development toolkit for Ruby with several small/cohesive abstractions (monads, enums, business logic, data validation...) to empower your development workflow - It's totally free of dependencies.}
11
11
  spec.homepage = 'https://github.com/serradura/kind'
12
12
  spec.license = 'MIT'
13
13
  spec.required_ruby_version = Gem::Requirement.new('>= 2.1.0')
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module ACTION_STEPS
5
+ private
6
+
7
+ def Check(mthod); ->(value) { __Check(thod, value) }; end
8
+ def Step(mthod); ->(value) { __Step(mthod, value) }; end
9
+ def Map(mthod); ->(value) { __Map(mthod, value) }; end
10
+ def Tee(mthod); ->(value) { __Tee(mthod, value) }; end
11
+ def Try(mthod, opt = Empty::HASH)
12
+ ->(value) { __Try(mthod, value, opt) }
13
+ end
14
+
15
+ def Check!(mthod, value); __Check(mthod, value); end
16
+ def Step!(mthod, value); __Step(mthod, value); end
17
+ def Map!(mthod, value); __Map(mthod, value); end
18
+ def Tee!(mthod, value); __Tee(mthod, value); end
19
+ def Try!(mthod, value, opt = Empty::HASH); __Try(mthod, value, opt); end
20
+
21
+ def __Check(mthod, value) # :nodoc:
22
+ __resolve_step(mthod, value) ? Success(mthod, value) : Failure(mthod, value)
23
+ end
24
+
25
+ def __Step(mthod, value) # :nodoc:
26
+ __resolve_step(mthod, value)
27
+ end
28
+
29
+ def __Map(mthod, value) # :nodoc:
30
+ Success(mthod, __resolve_step(mthod, value))
31
+ end
32
+
33
+ def __Tee(mthod, value) # :nodoc:
34
+ __resolve_step(mthod, value)
35
+
36
+ Success(mthod, value)
37
+ end
38
+
39
+ def __Try(mthod, value, opt = Empty::HASH) # :nodoc:
40
+ begin
41
+ Success(mthod, __resolve_step(mthod, value))
42
+ rescue opt.fetch(:catch, StandardError) => e
43
+ Failure(mthod, __map_step_exception(e))
44
+ end
45
+ end
46
+
47
+ def __resolve_step(mthod, value) # :nodoc:
48
+ send(mthod, value)
49
+ end
50
+
51
+ def __map_step_exception(value) # :nodoc:
52
+ value
53
+ end
54
+ end
55
+
56
+ private_constant :ACTION_STEPS
57
+ end
@@ -8,7 +8,7 @@ module Kind
8
8
  extend self
9
9
 
10
10
  def name!(name)
11
- KIND.of!(::Symbol, name)
11
+ STRICT.kind_of(::Symbol, name)
12
12
  end
13
13
 
14
14
  def value(kind, default, visibility = :private)
@@ -1,10 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'kind/__lib__/strict'
4
+
3
5
  module Kind
4
6
  module KIND
5
7
  extend self
6
8
 
7
- def null?(value) # :nodoc:
9
+ def nil_or_undefined?(value) # :nodoc:
8
10
  value.nil? || Undefined == value
9
11
  end
10
12
 
@@ -14,28 +16,6 @@ module Kind
14
16
  values.empty? ? of_kind : values.all?(&of_kind)
15
17
  end
16
18
 
17
- def of!(kind, value, kind_name = nil) # :nodoc:
18
- return value if kind === value
19
-
20
- error!(kind_name || kind.name, value)
21
- end
22
-
23
- def error!(kind_name, value, label = nil) # :nodoc:
24
- raise Error.new(kind_name, value, label: label)
25
- end
26
-
27
- def of_class?(value) # :nodoc:
28
- value.kind_of?(::Class)
29
- end
30
-
31
- def of_module?(value) # :nodoc:
32
- ::Module == value || (value.kind_of?(::Module) && !of_class?(value))
33
- end
34
-
35
- def of_module_or_class!(value) # :nodoc:
36
- of!(::Module, value, 'Module/Class')
37
- end
38
-
39
19
  def respond_to!(method_name, value) # :nodoc:
40
20
  return value if value.respond_to?(method_name)
41
21
 
@@ -51,15 +31,15 @@ module Kind
51
31
  end
52
32
 
53
33
  def is?(expected, value) # :nodoc:
54
- is(of_module_or_class!(expected), value)
34
+ is(STRICT.module_or_class(expected), value)
55
35
  end
56
36
 
57
37
  private
58
38
 
59
39
  def is(expected_kind, value) # :nodoc:
60
- kind = of_module_or_class!(value)
40
+ kind = STRICT.module_or_class(value)
61
41
 
62
- if of_class?(kind)
42
+ if OF.class?(kind)
63
43
  kind <= expected_kind || expected_kind == ::Class
64
44
  else
65
45
  kind == expected_kind || kind.kind_of?(expected_kind)
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module OF
5
+ extend self
6
+
7
+ def class?(value) # :nodoc:
8
+ value.kind_of?(::Class)
9
+ end
10
+
11
+ def module?(value) # :nodoc:
12
+ ::Module == value || (value.kind_of?(::Module) && !class?(value))
13
+ end
14
+ end
15
+
16
+ private_constant :OF
17
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind/__lib__/of'
4
+
5
+ module Kind
6
+ module STRICT
7
+ extend self
8
+
9
+ def error(kind_name, value, label = nil) # :nodoc:
10
+ raise Error.new(kind_name, value, label: label)
11
+ end
12
+
13
+ def object_is_a(kind, value, label = nil) # :nodoc:
14
+ return value if kind === value
15
+
16
+ error(kind.name, value, label)
17
+ end
18
+
19
+ def kind_of(kind, value, kind_name = nil) # :nodoc:
20
+ return value if kind === value
21
+
22
+ error(kind_name || kind.name, value)
23
+ end
24
+
25
+ def module_or_class(value) # :nodoc:
26
+ kind_of(::Module, value, 'Module/Class')
27
+ end
28
+
29
+ def class!(value) # :nodoc:
30
+ kind_of(::Class, value)
31
+ end
32
+
33
+ def module!(value) # :nodoc:
34
+ return value if OF.module?(value)
35
+
36
+ error('Module', value)
37
+ end
38
+
39
+ def not_nil(value, label) # :nodoc:
40
+ return value unless value.nil?
41
+
42
+ label_text = label ? "#{label}: " : ''
43
+
44
+ raise Error.new("#{label_text}expected to not be nil")
45
+ end
46
+ end
47
+
48
+ private_constant :STRICT
49
+ end
data/lib/kind/action.rb CHANGED
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'kind/basic'
4
+ require 'kind/empty'
4
5
  require 'kind/result'
5
6
  require 'kind/immutable_attributes'
7
+ require 'kind/__lib__/action_steps'
6
8
 
7
9
  module Kind
8
10
  module Action
@@ -20,6 +22,8 @@ module Kind
20
22
  'end'
21
23
  ].join("\n").freeze
22
24
 
25
+ private_constant :CALL_TMPL
26
+
23
27
  module ClassMethods
24
28
  include ImmutableAttributes::ClassMethods
25
29
 
@@ -71,21 +75,52 @@ module Kind
71
75
  end
72
76
  end
73
77
 
78
+ module StepAdapters
79
+ private
80
+
81
+ def Check!(mthod); __Check(mthod, Empty::HASH); end
82
+ def Step!(mthod); __Step(mthod, Empty::HASH); end
83
+ def Map!(mthod); __Map(mthod, Empty::HASH); end
84
+ def Tee!(_mthod); raise NotImplementedError; end
85
+ def Try!(mthod, opt = Empty::HASH); __Try(mthod, Empty::HASH, opt); end
86
+
87
+ def __resolve_step(method_name, value)
88
+ m = method(method_name)
89
+ m.arity > 0 ? m.call(value) : m.call
90
+ end
91
+
92
+ def __map_step_exception(value)
93
+ { exception: value }
94
+ end
95
+ end
96
+
97
+ private_constant :StepAdapters
98
+
74
99
  def self.included(base)
75
- KIND.of!(::Class, base).extend(ClassMethods)
100
+ Kind.of_class(base).extend(ClassMethods)
76
101
 
102
+ base.send(:include, ACTION_STEPS)
103
+ base.send(:include, StepAdapters)
77
104
  base.send(:include, ImmutableAttributes::Reader)
78
105
  end
79
106
 
80
107
  include ImmutableAttributes::Initializer
81
108
 
109
+ def inspect
110
+ '#<%s attributes=%p nil_attributes=%p>' % [self.class.name, attributes, nil_attributes]
111
+ end
112
+
82
113
  private
83
114
 
84
115
  def Failure(arg1 = UNDEFINED, arg2 = UNDEFINED)
116
+ arg1 = Empty::HASH if UNDEFINED == arg1 && UNDEFINED == arg2
117
+
85
118
  Result::Failure[arg1, arg2, value_must_be_a: ::Hash]
86
119
  end
87
120
 
88
121
  def Success(arg1 = UNDEFINED, arg2 = UNDEFINED)
122
+ arg1 = Empty::HASH if UNDEFINED == arg1 && UNDEFINED == arg2
123
+
89
124
  Result::Success[arg1, arg2, value_must_be_a: ::Hash]
90
125
  end
91
126
  end
data/lib/kind/basic.rb CHANGED
@@ -35,11 +35,11 @@ module Kind
35
35
  end
36
36
 
37
37
  def of_class?(value)
38
- KIND.of_class?(value)
38
+ OF.class?(value)
39
39
  end
40
40
 
41
41
  def of_module?(value)
42
- KIND.of_module?(value)
42
+ OF.module?(value)
43
43
  end
44
44
 
45
45
  def respond_to?(value, *method_names)
@@ -49,15 +49,21 @@ module Kind
49
49
  end
50
50
 
51
51
  def of(kind, value, label: nil)
52
- return value if kind === value
53
-
54
- KIND.error!(kind.name, value, label)
52
+ STRICT.object_is_a(kind, value, label)
55
53
  end
56
54
 
57
55
  alias_method :of!, :of
58
56
 
57
+ def of_class(value)
58
+ STRICT.class!(value)
59
+ end
60
+
61
+ def of_module(value)
62
+ STRICT.module!(value)
63
+ end
64
+
59
65
  def of_module_or_class(value)
60
- KIND.of_module_or_class!(value)
66
+ STRICT.module_or_class(value)
61
67
  end
62
68
 
63
69
  def respond_to(value, *method_names)
@@ -70,4 +76,8 @@ module Kind
70
76
  def value(kind, value, default:)
71
77
  KIND.value(kind, value, of(kind, default))
72
78
  end
79
+
80
+ def or_nil(kind, value)
81
+ return value if kind === value
82
+ end
73
83
  end
data/lib/kind/dig.rb CHANGED
@@ -12,7 +12,7 @@ module Kind
12
12
  keys.reduce(data) do |memo, key|
13
13
  value = get(memo, key)
14
14
 
15
- break if KIND.null?(value)
15
+ break if KIND.nil_or_undefined?(value)
16
16
 
17
17
  value
18
18
  end
@@ -25,7 +25,7 @@ module Kind
25
25
 
26
26
  return result unless block_given?
27
27
 
28
- yield(result) unless KIND.null?(result)
28
+ yield(result) unless KIND.nil_or_undefined?(result)
29
29
  end
30
30
 
31
31
  def presence(*args, &block)
@@ -9,7 +9,7 @@ module Kind
9
9
  def value_or(default = UNDEFINED, &block)
10
10
  Error.invalid_default_arg! if UNDEFINED == default && !block
11
11
 
12
- UNDEFINED != default ? default : block.call
12
+ UNDEFINED != default ? default : block.call(value)
13
13
  end
14
14
 
15
15
  def map(&_)
data/lib/kind/function.rb CHANGED
@@ -17,9 +17,7 @@ module Kind
17
17
  end
18
18
 
19
19
  def self.extended(base)
20
- KIND.error!('Module', base) unless Kind.of_module?(base)
21
-
22
- base.extend(base)
20
+ base.extend(Kind.of_module(base))
23
21
  end
24
22
 
25
23
  def kind_function!
@@ -11,7 +11,7 @@ module Kind
11
11
  end
12
12
 
13
13
  def self.included(base)
14
- KIND.of!(::Class, base).send(:extend, ClassMethods)
14
+ Kind.of_class(base).send(:extend, ClassMethods)
15
15
  end
16
16
 
17
17
  module Behavior
@@ -22,7 +22,7 @@ module Kind
22
22
  end
23
23
 
24
24
  def initialize(arg = Empty::HASH)
25
- hash = KIND.of!(::Hash, arg)
25
+ hash = STRICT.kind_of(::Hash, arg)
26
26
 
27
27
  self.class.__dependencies__.each do |name, (kind, default, _visibility)|
28
28
  value_to_assign = ATTRIBUTES.value_to_assign!(kind, default, hash, name)
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'kind/result'
4
3
  require 'kind/functional'
4
+ require 'kind/functional/steps'
5
5
 
6
6
  module Kind
7
7
  module Functional::Action
@@ -56,10 +56,10 @@ module Kind
56
56
  "#{CALL_TMPL % [call_tmpl_args, call_tmpl_args]}"
57
57
  )
58
58
 
59
- if KIND.of_module?(self)
60
- self.send(:extend, Result::Methods)
59
+ if Kind.of_module?(self)
60
+ self.send(:extend, Functional::Steps)
61
61
  else
62
- self.send(:include, Result::Methods)
62
+ self.send(:include, Functional::Steps)
63
63
  self.send(:include, Functional::Behavior)
64
64
 
65
65
  __dependencies__.freeze
@@ -72,15 +72,13 @@ module Kind
72
72
  end
73
73
 
74
74
  def self.included(base)
75
- KIND.of!(::Class, base).send(:extend, Functional::DependencyInjection)
75
+ Kind.of_class(base).send(:extend, Functional::DependencyInjection)
76
76
 
77
77
  base.send(:extend, Macros)
78
78
  end
79
79
 
80
80
  def self.extended(base)
81
- KIND.error!('Module', base) unless Kind.of_module?(base)
82
-
83
- base.send(:extend, base)
81
+ base.send(:extend, Kind.of_module(base))
84
82
  base.send(:extend, Macros)
85
83
  end
86
84
 
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'kind/basic'
4
+ require 'kind/empty'
5
+ require 'kind/result'
6
+ require 'kind/__lib__/action_steps'
7
+
8
+ module Kind
9
+ module Functional
10
+ module Steps
11
+ def self.extended(base)
12
+ base.extend(Result::Methods)
13
+ base.extend(ACTION_STEPS)
14
+ end
15
+
16
+ def self.included(base)
17
+ base.send(:include, Result::Methods)
18
+ base.send(:include, ACTION_STEPS)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -28,7 +28,7 @@ module Kind
28
28
  end
29
29
 
30
30
  def with_attributes(arg)
31
- hash = KIND.of!(::Hash, arg)
31
+ hash = STRICT.kind_of(::Hash, arg)
32
32
 
33
33
  self.class.new(@_____attrs.merge(hash))
34
34
  end
data/lib/kind/maybe.rb CHANGED
@@ -14,7 +14,7 @@ module Kind
14
14
  extend self
15
15
 
16
16
  def new(value)
17
- (::Exception === value || KIND.null?(value) ? None : Some)
17
+ (::Exception === value || KIND.nil_or_undefined?(value) ? None : Some)
18
18
  .new(value)
19
19
  end
20
20
 
@@ -25,7 +25,7 @@ module Kind
25
25
  alias_method :and_then!, :map!
26
26
 
27
27
  def try!(method_name = UNDEFINED, *args, &block)
28
- KIND.of!(::Symbol, method_name)if UNDEFINED != method_name
28
+ STRICT.kind_of(::Symbol, method_name)if UNDEFINED != method_name
29
29
 
30
30
  self
31
31
  end
@@ -6,12 +6,12 @@ require 'kind/presence'
6
6
  module Kind
7
7
  module Maybe
8
8
  class Some < Monad
9
- KindSymbol = ->(value) { KIND.of!(::Symbol, value) }
9
+ KindSymbol = ->(value) { STRICT.kind_of(::Symbol, value) }
10
10
 
11
11
  VALUE_CANT_BE_NONE = "value can't be nil or Kind::Undefined".freeze
12
12
 
13
13
  def self.[](value)
14
- return new(value) if !KIND.null?(value)
14
+ return new(value) if !KIND.nil_or_undefined?(value)
15
15
 
16
16
  raise ArgumentError, VALUE_CANT_BE_NONE
17
17
  end
@@ -57,7 +57,7 @@ module Kind
57
57
  fn.call(@value)
58
58
  end
59
59
 
60
- !result || KIND.null?(result) ? NONE_INSTANCE : self
60
+ !result || KIND.nil_or_undefined?(result) ? NONE_INSTANCE : self
61
61
  end
62
62
 
63
63
  alias_method :accept, :check
@@ -73,7 +73,7 @@ module Kind
73
73
  fn.call(@value)
74
74
  end
75
75
 
76
- result || KIND.null?(result) ? NONE_INSTANCE : self
76
+ result || KIND.nil_or_undefined?(result) ? NONE_INSTANCE : self
77
77
  end
78
78
 
79
79
  def try!(method_name = UNDEFINED, *args, &block)
@@ -120,7 +120,7 @@ module Kind
120
120
 
121
121
  def resolve(result)
122
122
  return result if Maybe::None === result
123
- return NONE_INSTANCE if KIND.null?(result)
123
+ return NONE_INSTANCE if KIND.nil_or_undefined?(result)
124
124
  return None.new(result) if ::Exception === result
125
125
 
126
126
  Some.new(result)
@@ -3,9 +3,7 @@
3
3
  module Kind
4
4
  module BasicObject
5
5
  def [](value, label: nil)
6
- return value if self === value
7
-
8
- KIND.error!(name, value, label)
6
+ STRICT.object_is_a(self, value, label)
9
7
  end
10
8
 
11
9
  def or_nil(value)
@@ -33,7 +31,7 @@ module Kind
33
31
  end
34
32
 
35
33
  def or_null(value) # :nodoc:
36
- KIND.null?(value) ? value : self[value]
34
+ KIND.nil_or_undefined?(value) ? value : self[value]
37
35
  end
38
36
 
39
37
  private
@@ -3,11 +3,7 @@
3
3
  module Kind
4
4
  module NotNil
5
5
  def self.[](value, label: nil)
6
- return value unless value.nil?
7
-
8
- label_text = label ? "#{label}: " : ''
9
-
10
- raise Error.new("#{label_text}expected to not be nil")
6
+ STRICT.not_nil(value, label)
11
7
  end
12
8
  end
13
9
  end
@@ -34,7 +34,7 @@ module Kind
34
34
  def initialize(kind, opt)
35
35
  name = ResolveKindName.(kind, opt)
36
36
 
37
- @name = KIND.of!(::String, name)
37
+ @name = STRICT.kind_of(::String, name)
38
38
  @kind = KIND.respond_to!(:===, kind)
39
39
  end
40
40
 
@@ -5,7 +5,7 @@ module Kind
5
5
  include Kind::BasicObject
6
6
 
7
7
  def self.[](*args)
8
- args.each { |arg| KIND.of!(::Symbol, arg) }
8
+ args.each { |arg| STRICT.kind_of(::Symbol, arg) }
9
9
 
10
10
  new(args)
11
11
  end
@@ -16,7 +16,7 @@ module Kind
16
16
  end
17
17
 
18
18
  def |(kind)
19
- self.class.new(@kinds + [Interface[kind]])
19
+ self.class.new(@kinds + [Interface[kind.nil? ? Kind::Nil : kind]])
20
20
  end
21
21
 
22
22
  def ===(value)
data/lib/kind/presence.rb CHANGED
@@ -7,7 +7,7 @@ module Kind
7
7
  extend self
8
8
 
9
9
  def call(object)
10
- return if KIND.null?(object)
10
+ return if KIND.nil_or_undefined?(object)
11
11
 
12
12
  return object.blank? ? nil : object if object.respond_to?(:blank?)
13
13
 
@@ -11,13 +11,15 @@ module Kind
11
11
  def value_or(default = UNDEFINED, &block)
12
12
  Error.invalid_default_arg! if UNDEFINED == default && !block
13
13
 
14
- UNDEFINED != default ? default : block.call
14
+ UNDEFINED != default ? default : block.call(value)
15
15
  end
16
16
 
17
- def map(&_)
17
+ def map(_ = UNDEFINED, &_fn)
18
18
  self
19
19
  end
20
20
 
21
+ alias_method :|, :map
22
+ alias_method :>>, :map
21
23
  alias_method :map!, :map
22
24
  alias_method :then, :map
23
25
  alias_method :then!, :map
@@ -4,18 +4,21 @@ module Kind
4
4
  class Result::Monad
5
5
  include Result::Abstract
6
6
 
7
+ require 'kind/empty'
7
8
  require 'kind/result/monad/wrapper'
8
9
 
9
10
  attr_reader :type, :value
10
11
 
11
- def self.[](arg1 = UNDEFINED, arg2 = UNDEFINED, value_must_be_a: nil) # :nodoc:
12
- type = UNDEFINED == arg2 ? self::DEFAULT_TYPE : KIND.of!(::Symbol, arg1)
12
+ def self.[](arg1 = UNDEFINED, arg2 = UNDEFINED, opt = Empty::HASH) # :nodoc:
13
+ value_must_be_a = opt[:value_must_be_a]
14
+
15
+ type = UNDEFINED == arg2 ? self::DEFAULT_TYPE : STRICT.kind_of(::Symbol, arg1)
13
16
 
14
17
  Error.wrong_number_of_args!(given: 0, expected: '1 or 2') if UNDEFINED == arg1
15
18
 
16
19
  value = UNDEFINED == arg2 ? arg1 : arg2
17
20
 
18
- new(type, (value_must_be_a ? KIND.of!(value_must_be_a, value) : value))
21
+ new(type, (value_must_be_a ? STRICT.kind_of(value_must_be_a, value) : value))
19
22
  end
20
23
 
21
24
  private_class_method :new
@@ -29,10 +32,12 @@ module Kind
29
32
  raise NotImplementedError
30
33
  end
31
34
 
32
- def map(&_)
35
+ def map(_ = UNDEFINED, &_fn)
33
36
  raise NotImplementedError
34
37
  end
35
38
 
39
+ alias_method :|, :map
40
+ alias_method :>>, :map
36
41
  alias_method :map!, :map
37
42
  alias_method :then, :map
38
43
  alias_method :then!, :map
@@ -12,29 +12,42 @@ module Kind
12
12
  @value
13
13
  end
14
14
 
15
- def map(&fn)
16
- map!(&fn)
15
+ def map(callable = UNDEFINED, &fn)
16
+ _resolve_map(callable, fn)
17
17
  rescue Kind::Monad::Error => e
18
18
  raise e
19
19
  rescue StandardError => e
20
20
  Result::Failure[:exception, e]
21
21
  end
22
22
 
23
- def map!(&fn)
24
- monad = fn.call(@value)
25
-
26
- return monad if Result::Monad === monad
23
+ alias_method :then, :map
24
+ alias_method :and_then, :map
27
25
 
28
- raise Kind::Monad::Error.new('Kind::Success | Kind::Failure', monad)
26
+ def map!(callable = UNDEFINED, &fn)
27
+ _resolve_map(callable, fn)
29
28
  end
30
29
 
31
- alias_method :then, :map
30
+ alias_method :|, :map!
31
+ alias_method :>>, :map!
32
32
  alias_method :then!, :map!
33
- alias_method :and_then, :map
34
33
  alias_method :and_then!, :map!
35
34
 
36
35
  def inspect
37
36
  '#<%s type=%p value=%p>' % ['Kind::Success', type, value]
38
37
  end
38
+
39
+ private
40
+
41
+ def _resolve_map(callable, fn)
42
+ callable.respond_to?(:call) ? _map(callable) : _map(fn)
43
+ end
44
+
45
+ def _map(fn)
46
+ monad = fn.call(@value)
47
+
48
+ return monad if Result::Monad === monad
49
+
50
+ raise Kind::Monad::Error.new('Kind::Success | Kind::Failure', monad)
51
+ end
39
52
  end
40
53
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module STRICT
5
+ [
6
+ :object_is_a, :class!, :kind_of,
7
+ :module_or_class, :module!, :not_nil
8
+ ].each { |method_name| remove_method(method_name) }
9
+
10
+ def object_is_a(_kind, value, _label = nil) # :nodoc:
11
+ value
12
+ end
13
+
14
+ def class!(value) # :nodoc:
15
+ value
16
+ end
17
+
18
+ def kind_of(_kind, value, _kind_name = nil) # :nodoc:
19
+ value
20
+ end
21
+
22
+ def module_or_class(value) # :nodoc:
23
+ value
24
+ end
25
+
26
+ def module!(value) # :nodoc:
27
+ value
28
+ end
29
+
30
+ def not_nil(value, label) # :nodoc:
31
+ value
32
+ end
33
+ end
34
+ end
data/lib/kind/try.rb CHANGED
@@ -9,7 +9,7 @@ module Kind
9
9
  extend self
10
10
 
11
11
  def call!(object, method_name, args = Empty::ARRAY) # :nodoc
12
- return if KIND.null?(object)
12
+ return if KIND.nil_or_undefined?(object)
13
13
 
14
14
  resolve(object, method_name, args)
15
15
  end
@@ -21,7 +21,7 @@ module Kind
21
21
 
22
22
  return result unless block_given?
23
23
 
24
- yield(result) unless KIND.null?(result)
24
+ yield(result) unless KIND.nil_or_undefined?(result)
25
25
  end
26
26
 
27
27
  def presence(*args, &block)
@@ -101,7 +101,7 @@ class KindValidator < ActiveModel::EachValidator
101
101
  def kind_is_not(expected, value)
102
102
  case expected
103
103
  when ::Class
104
- return if expected == Kind.of!(::Class, value) || value < expected
104
+ return if expected == Kind.of_class(value) || value < expected
105
105
 
106
106
  "must be the class or a subclass of `#{expected.name}`"
107
107
  when ::Module
data/lib/kind/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Kind
4
- VERSION = '5.2.0'
4
+ VERSION = '5.6.0'
5
5
  end
metadata CHANGED
@@ -1,16 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kind
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.2.0
4
+ version: 5.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Serradura
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-03-18 00:00:00.000000000 Z
11
+ date: 2021-05-14 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: A simple type system (at runtime) for Ruby - free of dependencies.
13
+ description: A development toolkit for Ruby with several small/cohesive abstractions
14
+ (monads, enums, business logic, data validation...) to empower your development
15
+ workflow - It's totally free of dependencies.
14
16
  email:
15
17
  - rodrigo.serradura@gmail.com
16
18
  executables: []
@@ -31,8 +33,11 @@ files:
31
33
  - bin/setup
32
34
  - kind.gemspec
33
35
  - lib/kind.rb
36
+ - lib/kind/__lib__/action_steps.rb
34
37
  - lib/kind/__lib__/attributes.rb
35
38
  - lib/kind/__lib__/kind.rb
39
+ - lib/kind/__lib__/of.rb
40
+ - lib/kind/__lib__/strict.rb
36
41
  - lib/kind/__lib__/undefined.rb
37
42
  - lib/kind/action.rb
38
43
  - lib/kind/active_model/validation.rb
@@ -54,6 +59,7 @@ files:
54
59
  - lib/kind/function.rb
55
60
  - lib/kind/functional.rb
56
61
  - lib/kind/functional/action.rb
62
+ - lib/kind/functional/steps.rb
57
63
  - lib/kind/immutable_attributes.rb
58
64
  - lib/kind/immutable_attributes/initializer.rb
59
65
  - lib/kind/immutable_attributes/reader.rb
@@ -109,6 +115,7 @@ files:
109
115
  - lib/kind/result/monad.rb
110
116
  - lib/kind/result/monad/wrapper.rb
111
117
  - lib/kind/result/success.rb
118
+ - lib/kind/strict/disabled.rb
112
119
  - lib/kind/try.rb
113
120
  - lib/kind/validator.rb
114
121
  - lib/kind/version.rb
@@ -137,5 +144,6 @@ requirements: []
137
144
  rubygems_version: 3.2.11
138
145
  signing_key:
139
146
  specification_version: 4
140
- summary: A simple type system (at runtime) for Ruby.
147
+ summary: A development toolkit for Ruby with several small/cohesive abstractions to
148
+ empower your development workflow.
141
149
  test_files: []