kind 5.4.1 → 5.8.0

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: 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