kind 5.4.1 → 5.8.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: 4b29ece70180bb5064e2d1c598ce81d09dabfe11492a14e4f97efe4f47154565
4
- data.tar.gz: 927a414846defd4e53430fbb6d5a36b26dbc51b1de6bb81b8bb797f266dca8c8
3
+ metadata.gz: 93717ed184e878dc6d3dea92d7c35f92f1157c3d7277d44960d7030b240173f7
4
+ data.tar.gz: d7f61ff50a6d66a2abcf62baf220528804e66bce854c8de76fe5dcb5327d0e46
5
5
  SHA512:
6
- metadata.gz: '018246a93c971ba79591b6a6f6e18417d5c67f8574865a38fa617782ce2419a3941851303b99a90ad31ab5e4362a0465c8b13222096cc17521693a775248b8ae'
7
- data.tar.gz: dcd4c0ac25fe19b63039d92cd40d006b133cbc662b662933b5ec6b3d95b3fcbbd5767f842a2402a1e28e4f2c831a3c058aa69cac7614964a03df0da2d8a9ba3f
6
+ metadata.gz: 185a54220516ff302377c99d44aaff8d939fe14aed4dd9a3cc35c8d8e766bc2bf72451191a2751be14a1dd4687b3d818436d54646ac0f36c54ce9a61963865b8
7
+ data.tar.gz: 6c452536bc4f40a94dec53c7fa6779157f763f3a5239db9520fb75f2f5036c09bb2d145c5b2a53b85c42825b534c7c55ef4ec3228f0576f701543572f011727b
@@ -0,0 +1,27 @@
1
+
2
+ name: build
3
+ on: [pull_request]
4
+ jobs:
5
+ test:
6
+ runs-on: ubuntu-latest
7
+ strategy:
8
+ matrix:
9
+ ruby: [2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 3.0]
10
+ steps:
11
+ - uses: actions/checkout@v2
12
+ - uses: ruby/setup-ruby@v1
13
+ with:
14
+ ruby-version: ${{ matrix.ruby }}
15
+ bundler-cache: true
16
+ - name: Test and generate coverage
17
+ run: bin/test
18
+ - name: Format coverage
19
+ if: ${{ matrix.ruby >= 3 }}
20
+ run: bin/prepare_coverage
21
+ - uses: paambaati/codeclimate-action@v2.7.5
22
+ if: ${{ matrix.ruby >= 3 }}
23
+ env:
24
+ CC_TEST_REPORTER_ID: 9561ceed21b6724aea8063e82e5700bc8266e962978089594bf2d8f8ca5ffc94
25
+ with:
26
+ debug: true
27
+ coverageLocations: coverage/.resultset.json:simplecov
data/.gitignore CHANGED
@@ -9,3 +9,4 @@
9
9
 
10
10
  Gemfile.lock
11
11
  .foo
12
+ .tool-versions
@@ -0,0 +1,8 @@
1
+ {
2
+ "cSpell.enabled": true,
3
+ "cSpell.ignoreWords": [
4
+ "paambaati",
5
+ "resultset",
6
+ "simplecov"
7
+ ]
8
+ }
data/CHANGELOG.md CHANGED
@@ -3,79 +3,87 @@
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.8.0 (2021-09-22)](#580-2021-09-22)
7
+ - [Added](#added)
8
+ - [5.7.0 (2021-06-22)](#570-2021-06-22)
9
+ - [Added](#added-1)
10
+ - [5.6.0 (2021-05-14)](#560-2021-05-14)
11
+ - [Added](#added-2)
12
+ - [5.5.0 (2021-04-05)](#550-2021-04-05)
13
+ - [Added](#added-3)
6
14
  - [5.4.1 (2021-03-26)](#541-2021-03-26)
7
15
  - [Fixed](#fixed)
8
16
  - [5.4.0 (2021-03-25)](#540-2021-03-25)
9
- - [Added](#added)
17
+ - [Added](#added-4)
10
18
  - [5.3.0 (2021-03-23)](#530-2021-03-23)
11
- - [Added](#added-1)
19
+ - [Added](#added-5)
12
20
  - [5.2.0 (2021-03-17)](#520-2021-03-17)
13
- - [Added](#added-2)
21
+ - [Added](#added-6)
14
22
  - [Deprecated](#deprecated)
15
23
  - [Changes](#changes)
16
24
  - [5.1.0 (2021-02-23)](#510-2021-02-23)
17
- - [Added](#added-3)
25
+ - [Added](#added-7)
18
26
  - [Deprecated](#deprecated-1)
19
27
  - [5.0.0 (2021-02-22)](#500-2021-02-22)
20
28
  - [Breaking Changes](#breaking-changes)
21
29
  - [Removed](#removed)
22
30
  - [4.1.0 (2021-02-22)](#410-2021-02-22)
23
- - [Added](#added-4)
31
+ - [Added](#added-8)
24
32
  - [4.0.0 (2021-02-22)](#400-2021-02-22)
25
- - [Added](#added-5)
33
+ - [Added](#added-9)
26
34
  - [Deprecated](#deprecated-2)
27
35
  - [Fixed](#fixed-1)
28
36
  - [3.1.0 (2020-07-08)](#310-2020-07-08)
29
- - [Added](#added-6)
37
+ - [Added](#added-10)
30
38
  - [3.0.0 (2020-06-25)](#300-2020-06-25)
31
39
  - [Breaking Changes](#breaking-changes-1)
32
- - [Added](#added-7)
40
+ - [Added](#added-11)
33
41
  - [2.3.0 (2020-06-24)](#230-2020-06-24)
34
- - [Added](#added-8)
42
+ - [Added](#added-12)
35
43
  - [2.2.0 (2020-06-23)](#220-2020-06-23)
36
- - [Added](#added-9)
44
+ - [Added](#added-13)
37
45
  - [2.1.0 (2020-05-12)](#210-2020-05-12)
38
- - [Added](#added-10)
46
+ - [Added](#added-14)
39
47
  - [Breaking Changes](#breaking-changes-2)
40
48
  - [2.0.0 (2020-05-07)](#200-2020-05-07)
41
- - [Added](#added-11)
49
+ - [Added](#added-15)
42
50
  - [Breaking Changes](#breaking-changes-3)
43
51
  - [Removed](#removed-1)
44
52
  - [1.9.0 (2020-05-06)](#190-2020-05-06)
45
- - [Added](#added-12)
53
+ - [Added](#added-16)
46
54
  - [1.8.0 (2020-05-03)](#180-2020-05-03)
47
- - [Added](#added-13)
55
+ - [Added](#added-17)
48
56
  - [1.7.0 (2020-05-03)](#170-2020-05-03)
49
57
  - [Fixed](#fixed-2)
50
58
  - [1.6.0 (2020-04-17)](#160-2020-04-17)
51
- - [Added](#added-14)
59
+ - [Added](#added-18)
52
60
  - [Changes](#changes-1)
53
61
  - [1.5.0 (2020-04-12)](#150-2020-04-12)
54
- - [Added](#added-15)
62
+ - [Added](#added-19)
55
63
  - [1.4.0 (2020-04-12)](#140-2020-04-12)
56
- - [Added](#added-16)
64
+ - [Added](#added-20)
57
65
  - [1.3.0 (2020-04-12)](#130-2020-04-12)
58
- - [Added](#added-17)
66
+ - [Added](#added-21)
59
67
  - [1.2.0 (2020-04-12)](#120-2020-04-12)
60
- - [Added](#added-18)
68
+ - [Added](#added-22)
61
69
  - [1.1.0 (2020-04-09)](#110-2020-04-09)
62
- - [Added](#added-19)
70
+ - [Added](#added-23)
63
71
  - [Fixed](#fixed-3)
64
72
  - [1.0.0 (2020-03-16)](#100-2020-03-16)
65
- - [Added](#added-20)
73
+ - [Added](#added-24)
66
74
  - [0.6.0 (2020-01-06)](#060-2020-01-06)
67
- - [Added](#added-21)
75
+ - [Added](#added-25)
68
76
  - [0.5.0 (2020-01-04)](#050-2020-01-04)
69
- - [Added](#added-22)
77
+ - [Added](#added-26)
70
78
  - [0.4.0 (2020-01-03)](#040-2020-01-03)
71
- - [Added](#added-23)
79
+ - [Added](#added-27)
72
80
  - [0.3.0 (2020-01-03)](#030-2020-01-03)
73
- - [Added](#added-24)
81
+ - [Added](#added-28)
74
82
  - [Breaking Changes](#breaking-changes-4)
75
83
  - [0.2.0 (2020-01-02)](#020-2020-01-02)
76
- - [Added](#added-25)
84
+ - [Added](#added-29)
77
85
  - [0.1.0 (2019-12-26)](#010-2019-12-26)
78
- - [Added](#added-26)
86
+ - [Added](#added-30)
79
87
 
80
88
  ## Unreleased
81
89
 
@@ -87,6 +95,181 @@ This project follows [semver 2.0.0](http://semver.org/spec/v2.0.0.html) and the
87
95
  ### Fixed
88
96
  -->
89
97
 
98
+ 5.8.0 (2021-09-22)
99
+ ------------------
100
+
101
+ ### Added
102
+
103
+ * [#66](https://github.com/serradura/kind/pull/66) - Add `Kind::Any` to make easier the verification of a value in a list (array) of expected values.
104
+ ```ruby
105
+ require 'kind/any'
106
+
107
+ Level = Kind::Any[:low, :high] # or Kind::Any.new([:low, :high])
108
+
109
+ Level === :low # true
110
+ Level === :high # true
111
+
112
+ Level === :foo # false
113
+
114
+ Level[:low] # :low
115
+ Level[:high] # :high
116
+
117
+ Level[:foo] # Kind::Error (:foo expected to be a kind of Kind::Any[:low, :high])
118
+
119
+ level_or_any_symbol = # (Kind::Any[:low, :high] | Symbol)
120
+
121
+ Level.name # 'Kind::Any[:low, :high]'
122
+ Level.inspect # 'Kind::Any[:low, :high]'
123
+
124
+ Level.values # [:low, :high]
125
+ ```
126
+
127
+ * [#66](https://github.com/serradura/kind/pull/66) - Make `Kind.assert_hash!(hash, schema:)` works with a `Kind::Object`.
128
+ ```ruby
129
+ require 'kind/enum'
130
+
131
+ module Level
132
+ include Kind::Enum.from_array([:low, :medium, :high], use_index_as_value: false)
133
+ end
134
+
135
+ Level.keys # ["low", "medium", "high"]
136
+ Level.values # [:low, :medium, :high]
137
+
138
+ # ---
139
+
140
+ module Status
141
+ include Kind::Enum.from_array([:open, :closed], use_index_as_value: true)
142
+ end
143
+
144
+ Status.keys # ["open", "closed"]
145
+ Status.values # [0, 1]
146
+ ```
147
+
148
+ * [#66](https://github.com/serradura/kind/pull/66) - Make `Kind.assert_hash!(hash, schema:)` works with a `Kind::Object`.
149
+ ```ruby
150
+ FilledString = begin
151
+ filled_string = ->(value) {value.is_a?(String) && value.present?}
152
+
153
+ Kind::Of(filled_string, name: 'FilledString')
154
+ end
155
+
156
+ Kind.assert_hash!(some_hash, schema: {
157
+ string: FilledString,
158
+ callable: Kind::Callable,
159
+ })
160
+ ```
161
+
162
+ * [#66](https://github.com/serradura/kind/pull/66) - Improve the exception messages of `Kind.assert_hash!(hash, schema:)`.
163
+ ```ruby
164
+ Kind.assert_hash!({status: 1}, schema: {status: Kind::String | Symbol})
165
+ # Kind::Error (The key :status has an invalid value. Expected: (String | Symbol))
166
+
167
+ Kind.assert_hash!({status: 'closed'}, schema: {status: 'active'})
168
+ # Kind::Error (The key :status has an invalid value. Expected: active, Given: closed)
169
+
170
+ Kind.assert_hash!({callable: 1}, schema: {callable: Kind::Callable})
171
+ # Kind::Error (The key :callable has an invalid value. Expected: Callable)
172
+ ```
173
+
174
+ * [#66](https://github.com/serradura/kind/pull/66) - Make `Kind.assert_hash!(hash, **options)` raises an error if the given hash be empty.
175
+ ```ruby
176
+ Kind.assert_hash!({}, keys: []) # ArgumentError (hash can't be empty)
177
+ Kind.assert_hash!({}, schema: {}) # ArgumentError (hash can't be empty)
178
+ ```
179
+
180
+ [⬆️  Back to Top](#changelog-)
181
+
182
+ 5.7.0 (2021-06-22)
183
+ ------------------
184
+
185
+ ### Added
186
+
187
+ * [#58](https://github.com/serradura/kind/pull/58) - Add `Add Kind.assert_hash!(hash, keys:)`, you can use the `require_all:` option to check if the hashes have the same keys.
188
+ ```ruby
189
+ h1 = {a: 1, b: 1}
190
+
191
+ Kind.assert_hash!(h1, keys: [:a, :b])
192
+ Kind.assert_hash!(h1, keys: [:a]) # ArgumentError (Unknown key: :b. Valid keys are: :a)
193
+
194
+ # --
195
+
196
+ h2 = {'a' => 1, 'b' => 2}
197
+
198
+ Kind.assert_hash!(h2, keys: ['a', 'b'])
199
+ ```
200
+
201
+ * [#58](https://github.com/serradura/kind/pull/58) - Add `Add Kind.assert_hash!(hash, schema:)`, you can use the `require_all:` option to check if the hashes have the same keys.
202
+ ```ruby
203
+ hash = {hash: {}, array: [], number: 1, string: 'foo', email: 'bar@bar.com', null: nil}
204
+
205
+ Kind.assert_hash!(hash, schema: {
206
+ hash: {},
207
+ array: [],
208
+ email: 'bar@bar.com',
209
+ string: 'foo',
210
+ number: 1,
211
+ null: nil
212
+ })
213
+
214
+ Kind.assert_hash!(hash, schema: {
215
+ hash: Enumerable,
216
+ array: Enumerable,
217
+ email: /\A.+@.+\..+\z/,
218
+ string: String
219
+ })
220
+
221
+ Kind.assert_hash!(hash, schema: {
222
+ hash: Hash,
223
+ array: Array,
224
+ email: String,
225
+ string: String
226
+ })
227
+
228
+ Kind.assert_hash!(h1, schema: {
229
+ email: ->(value) { value.is_a?(String) && value.include?('@') }
230
+ })
231
+ ```
232
+
233
+ [⬆️  Back to Top](#changelog-)
234
+
235
+ 5.6.0 (2021-05-14)
236
+ ------------------
237
+
238
+ ### Added
239
+
240
+ * [#57](https://github.com/serradura/kind/pull/57) - Allow the usage of `nil` to define union types.
241
+ ```ruby
242
+ (Kind::String | nil) === '' # true
243
+ (Kind::String | nil) === nil # true
244
+
245
+ (Kind::String | nil) === {} # false
246
+ ```
247
+
248
+ [⬆️  Back to Top](#changelog-)
249
+
250
+ 5.5.0 (2021-04-05)
251
+ ------------------
252
+
253
+ ### Added
254
+
255
+ * [#56](https://github.com/serradura/kind/pull/56) - Add `Kind.or_nil()`.
256
+ ```ruby
257
+ Kind.or_nil(String, 1) # nil
258
+
259
+ Kind.or_nil(String, '') # ""
260
+
261
+ # --
262
+
263
+ filled_string = ->(value) { value.is_a?(String) && !value.empty? }
264
+
265
+ Kind.or_nil(filled_string, 1) # nil
266
+ Kind.or_nil(filled_string, '') # nil
267
+
268
+ Kind.or_nil(filled_string, '1') # "1"
269
+ ```
270
+
271
+ [⬆️  Back to Top](#changelog-)
272
+
90
273
  5.4.1 (2021-03-26)
91
274
  ------------------
92
275
 
data/Gemfile CHANGED
@@ -20,7 +20,7 @@ simplecov_version =
20
20
  case RUBY_VERSION
21
21
  when /\A2.[123]/ then '0.17.1'
22
22
  when /\A2.4/ then '~> 0.18.5'
23
- else '~> 0.19'
23
+ else '~> 0.21.2'
24
24
  end
25
25
 
26
26
  is_ruby_2_1 = RUBY_VERSION <= '2.2.0'
data/README.md CHANGED
@@ -8,8 +8,8 @@
8
8
  <img alt="Gem" src="https://img.shields.io/gem/v/kind.svg?style=flat-square">
9
9
  </a>
10
10
 
11
- <a href="https://travis-ci.com/serradura/kind">
12
- <img alt="Build Status" src="https://travis-ci.com/serradura/kind.svg?branch=master">
11
+ <a href="https://github.com/serradura/kind/actions/workflows/ci.yml">
12
+ <img alt="Build Status" src="https://github.com/serradura/kind/actions/workflows/ci.yml/badge.svg">
13
13
  </a>
14
14
 
15
15
  <br />
@@ -42,7 +42,7 @@ So, I invite you to check out these features to see how they could be useful for
42
42
  Version | Documentation
43
43
  ---------- | -------------
44
44
  unreleased | https://github.com/serradura/kind/blob/main/README.md
45
- 5.4.1 | https://github.com/serradura/kind/blob/v5.x/README.md
45
+ 5.8.0 | https://github.com/serradura/kind/blob/v5.x/README.md
46
46
  4.1.0 | https://github.com/serradura/kind/blob/v4.x/README.md
47
47
  3.1.0 | https://github.com/serradura/kind/blob/v3.x/README.md
48
48
  2.3.0 | https://github.com/serradura/kind/blob/v2.x/README.md
@@ -125,7 +125,7 @@ unreleased | https://github.com/serradura/kind/blob/main/README.md
125
125
  | kind | branch | ruby | activemodel |
126
126
  | -------------- | ------- | ------------------ | -------------- |
127
127
  | unreleased | main | >= 2.1.0, <= 3.0.0 | >= 3.2, < 7.0 |
128
- | 5.4.1 | v5.x | >= 2.1.0, <= 3.0.0 | >= 3.2, < 7.0 |
128
+ | 5.8.0 | v5.x | >= 2.1.0, <= 3.0.0 | >= 3.2, < 7.0 |
129
129
  | 4.1.0 | v4.x | >= 2.2.0, <= 3.0.0 | >= 3.2, < 7.0 |
130
130
  | 3.1.0 | v3.x | >= 2.2.0, <= 2.7 | >= 3.2, < 7.0 |
131
131
  | 2.3.0 | v2.x | >= 2.2.0, <= 2.7 | >= 3.2, <= 6.0 |
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+ # Borrowed from https://gist.github.com/qortex/7e7c49f3731391a91ee898336183acef
4
+
5
+ # Temporary hack to get CodeClimate to work with SimpleCov 0.18 JSON format until issue is fixed
6
+ # upstream: https://github.com/codeclimate/test-reporter/issues/413
7
+
8
+ require "json"
9
+
10
+ filename = "coverage/.resultset.json"
11
+ contents = JSON.parse(File.read(filename))
12
+
13
+ def remove_lines_key(obj)
14
+ case obj
15
+ when Hash
16
+ obj.transform_values do |val|
17
+ val.is_a?(Hash) && val.key?("lines") ? val["lines"] : remove_lines_key(val)
18
+ end
19
+ else
20
+ obj
21
+ end
22
+ end
23
+
24
+ # overwrite
25
+ File.write(filename, JSON.generate(remove_lines_key(contents)))
26
+
27
+ puts Dir['coverage/.*.json']
data/bin/test ADDED
@@ -0,0 +1,76 @@
1
+ #!/bin/bash
2
+
3
+ set -e
4
+
5
+ RUBY_V=$(ruby -v)
6
+
7
+ function reset_gemfile_and_test {
8
+ rm Gemfile.lock
9
+
10
+ eval "$1 bundle update"
11
+ eval "$1 bundle exec rake test"
12
+ }
13
+
14
+ function test_with_activemodel {
15
+ reset_gemfile_and_test "ACTIVEMODEL_VERSION=$1"
16
+ }
17
+
18
+ function run_tests_by_modules {
19
+ rm Gemfile.lock
20
+
21
+ bundle update
22
+
23
+ eval "KIND_BASIC=t bundle exec rake test TEST='test/kind/{basic/*_test,basic_test}.rb'"
24
+ eval "KIND_BASIC=t bundle exec rake test TEST='test/kind/enum_test.rb'"
25
+ eval "KIND_BASIC=t bundle exec rake test TEST='test/kind/presence_test.rb'"
26
+ eval "KIND_BASIC=t bundle exec rake test TEST='test/kind/dig_test.rb'"
27
+ eval "KIND_BASIC=t bundle exec rake test TEST='test/kind/try_test.rb'"
28
+ eval "KIND_BASIC=t bundle exec rake test TEST='test/kind/maybe_test.rb'"
29
+ eval "KIND_BASIC=t bundle exec rake test TEST='test/kind/immutable_attributes_test.rb'"
30
+ eval "KIND_BASIC=t bundle exec rake test TEST='test/kind/function_test.rb'"
31
+ eval "KIND_BASIC=t bundle exec rake test TEST='test/kind/action_test.rb'"
32
+ eval "KIND_BASIC=t bundle exec rake test TEST='test/kind/{functional/*_test,functional_test}.rb'"
33
+ eval "KIND_BASIC=t bundle exec rake test TEST='test/kind/either/*_test.rb'"
34
+ eval "KIND_BASIC=t bundle exec rake test TEST='test/kind/result/*_test.rb'"
35
+
36
+ eval "KIND_STRICT=t bundle exec rake test TEST='test/kind/strict_disabled_test.rb'"
37
+ }
38
+
39
+ RUBY_2_12345="ruby 2.[12345]."
40
+ RUBY_2_2345="ruby 2.[2345]."
41
+ RUBY_2_1234="ruby 2.[1234]."
42
+ RUBY_2_567="ruby 2.[567]."
43
+ RUBY_2_12="ruby 2.[12]."
44
+ RUBY_3_X="ruby 3.0."
45
+
46
+ if [[ $RUBY_V =~ $RUBY_2_12345 ]]; then
47
+ if [[ $RUBY_V =~ $RUBY_2_12 ]]; then
48
+ test_with_activemodel "3.2"
49
+ fi
50
+
51
+ if [[ $RUBY_V =~ $RUBY_2_2345 ]]; then
52
+ test_with_activemodel "4.0"
53
+ test_with_activemodel "4.1"
54
+ test_with_activemodel "4.2"
55
+ test_with_activemodel "5.0"
56
+ test_with_activemodel "5.1"
57
+ test_with_activemodel "5.2"
58
+ fi
59
+
60
+ if [[ $RUBY_V =~ $RUBY_2_1234 ]]; then
61
+ run_tests_by_modules
62
+
63
+ reset_gemfile_and_test
64
+ fi
65
+ fi
66
+
67
+ if [[ $RUBY_V =~ $RUBY_2_567 ]] || [[ $RUBY_V =~ $RUBY_3_X ]]; then
68
+ gem install bundler -v ">= 2" --no-doc
69
+
70
+ test_with_activemodel "6.0"
71
+ test_with_activemodel "6.1"
72
+
73
+ run_tests_by_modules
74
+
75
+ reset_gemfile_and_test
76
+ fi
@@ -0,0 +1,92 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ module AssertHash
5
+ module Keys
6
+ def self.require_all(keys, hash)
7
+ expected_keys = keys - hash.keys
8
+
9
+ unless expected_keys.empty?
10
+ raise KeyError.new("#{hash.inspect} expected to have these keys: #{expected_keys}")
11
+ end
12
+
13
+ unexpected_keys = hash.keys - keys
14
+
15
+ unless unexpected_keys.empty?
16
+ raise KeyError.new("#{hash.inspect} expected to NOT have these keys: #{unexpected_keys}")
17
+ end
18
+
19
+ hash
20
+ end
21
+ end
22
+
23
+ module Schema
24
+ extend self
25
+
26
+ KindObject = ->(value) do
27
+ defined?(Kind::Object) ? Kind::Object === value : false
28
+ end
29
+
30
+ KindUnionType = ->(value) do
31
+ defined?(Kind::UnionType) ? Kind::UnionType === value : false
32
+ end
33
+
34
+ def any(hash, spec)
35
+ spec.each do |key, expected|
36
+ value = hash[key]
37
+ error_message = "The key #{key.inspect} has an invalid value"
38
+
39
+ case expected
40
+ when KindUnionType, KindObject then assert_kind_object(expected, value, error_message)
41
+ when ::Module then assert_kind_of(expected, value, error_message)
42
+ when ::Proc then assert(expected.call(value), error_message)
43
+ when ::Regexp then assert_match(expected, value, error_message)
44
+ when ::NilClass then assert_nil(value, error_message)
45
+ else assert_equal(expected, value, error_message)
46
+ end
47
+ end
48
+
49
+ hash
50
+ end
51
+
52
+ def all(hash, spec)
53
+ Keys.require_all(spec.keys, hash)
54
+
55
+ any(hash, spec)
56
+ end
57
+
58
+ private
59
+
60
+ def assert_equal(expected, value, message)
61
+ raise_kind_error("#{message}. Expected: #{expected}, Given: #{value}") if expected != value
62
+ end
63
+
64
+ def assert(value, message)
65
+ raise_kind_error(message) unless value
66
+ end
67
+
68
+ def assert_nil(value, message)
69
+ raise_kind_error("#{message}. Expected: nil") unless value.nil?
70
+ end
71
+
72
+ def assert_match(expected, value, message)
73
+ STRICT.kind_of(String, value)
74
+
75
+ raise_kind_error("#{message}. Expected: #{expected}") if value !~ expected
76
+ end
77
+
78
+ def assert_kind_of(expected, value, message)
79
+ raise_kind_error("#{message}. Expected: #{expected}") unless expected === value
80
+ end
81
+
82
+ def assert_kind_object(expected, value, message)
83
+ raise_kind_error("#{message}. Expected: #{expected.name}") unless expected === value
84
+ end
85
+
86
+
87
+ def raise_kind_error(message)
88
+ raise Error.new(message)
89
+ end
90
+ end
91
+ end
92
+ end
@@ -6,14 +6,16 @@ module Kind
6
6
  module STRICT
7
7
  extend self
8
8
 
9
+ require 'kind/__lib__/assert_hash'
10
+
9
11
  def error(kind_name, value, label = nil) # :nodoc:
10
12
  raise Error.new(kind_name, value, label: label)
11
13
  end
12
14
 
13
- def object_is_a(kind, value, label = nil) # :nodoc:
15
+ def object_is_a(kind, value, label = nil, expected = nil) # :nodoc:
14
16
  return value if kind === value
15
17
 
16
- error(kind.name, value, label)
18
+ error(expected || kind.name, value, label)
17
19
  end
18
20
 
19
21
  def kind_of(kind, value, kind_name = nil) # :nodoc:
@@ -43,6 +45,46 @@ module Kind
43
45
 
44
46
  raise Error.new("#{label_text}expected to not be nil")
45
47
  end
48
+
49
+ def in!(list, value)
50
+ return value if list.include?(value)
51
+
52
+ raise Error.new("#{value} expected to be included in #{list.inspect}")
53
+ end
54
+
55
+ def assert_hash!(hash, options)
56
+ check_keys = options.key?(:keys)
57
+ check_schema = options.key?(:schema)
58
+
59
+ raise ArgumentError, ':keys or :schema is missing' if !check_keys && !check_schema
60
+ raise ArgumentError, "hash can't be empty" if hash.empty?
61
+
62
+ require_all = options[:require_all]
63
+
64
+ return assert_hash_keys!(hash, options[:keys], require_all) if check_keys
65
+
66
+ assert_hash_schema!(hash, options[:schema], require_all)
67
+ end
68
+
69
+ private
70
+
71
+ def assert_hash_keys!(hash, arg, require_all)
72
+ keys = Array(arg)
73
+
74
+ AssertHash::Keys.require_all(keys, hash) if require_all
75
+
76
+ hash.each_key do |k|
77
+ unless keys.include?(k)
78
+ raise ArgumentError.new("Unknown key: #{k.inspect}. Valid keys are: #{keys.map(&:inspect).join(', ')}")
79
+ end
80
+ end
81
+ end
82
+
83
+ def assert_hash_schema!(hash, schema, require_all)
84
+ return AssertHash::Schema.all(hash, schema) if require_all
85
+
86
+ AssertHash::Schema.any(hash, schema)
87
+ end
46
88
  end
47
89
 
48
90
  private_constant :STRICT
data/lib/kind/any.rb ADDED
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kind
4
+ class Any
5
+ FilledArray = ->(value) {value.is_a?(::Array) && !value.empty?}
6
+
7
+ singleton_class.send(:alias_method, :[], :new)
8
+
9
+ attr_reader :values
10
+
11
+ def initialize(*args)
12
+ array = args.size == 1 ? args[0] : args
13
+
14
+ @values = Kind.of(FilledArray, array, expected: 'filled array')
15
+ end
16
+
17
+ def ===(other)
18
+ @values.any? { |value| value == other }
19
+ end
20
+
21
+ def [](value, label: nil)
22
+ STRICT.object_is_a(self, value, label)
23
+ end
24
+
25
+ def |(another_kind)
26
+ UnionType[self] | another_kind
27
+ end
28
+
29
+ def name
30
+ "Kind::Any#{@values}"
31
+ end
32
+
33
+ alias inspect name
34
+
35
+ private_constant :FilledArray
36
+ end
37
+ end
data/lib/kind/basic.rb CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  require 'kind/version'
4
4
 
5
- require 'kind/__lib__/kind'
6
5
  require 'kind/__lib__/undefined'
6
+ require 'kind/__lib__/kind'
7
7
 
8
8
  require 'kind/basic/undefined'
9
9
  require 'kind/basic/error'
@@ -48,8 +48,8 @@ module Kind
48
48
  KIND.interface?(method_names, value)
49
49
  end
50
50
 
51
- def of(kind, value, label: nil)
52
- STRICT.object_is_a(kind, value, label)
51
+ def of(kind, value, label: nil, expected: nil)
52
+ STRICT.object_is_a(kind, value, label, expected)
53
53
  end
54
54
 
55
55
  alias_method :of!, :of
@@ -76,4 +76,20 @@ module Kind
76
76
  def value(kind, value, default:)
77
77
  KIND.value(kind, value, of(kind, default))
78
78
  end
79
+
80
+ def or_nil(kind, value)
81
+ return value if kind === value
82
+ end
83
+
84
+ def in!(list, value)
85
+ STRICT.in!(list, value)
86
+ end
87
+
88
+ def include!(value, list)
89
+ STRICT.in!(list, value)
90
+ end
91
+
92
+ def assert_hash!(hash, **kargs)
93
+ STRICT.assert_hash!(hash, kargs)
94
+ end
79
95
  end
@@ -33,7 +33,7 @@ module Kind
33
33
  end
34
34
 
35
35
  def key?(arg)
36
- arg.respond_to?(:to_sym) ? ref?(arg) && !value?(arg) : false
36
+ arg.respond_to?(:to_sym) ? ENUM__KEYS.member?(arg.to_s) : false
37
37
  end
38
38
 
39
39
  def value?(arg)
data/lib/kind/enum.rb CHANGED
@@ -11,50 +11,58 @@ module Kind
11
11
  extend self
12
12
 
13
13
  def values(input)
14
- enum_module = ::Module.new
14
+ __create(input)
15
+ end
15
16
 
16
- enum_items =
17
- case input
18
- when ::Hash then create_from_hash(input)
19
- when ::Array then create_from_array(input)
20
- else raise ArgumentError, 'use an array or hash to define a Kind::Enum'
21
- end
17
+ def from_array(input, use_index_as_value:)
18
+ __create(input, use_index_as_value)
19
+ end
22
20
 
23
- enum_items.each { |item| enum_module.const_set(item.name, item) }
21
+ private
24
22
 
25
- enum_map = enum_items.each_with_object({}) do |item, memo|
26
- memo[item.to_s] = item
27
- memo[item.value] = item
28
- memo[item.to_sym] = item
29
- end
23
+ def __create(input, use_index_as_value = true)
24
+ enum_module = ::Module.new
30
25
 
31
- enum_module.const_set(:ENUM__MAP, enum_map)
32
- enum_module.const_set(:ENUM__HASH, enum_items.map(&:to_ary).to_h.freeze)
33
- enum_module.const_set(:ENUM__KEYS, ::Set.new(enum_items.map(&:key)).freeze)
34
- enum_module.const_set(:ENUM__VALS, ::Set.new(enum_items.map(&:value)).freeze)
35
- enum_module.const_set(:ENUM__REFS, ::Set.new(enum_map.keys))
36
- enum_module.const_set(:ENUM__ITEMS, enum_items.freeze)
26
+ enum_items =
27
+ case input
28
+ when ::Hash then __create_from_hash(input)
29
+ when ::Array then __create_from_array(input, use_index_as_value)
30
+ else raise ArgumentError, 'use an array or hash to define a Kind::Enum'
31
+ end
37
32
 
38
- enum_module.send(:private_constant, :ENUM__MAP, :ENUM__HASH, :ENUM__KEYS,
39
- :ENUM__VALS, :ENUM__REFS, :ENUM__ITEMS)
33
+ enum_items.each { |item| enum_module.const_set(item.name, item) }
40
34
 
41
- enum_module.module_eval(METHODS)
35
+ enum_map = enum_items.each_with_object({}) do |item, memo|
36
+ memo[item.to_s] = item
37
+ memo[item.value] = item
38
+ memo[item.to_sym] = item
39
+ end
42
40
 
43
- enum_module.extend(enum_module)
44
- enum_module
45
- end
41
+ enum_module.const_set(:ENUM__MAP, enum_map)
42
+ enum_module.const_set(:ENUM__HASH, enum_items.map(&:to_ary).to_h.freeze)
43
+ enum_module.const_set(:ENUM__KEYS, ::Set.new(enum_items.map(&:key)).freeze)
44
+ enum_module.const_set(:ENUM__VALS, ::Set.new(enum_items.map(&:value)).freeze)
45
+ enum_module.const_set(:ENUM__REFS, ::Set.new(enum_map.keys))
46
+ enum_module.const_set(:ENUM__ITEMS, enum_items.freeze)
46
47
 
47
- private
48
+ enum_module.send(:private_constant, :ENUM__MAP, :ENUM__HASH, :ENUM__KEYS,
49
+ :ENUM__VALS, :ENUM__REFS, :ENUM__ITEMS)
50
+
51
+ enum_module.module_eval(METHODS)
52
+
53
+ enum_module.extend(enum_module)
54
+ enum_module
55
+ end
48
56
 
49
- def create_from_hash(input)
50
- input.map { |key, value| build_item(key, value) }
57
+ def __create_from_hash(input)
58
+ input.map { |key, value| __item(key, value) }
51
59
  end
52
60
 
53
- def create_from_array(input)
54
- input.map.with_index { |key, index| build_item(key, index) }
61
+ def __create_from_array(input, use_index_as_value)
62
+ input.map.with_index { |key, index| use_index_as_value ? __item(key, index) : __item(key, key) }
55
63
  end
56
64
 
57
- def build_item(key, value)
65
+ def __item(key, value)
58
66
  return Item.new(key, value) if key.respond_to?(:to_sym)
59
67
 
60
68
  raise ArgumentError, 'use a string or symbol to define a Kind::Enum item'
@@ -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)
@@ -3,11 +3,17 @@
3
3
  module Kind
4
4
  module STRICT
5
5
  [
6
- :object_is_a, :class!, :kind_of,
7
- :module_or_class, :module!, :not_nil
6
+ :object_is_a,
7
+ :class!,
8
+ :kind_of,
9
+ :module_or_class,
10
+ :module!,
11
+ :not_nil,
12
+ :in!,
13
+ :assert_hash!
8
14
  ].each { |method_name| remove_method(method_name) }
9
15
 
10
- def object_is_a(_kind, value, _label = nil) # :nodoc:
16
+ def object_is_a(_kind, value, _label = nil, _expected = nil) # :nodoc:
11
17
  value
12
18
  end
13
19
 
@@ -30,5 +36,17 @@ module Kind
30
36
  def not_nil(value, label) # :nodoc:
31
37
  value
32
38
  end
39
+
40
+ def in!(list, value) # :nodoc:
41
+ value
42
+ end
43
+
44
+ def assert_hash!(hash, _options) # :nodoc:
45
+ hash
46
+ end
47
+
48
+ def assert_hash!(hash, _options) # :nodoc:
49
+ hash
50
+ end
33
51
  end
34
52
  end
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.4.1'
4
+ VERSION = '5.8.0'
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kind
3
3
  version: !ruby/object:Gem::Version
4
- version: 5.4.1
4
+ version: 5.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rodrigo Serradura
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-03-26 00:00:00.000000000 Z
11
+ date: 2021-09-22 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A development toolkit for Ruby with several small/cohesive abstractions
14
14
  (monads, enums, business logic, data validation...) to empower your development
@@ -19,10 +19,10 @@ executables: []
19
19
  extensions: []
20
20
  extra_rdoc_files: []
21
21
  files:
22
+ - ".github/workflows/ci.yml"
22
23
  - ".gitignore"
23
24
  - ".tool-versions"
24
- - ".travis.sh"
25
- - ".travis.yml"
25
+ - ".vscode/settings.json"
26
26
  - CHANGELOG.md
27
27
  - CODE_OF_CONDUCT.md
28
28
  - Gemfile
@@ -30,10 +30,13 @@ files:
30
30
  - README.md
31
31
  - Rakefile
32
32
  - bin/console
33
+ - bin/prepare_coverage
33
34
  - bin/setup
35
+ - bin/test
34
36
  - kind.gemspec
35
37
  - lib/kind.rb
36
38
  - lib/kind/__lib__/action_steps.rb
39
+ - lib/kind/__lib__/assert_hash.rb
37
40
  - lib/kind/__lib__/attributes.rb
38
41
  - lib/kind/__lib__/kind.rb
39
42
  - lib/kind/__lib__/of.rb
@@ -41,6 +44,7 @@ files:
41
44
  - lib/kind/__lib__/undefined.rb
42
45
  - lib/kind/action.rb
43
46
  - lib/kind/active_model/validation.rb
47
+ - lib/kind/any.rb
44
48
  - lib/kind/basic.rb
45
49
  - lib/kind/basic/error.rb
46
50
  - lib/kind/basic/undefined.rb
@@ -119,14 +123,13 @@ files:
119
123
  - lib/kind/try.rb
120
124
  - lib/kind/validator.rb
121
125
  - lib/kind/version.rb
122
- - test.sh
123
126
  homepage: https://github.com/serradura/kind
124
127
  licenses:
125
128
  - MIT
126
129
  metadata:
127
130
  homepage_uri: https://github.com/serradura/kind
128
131
  source_code_uri: https://github.com/serradura/kind
129
- post_install_message:
132
+ post_install_message:
130
133
  rdoc_options: []
131
134
  require_paths:
132
135
  - lib
@@ -142,7 +145,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
142
145
  version: '0'
143
146
  requirements: []
144
147
  rubygems_version: 3.2.11
145
- signing_key:
148
+ signing_key:
146
149
  specification_version: 4
147
150
  summary: A development toolkit for Ruby with several small/cohesive abstractions to
148
151
  empower your development workflow.
data/.travis.sh DELETED
@@ -1,77 +0,0 @@
1
- #!/bin/bash
2
-
3
- RUBY_V=$(ruby -v)
4
-
5
- function run_basic_tests {
6
- if [ ! -z "$1" ]; then
7
- bundle_cmd="bundle _$1_"
8
- else
9
- bundle_cmd="bundle"
10
- fi
11
-
12
- eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/{basic/*_test,basic_test}.rb'"
13
- eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/enum_test.rb'"
14
- eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/presence_test.rb'"
15
- eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/dig_test.rb'"
16
- eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/try_test.rb'"
17
- eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/maybe_test.rb'"
18
- eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/immutable_attributes_test.rb'"
19
- eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/function_test.rb'"
20
- eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/action_test.rb'"
21
- eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/{functional/*_test,functional_test}.rb'"
22
- eval "KIND_BASIC=t $bundle_cmd exec rake test TEST='test/kind/either/*_test.rb'"
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'"
26
- }
27
-
28
- function run_with_bundler {
29
- rm Gemfile.lock
30
-
31
- if [ ! -z "$1" ]; then
32
- bundle_cmd="bundle _$1_"
33
- else
34
- bundle_cmd="bundle"
35
- fi
36
-
37
- eval "$2 $bundle_cmd update"
38
- eval "$2 $bundle_cmd exec rake test"
39
- }
40
-
41
- function run_with_am_version_and_bundler {
42
- run_with_bundler "$2" "ACTIVEMODEL_VERSION=$1"
43
- }
44
-
45
- RUBY_2_12="ruby 2.[12]."
46
- RUBY_2_2345="ruby 2.[2345]."
47
- RUBY_2_12345="ruby 2.[12345]."
48
- RUBY_2_567="ruby 2.[567]."
49
- RUBY_3_0="ruby 3.0."
50
-
51
- if [[ $RUBY_V =~ $RUBY_2_12 ]]; then
52
- run_with_am_version_and_bundler "3.2" "$BUNDLER_V1"
53
- fi
54
-
55
- if [[ $RUBY_V =~ $RUBY_2_2345 ]]; then
56
- run_with_am_version_and_bundler "4.0" "$BUNDLER_V1"
57
- run_with_am_version_and_bundler "4.1" "$BUNDLER_V1"
58
- run_with_am_version_and_bundler "4.2" "$BUNDLER_V1"
59
- run_with_am_version_and_bundler "5.0" "$BUNDLER_V1"
60
- run_with_am_version_and_bundler "5.1" "$BUNDLER_V1"
61
- run_with_am_version_and_bundler "5.2" "$BUNDLER_V1"
62
- fi
63
-
64
- if [[ $RUBY_V =~ $RUBY_2_12345 ]]; then
65
- run_basic_tests "$BUNDLER_V1"
66
- run_with_bundler "$BUNDLER_V1"
67
- fi
68
-
69
- if [[ $RUBY_V =~ $RUBY_2_567 ]] || [[ $RUBY_V =~ $RUBY_3_0 ]]; then
70
- gem install bundler -v ">= 2" --no-doc
71
-
72
- run_with_am_version_and_bundler "6.0"
73
- run_with_am_version_and_bundler "6.1"
74
-
75
- run_basic_tests
76
- run_with_bundler
77
- fi
data/.travis.yml DELETED
@@ -1,35 +0,0 @@
1
- ---
2
- language: ruby
3
-
4
- cache:
5
- bundler: true
6
- directories:
7
- - /home/travis/.rvm/
8
-
9
- rvm:
10
- - 2.1.10
11
- - 2.2.2
12
- - 2.3.0
13
- - 2.4.0
14
- - 2.5.0
15
- - 2.6.0
16
- - 2.7.0
17
- - 3.0.0
18
-
19
- env:
20
- - BUNDLER_V1="1.17.3"
21
-
22
- before_install:
23
- - gem install bundler -v "$BUNDLER_V1"
24
-
25
- install: bundle install --jobs=3 --retry=3
26
-
27
- before_script:
28
- - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
29
- - chmod +x ./cc-test-reporter
30
- - "./cc-test-reporter before-build"
31
-
32
- script: "./.travis.sh"
33
-
34
- after_success:
35
- - "./cc-test-reporter after-build -t simplecov"
data/test.sh DELETED
@@ -1,11 +0,0 @@
1
- #!/bin/bash
2
-
3
- source $(dirname $0)/.travis.sh
4
-
5
- echo ''
6
- echo 'Resetting Gemfile'
7
- echo ''
8
-
9
- rm Gemfile.lock
10
-
11
- bundle