kind 5.5.0 → 5.8.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9e91ad1bc49fe0ca0e32f62bf55da4c6fa6aa8a54372aeeda0894cc76ac93c18
4
- data.tar.gz: 241a52a87f1492c6b952687e3105f3b95c90f3ac30c2810da3911414737861cd
3
+ metadata.gz: 9f597d5c2bf74605429011719cb548759b2acb223a0947f4d26137ad02ade075
4
+ data.tar.gz: 66e68c83721e37746d3102bed5befe2e7e0d3c30fd2e0ff02bc30007114620dc
5
5
  SHA512:
6
- metadata.gz: 7b2ba35383908bac2542bb93afabb2cc0385a06556a184101dd7da21983bb55ee548ab539f609e8604dcbceecdea6d6b67f26dd8323f1404dee5eca308951bcb
7
- data.tar.gz: 9500bf6cc0484ae417ddea388ca670a4f615931c32ca24f82ae6798ac57bae9e9e80e663aca9c19d837a1f82529c2127a96effc546d13b175fdb3f12456854fe
6
+ metadata.gz: 856b289e62376c59e570c1de277e976e9434040dedd12cbaaca7ef8a71aa1c33ba1d2c304a3c5b7634830326838ac58ace2c5647d8c5b36a9df1638c70e67385
7
+ data.tar.gz: 831252cae86eee16ae082a9f64f5065ed2dfcb33ae2df904517cce929b81a7951fd844f895fce1a44b30d60901e36f4642c74dbd9b861d9de76b9ef72146acc4
@@ -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,81 +3,89 @@
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.5.0 (2021-04-05)](#550-2021-04-05)
6
+ - [5.8.1 (2021-09-22)](#581-2021-09-22)
7
+ - [Fixed](#fixed)
8
+ - [5.8.0 (2021-09-22)](#580-2021-09-22)
7
9
  - [Added](#added)
10
+ - [5.7.0 (2021-06-22)](#570-2021-06-22)
11
+ - [Added](#added-1)
12
+ - [5.6.0 (2021-05-14)](#560-2021-05-14)
13
+ - [Added](#added-2)
14
+ - [5.5.0 (2021-04-05)](#550-2021-04-05)
15
+ - [Added](#added-3)
8
16
  - [5.4.1 (2021-03-26)](#541-2021-03-26)
9
- - [Fixed](#fixed)
17
+ - [Fixed](#fixed-1)
10
18
  - [5.4.0 (2021-03-25)](#540-2021-03-25)
11
- - [Added](#added-1)
19
+ - [Added](#added-4)
12
20
  - [5.3.0 (2021-03-23)](#530-2021-03-23)
13
- - [Added](#added-2)
21
+ - [Added](#added-5)
14
22
  - [5.2.0 (2021-03-17)](#520-2021-03-17)
15
- - [Added](#added-3)
23
+ - [Added](#added-6)
16
24
  - [Deprecated](#deprecated)
17
25
  - [Changes](#changes)
18
26
  - [5.1.0 (2021-02-23)](#510-2021-02-23)
19
- - [Added](#added-4)
27
+ - [Added](#added-7)
20
28
  - [Deprecated](#deprecated-1)
21
29
  - [5.0.0 (2021-02-22)](#500-2021-02-22)
22
30
  - [Breaking Changes](#breaking-changes)
23
31
  - [Removed](#removed)
24
32
  - [4.1.0 (2021-02-22)](#410-2021-02-22)
25
- - [Added](#added-5)
33
+ - [Added](#added-8)
26
34
  - [4.0.0 (2021-02-22)](#400-2021-02-22)
27
- - [Added](#added-6)
35
+ - [Added](#added-9)
28
36
  - [Deprecated](#deprecated-2)
29
- - [Fixed](#fixed-1)
37
+ - [Fixed](#fixed-2)
30
38
  - [3.1.0 (2020-07-08)](#310-2020-07-08)
31
- - [Added](#added-7)
39
+ - [Added](#added-10)
32
40
  - [3.0.0 (2020-06-25)](#300-2020-06-25)
33
41
  - [Breaking Changes](#breaking-changes-1)
34
- - [Added](#added-8)
42
+ - [Added](#added-11)
35
43
  - [2.3.0 (2020-06-24)](#230-2020-06-24)
36
- - [Added](#added-9)
44
+ - [Added](#added-12)
37
45
  - [2.2.0 (2020-06-23)](#220-2020-06-23)
38
- - [Added](#added-10)
46
+ - [Added](#added-13)
39
47
  - [2.1.0 (2020-05-12)](#210-2020-05-12)
40
- - [Added](#added-11)
48
+ - [Added](#added-14)
41
49
  - [Breaking Changes](#breaking-changes-2)
42
50
  - [2.0.0 (2020-05-07)](#200-2020-05-07)
43
- - [Added](#added-12)
51
+ - [Added](#added-15)
44
52
  - [Breaking Changes](#breaking-changes-3)
45
53
  - [Removed](#removed-1)
46
54
  - [1.9.0 (2020-05-06)](#190-2020-05-06)
47
- - [Added](#added-13)
55
+ - [Added](#added-16)
48
56
  - [1.8.0 (2020-05-03)](#180-2020-05-03)
49
- - [Added](#added-14)
57
+ - [Added](#added-17)
50
58
  - [1.7.0 (2020-05-03)](#170-2020-05-03)
51
- - [Fixed](#fixed-2)
59
+ - [Fixed](#fixed-3)
52
60
  - [1.6.0 (2020-04-17)](#160-2020-04-17)
53
- - [Added](#added-15)
61
+ - [Added](#added-18)
54
62
  - [Changes](#changes-1)
55
63
  - [1.5.0 (2020-04-12)](#150-2020-04-12)
56
- - [Added](#added-16)
64
+ - [Added](#added-19)
57
65
  - [1.4.0 (2020-04-12)](#140-2020-04-12)
58
- - [Added](#added-17)
66
+ - [Added](#added-20)
59
67
  - [1.3.0 (2020-04-12)](#130-2020-04-12)
60
- - [Added](#added-18)
68
+ - [Added](#added-21)
61
69
  - [1.2.0 (2020-04-12)](#120-2020-04-12)
62
- - [Added](#added-19)
70
+ - [Added](#added-22)
63
71
  - [1.1.0 (2020-04-09)](#110-2020-04-09)
64
- - [Added](#added-20)
65
- - [Fixed](#fixed-3)
72
+ - [Added](#added-23)
73
+ - [Fixed](#fixed-4)
66
74
  - [1.0.0 (2020-03-16)](#100-2020-03-16)
67
- - [Added](#added-21)
75
+ - [Added](#added-24)
68
76
  - [0.6.0 (2020-01-06)](#060-2020-01-06)
69
- - [Added](#added-22)
77
+ - [Added](#added-25)
70
78
  - [0.5.0 (2020-01-04)](#050-2020-01-04)
71
- - [Added](#added-23)
79
+ - [Added](#added-26)
72
80
  - [0.4.0 (2020-01-03)](#040-2020-01-03)
73
- - [Added](#added-24)
81
+ - [Added](#added-27)
74
82
  - [0.3.0 (2020-01-03)](#030-2020-01-03)
75
- - [Added](#added-25)
83
+ - [Added](#added-28)
76
84
  - [Breaking Changes](#breaking-changes-4)
77
85
  - [0.2.0 (2020-01-02)](#020-2020-01-02)
78
- - [Added](#added-26)
86
+ - [Added](#added-29)
79
87
  - [0.1.0 (2019-12-26)](#010-2019-12-26)
80
- - [Added](#added-27)
88
+ - [Added](#added-30)
81
89
 
82
90
  ## Unreleased
83
91
 
@@ -89,6 +97,175 @@ This project follows [semver 2.0.0](http://semver.org/spec/v2.0.0.html) and the
89
97
  ### Fixed
90
98
  -->
91
99
 
100
+ 5.8.1 (2021-09-22)
101
+ ------------------
102
+
103
+ ### Fixed
104
+
105
+ * [#67](https://github.com/serradura/kind/pull/67) - Make `Kind.assert_hash!(some_hash, schema:)` works with a `Kind::Any` instance.
106
+ ```ruby
107
+ require 'kind/any'
108
+
109
+ Level = Kind::Any[:low, :high]
110
+
111
+ Kind.assert_hash!({level: :medium}, schema: {level: Level})
112
+ # Kind::Error (The key :status has an invalid value. Expected: Kind::Any[:low, :high])
113
+ ```
114
+
115
+ [⬆️  Back to Top](#changelog-)
116
+
117
+ 5.8.0 (2021-09-22)
118
+ ------------------
119
+
120
+ ### Added
121
+
122
+ * [#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.
123
+ ```ruby
124
+ require 'kind/any'
125
+
126
+ Level = Kind::Any[:low, :high] # or Kind::Any.new([:low, :high])
127
+
128
+ Level === :low # true
129
+ Level === :high # true
130
+
131
+ Level === :foo # false
132
+
133
+ Level[:low] # :low
134
+ Level[:high] # :high
135
+
136
+ Level[:foo] # Kind::Error (:foo expected to be a kind of Kind::Any[:low, :high])
137
+
138
+ level_or_any_symbol = # (Kind::Any[:low, :high] | Symbol)
139
+
140
+ Level.name # 'Kind::Any[:low, :high]'
141
+ Level.inspect # 'Kind::Any[:low, :high]'
142
+
143
+ Level.values # [:low, :high]
144
+ ```
145
+
146
+ * [#66](https://github.com/serradura/kind/pull/66) - Make `Kind.assert_hash!(hash, schema:)` works with a `Kind::Object`.
147
+ ```ruby
148
+ require 'kind/enum'
149
+
150
+ module Level
151
+ include Kind::Enum.from_array([:low, :medium, :high], use_index_as_value: false)
152
+ end
153
+
154
+ Level.keys # ["low", "medium", "high"]
155
+ Level.values # [:low, :medium, :high]
156
+
157
+ # ---
158
+
159
+ module Status
160
+ include Kind::Enum.from_array([:open, :closed], use_index_as_value: true)
161
+ end
162
+
163
+ Status.keys # ["open", "closed"]
164
+ Status.values # [0, 1]
165
+ ```
166
+
167
+ * [#66](https://github.com/serradura/kind/pull/66) - Make `Kind.assert_hash!(hash, schema:)` works with a `Kind::Object`.
168
+ ```ruby
169
+ FilledString = begin
170
+ filled_string = ->(value) {value.is_a?(String) && value.present?}
171
+
172
+ Kind::Of(filled_string, name: 'FilledString')
173
+ end
174
+
175
+ Kind.assert_hash!(some_hash, schema: {
176
+ string: FilledString,
177
+ callable: Kind::Callable,
178
+ })
179
+ ```
180
+
181
+ * [#66](https://github.com/serradura/kind/pull/66) - Improve the exception messages of `Kind.assert_hash!(hash, schema:)`.
182
+ ```ruby
183
+ Kind.assert_hash!({status: 1}, schema: {status: Kind::String | Symbol})
184
+ # Kind::Error (The key :status has an invalid value. Expected: (String | Symbol))
185
+
186
+ Kind.assert_hash!({status: 'closed'}, schema: {status: 'active'})
187
+ # Kind::Error (The key :status has an invalid value. Expected: active, Given: closed)
188
+
189
+ Kind.assert_hash!({callable: 1}, schema: {callable: Kind::Callable})
190
+ # Kind::Error (The key :callable has an invalid value. Expected: Callable)
191
+ ```
192
+
193
+ * [#66](https://github.com/serradura/kind/pull/66) - Make `Kind.assert_hash!(hash, **options)` raises an error if the given hash be empty.
194
+ ```ruby
195
+ Kind.assert_hash!({}, keys: []) # ArgumentError (hash can't be empty)
196
+ Kind.assert_hash!({}, schema: {}) # ArgumentError (hash can't be empty)
197
+ ```
198
+
199
+ [⬆️  Back to Top](#changelog-)
200
+
201
+ 5.7.0 (2021-06-22)
202
+ ------------------
203
+
204
+ ### Added
205
+
206
+ * [#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.
207
+ ```ruby
208
+ h1 = {a: 1, b: 1}
209
+
210
+ Kind.assert_hash!(h1, keys: [:a, :b])
211
+ Kind.assert_hash!(h1, keys: [:a]) # ArgumentError (Unknown key: :b. Valid keys are: :a)
212
+
213
+ # --
214
+
215
+ h2 = {'a' => 1, 'b' => 2}
216
+
217
+ Kind.assert_hash!(h2, keys: ['a', 'b'])
218
+ ```
219
+
220
+ * [#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.
221
+ ```ruby
222
+ hash = {hash: {}, array: [], number: 1, string: 'foo', email: 'bar@bar.com', null: nil}
223
+
224
+ Kind.assert_hash!(hash, schema: {
225
+ hash: {},
226
+ array: [],
227
+ email: 'bar@bar.com',
228
+ string: 'foo',
229
+ number: 1,
230
+ null: nil
231
+ })
232
+
233
+ Kind.assert_hash!(hash, schema: {
234
+ hash: Enumerable,
235
+ array: Enumerable,
236
+ email: /\A.+@.+\..+\z/,
237
+ string: String
238
+ })
239
+
240
+ Kind.assert_hash!(hash, schema: {
241
+ hash: Hash,
242
+ array: Array,
243
+ email: String,
244
+ string: String
245
+ })
246
+
247
+ Kind.assert_hash!(h1, schema: {
248
+ email: ->(value) { value.is_a?(String) && value.include?('@') }
249
+ })
250
+ ```
251
+
252
+ [⬆️  Back to Top](#changelog-)
253
+
254
+ 5.6.0 (2021-05-14)
255
+ ------------------
256
+
257
+ ### Added
258
+
259
+ * [#57](https://github.com/serradura/kind/pull/57) - Allow the usage of `nil` to define union types.
260
+ ```ruby
261
+ (Kind::String | nil) === '' # true
262
+ (Kind::String | nil) === nil # true
263
+
264
+ (Kind::String | nil) === {} # false
265
+ ```
266
+
267
+ [⬆️  Back to Top](#changelog-)
268
+
92
269
  5.5.0 (2021-04-05)
93
270
  ------------------
94
271
 
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.5.0 | https://github.com/serradura/kind/blob/v5.x/README.md
45
+ 5.8.1 | 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.5.0 | v5.x | >= 2.1.0, <= 3.0.0 | >= 3.2, < 7.0 |
128
+ | 5.8.1 | 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,88 @@
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
+ KindAny = ->(value) { defined?(Kind::Any) ? Kind::Any === value : false }
27
+ KindObject = ->(value) { defined?(Kind::Object) ? Kind::Object === value : false }
28
+ KindUnionType = ->(value) { defined?(Kind::UnionType) ? Kind::UnionType === value : false }
29
+
30
+ def any(hash, spec)
31
+ spec.each do |key, expected|
32
+ value = hash[key]
33
+ error_message = "The key #{key.inspect} has an invalid value"
34
+
35
+ case expected
36
+ when KindAny, KindObject, KindUnionType then assert_kind_object(expected, value, error_message)
37
+ when ::Module then assert_kind_of(expected, value, error_message)
38
+ when ::Proc then assert(expected.call(value), error_message)
39
+ when ::Regexp then assert_match(expected, value, error_message)
40
+ when ::NilClass then assert_nil(value, error_message)
41
+ else assert_equal(expected, value, error_message)
42
+ end
43
+ end
44
+
45
+ hash
46
+ end
47
+
48
+ def all(hash, spec)
49
+ Keys.require_all(spec.keys, hash)
50
+
51
+ any(hash, spec)
52
+ end
53
+
54
+ private
55
+
56
+ def assert_equal(expected, value, message)
57
+ raise_kind_error("#{message}. Expected: #{expected.inspect}, Given: #{value.inspect}") if expected != value
58
+ end
59
+
60
+ def assert(value, message)
61
+ raise_kind_error(message) unless value
62
+ end
63
+
64
+ def assert_nil(value, message)
65
+ raise_kind_error("#{message}. Expected: nil") unless value.nil?
66
+ end
67
+
68
+ def assert_match(expected, value, message)
69
+ STRICT.kind_of(String, value)
70
+
71
+ raise_kind_error("#{message}. Expected: #{expected.inspect}") if value !~ expected
72
+ end
73
+
74
+ def assert_kind_of(expected, value, message)
75
+ raise_kind_error("#{message}. Expected: #{expected.inspect}") unless expected === value
76
+ end
77
+
78
+ def assert_kind_object(expected, value, message)
79
+ raise_kind_error("#{message}. Expected: #{expected.name}") unless expected === value
80
+ end
81
+
82
+
83
+ def raise_kind_error(message)
84
+ raise Error.new(message)
85
+ end
86
+ end
87
+ end
88
+ 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
@@ -80,4 +80,16 @@ module Kind
80
80
  def or_nil(kind, value)
81
81
  return value if kind === value
82
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
83
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.5.0'
4
+ VERSION = '5.8.1'
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.5.0
4
+ version: 5.8.1
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-04-06 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