obvious 0.0.8 → 0.2.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 +7 -0
- data/.github/workflows/obvious.yml +19 -0
- data/.gitignore +0 -1
- data/CHANGELOG.md +19 -0
- data/Gemfile.lock +19 -0
- data/README.md +4 -33
- data/Rakefile +8 -0
- data/lib/obvious/contract.rb +154 -144
- data/lib/obvious/obj.rb +36 -0
- data/lib/obvious/version.rb +1 -1
- data/lib/obvious.rb +1 -1
- data/obvious.gemspec +5 -7
- data/test/contract_test.rb +62 -0
- data/test/entity_test.rb +76 -0
- data/test/obj_test.rb +65 -0
- metadata +26 -48
- data/bin/obvious +0 -7
- data/lib/generators/application_generator.rb +0 -199
- data/lib/generators/descriptor.rb +0 -131
- data/lib/generators/helpers/application.rb +0 -47
- data/lib/obvious/files/Rakefile +0 -4
- data/lib/obvious/files/external/fs_plug.rb +0 -99
- data/lib/obvious/files/external/mongo_plug.rb +0 -62
- data/lib/obvious/files/external/mysql_plug.rb +0 -38
- data/lib/obvious/files/external/s3_plug.rb +0 -94
- data/spec/.spec_helper.rb.swp +0 -0
- data/spec/contract_spec.rb +0 -63
- data/spec/entity_spec.rb +0 -75
- data/spec/generators/descriptor_spec.rb +0 -34
- data/spec/spec_helper.rb +0 -3
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 9a0c1346518213e685185d08916b2c573697ca5d6be5763f8cfe245febeac4f0
|
4
|
+
data.tar.gz: 2125df9f05284d2e34b16edb85ee14f73d1d1f2a296df90c7a2b4e2a795ab81f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1f8684fe2bc370eb3d2cec4933e3281a00b0633a5829cdee580b36632d330e6b71ba605612bd1ef5f5cfa6884ed66369a50117f68d3b1a9a9ec73a0c3e5bdfda
|
7
|
+
data.tar.gz: f393f71805593ff5b748477ebe9486148c356fdb73b1fb09f108453611ceabf4cce982768914c10de719012b0782659bf57ab4e91357871173551d60fb4503ff
|
@@ -0,0 +1,19 @@
|
|
1
|
+
name: obvious
|
2
|
+
|
3
|
+
on: [push]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
unit-tests:
|
7
|
+
strategy:
|
8
|
+
matrix:
|
9
|
+
ruby: [ 2.7, '3.0', 3.1 ]
|
10
|
+
name: Ruby ${{ matrix.ruby }} unit tests
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
steps:
|
13
|
+
- uses: actions/checkout@v2
|
14
|
+
- uses: ruby/setup-ruby@v1
|
15
|
+
with:
|
16
|
+
ruby-version: ${{ matrix.ruby }}
|
17
|
+
bundler-cache: true
|
18
|
+
- run: |
|
19
|
+
ruby -Ilib:test test/*_test.rb
|
data/.gitignore
CHANGED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Changelog
|
2
|
+
|
3
|
+
All notable changes to this project will be documented in this file.
|
4
|
+
|
5
|
+
## [0.2.0]
|
6
|
+
|
7
|
+
### Added
|
8
|
+
|
9
|
+
### Changed
|
10
|
+
|
11
|
+
- Updated README to be current
|
12
|
+
- Removed generators from Obvious
|
13
|
+
- Refactored Obvious::Obj#define to be simpler
|
14
|
+
- Replaced RSPec with Minitest for testing
|
15
|
+
|
16
|
+
### Fixed
|
17
|
+
|
18
|
+
- Fixed namespace issues with Contract errors
|
19
|
+
- Updated project URL on Rubygems listing
|
data/Gemfile.lock
ADDED
data/README.md
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
# Obvious
|
2
2
|
|
3
|
-
Obvious is an architecture framework. The goal is to provide architectural
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
You can get a full explanation of Obvious at http://obvious.retromocha.com
|
3
|
+
Obvious is an architecture framework. The goal is to provide architectural
|
4
|
+
structure for a highly testable system that is obvious to understand and where
|
5
|
+
both the front end UI and back end infrastructure are treated as implementation
|
6
|
+
details independent of the app logic itself.
|
8
7
|
|
9
8
|
## Installation
|
10
9
|
|
@@ -15,31 +14,3 @@ Add this line to your application's Gemfile:
|
|
15
14
|
And then execute:
|
16
15
|
|
17
16
|
$ bundle
|
18
|
-
|
19
|
-
Or install it yourself as:
|
20
|
-
|
21
|
-
$ gem install obvious
|
22
|
-
|
23
|
-
## Usage
|
24
|
-
|
25
|
-
Obvious is designed to be used in two ways - as a gem you require in your Obvious projects and also as an Obvious project
|
26
|
-
generation tool.
|
27
|
-
|
28
|
-
The project generation tool is run by executing...
|
29
|
-
|
30
|
-
$ obvious generate
|
31
|
-
|
32
|
-
in a directory containing a decriptors directory. You can read more about descriptors on the Obvious page or see an example
|
33
|
-
in the Obvious Status example app: https://github.com/RetroMocha/obvious_status.
|
34
|
-
|
35
|
-
Currently the footprint of the Obvious library is quite small. The most important things defined so far are the Contract class
|
36
|
-
and the Hash.has_shape? method. The rest of what makes an Obvious app interesting is the structure itself, not the libraries Obvious
|
37
|
-
provides.
|
38
|
-
|
39
|
-
## Contributing
|
40
|
-
|
41
|
-
1. Fork it
|
42
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
43
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
44
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
45
|
-
5. Create new Pull Request
|
data/Rakefile
CHANGED
data/lib/obvious/contract.rb
CHANGED
@@ -1,166 +1,179 @@
|
|
1
|
-
|
2
|
-
@@disable_override = false
|
1
|
+
module Obvious
|
3
2
|
|
4
|
-
|
5
|
-
|
6
|
-
def self.contract_list
|
7
|
-
[]
|
8
|
-
end
|
9
|
-
|
10
|
-
# Public: Sets the contracts
|
11
|
-
#
|
12
|
-
# contracts - a list of contracts, as String or as Symbols.
|
13
|
-
#
|
14
|
-
# Examples
|
15
|
-
#
|
16
|
-
# class FooJackContract < Contract
|
17
|
-
# contracts :save, :get, :list
|
18
|
-
#
|
19
|
-
# end
|
20
|
-
#
|
21
|
-
# Returns Nothing.
|
22
|
-
def self.contracts *contracts
|
23
|
-
singleton_class.send :define_method, :contract_list do
|
24
|
-
contracts
|
25
|
-
end
|
26
|
-
end
|
3
|
+
class Contract
|
4
|
+
@@disable_override = false
|
27
5
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
#
|
33
|
-
# Examples
|
34
|
-
#
|
35
|
-
# class FooJackContract < Contract
|
36
|
-
#
|
37
|
-
# contract_for :save, {
|
38
|
-
# :input => Foo.shape,
|
39
|
-
# :output => Foo.shape,
|
40
|
-
# }
|
41
|
-
#
|
42
|
-
# end
|
43
|
-
#
|
44
|
-
def self.contract_for method, contract
|
45
|
-
method_alias = "#{method}_alias".to_sym
|
46
|
-
method_contract = "#{method}_contract".to_sym
|
47
|
-
|
48
|
-
define_method method_contract do |*args|
|
49
|
-
input = args[0]
|
50
|
-
call_method method_alias, input, contract[:input], contract[:output]
|
6
|
+
# Provides a default empty array for method_added
|
7
|
+
# Overriden by self.contracts
|
8
|
+
def self.contract_list
|
9
|
+
[]
|
51
10
|
end
|
52
11
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
self.send :alias_method, method_alias, name
|
69
|
-
self.send :remove_method, name
|
70
|
-
self.send :alias_method, name, method_contract
|
71
|
-
|
72
|
-
@@disable_override = false
|
73
|
-
else
|
74
|
-
# puts self.inspect
|
75
|
-
# puts "defining other method #{name}"
|
76
|
-
end
|
12
|
+
# Public: Sets the contracts
|
13
|
+
#
|
14
|
+
# contracts - a list of contracts, as String or as Symbols.
|
15
|
+
#
|
16
|
+
# Examples
|
17
|
+
#
|
18
|
+
# class FooJackContract < Contract
|
19
|
+
# contracts :save, :get, :list
|
20
|
+
#
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# Returns Nothing.
|
24
|
+
def self.contracts *contracts
|
25
|
+
singleton_class.send :define_method, :contract_list do
|
26
|
+
contracts
|
77
27
|
end
|
78
28
|
end
|
79
|
-
end
|
80
29
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
30
|
+
# Public: Defines a contract for a method
|
31
|
+
#
|
32
|
+
# method - a symbol representing the method name
|
33
|
+
# contract - a hash with the keys :input and :output holding the respective shapes.
|
34
|
+
#
|
35
|
+
# Examples
|
36
|
+
#
|
37
|
+
# class FooJackContract < Contract
|
38
|
+
#
|
39
|
+
# contract_for :save, {
|
40
|
+
# :input => Foo.shape,
|
41
|
+
# :output => Foo.shape,
|
42
|
+
# }
|
43
|
+
#
|
44
|
+
# end
|
45
|
+
#
|
46
|
+
def self.contract_for method, contract
|
47
|
+
method_alias = "#{method}_alias".to_sym
|
48
|
+
method_contract = "#{method}_contract".to_sym
|
49
|
+
|
50
|
+
define_method method_contract do |*args|
|
51
|
+
input = args[0]
|
52
|
+
call_method method_alias, input, contract[:input], contract[:output]
|
97
53
|
end
|
98
54
|
|
99
|
-
|
100
|
-
else
|
101
|
-
result = self.send method
|
55
|
+
contracts( *contract_list, method )
|
102
56
|
end
|
103
57
|
|
104
|
-
#
|
105
|
-
#
|
106
|
-
|
107
|
-
|
58
|
+
# This method will move methods defined in @contracts into new methods.
|
59
|
+
# Each entry in @contracts will cause the method with the same name to
|
60
|
+
# become method_name_alias and for the original method to point to
|
61
|
+
# method_name_contract.
|
62
|
+
def self.method_added name
|
63
|
+
unless @@disable_override
|
64
|
+
self.contract_list.each do |method|
|
65
|
+
if name == method.to_sym
|
66
|
+
method_alias = "#{method}_alias".to_sym
|
67
|
+
method_contract = "#{method}_contract".to_sym
|
68
|
+
|
69
|
+
@@disable_override = true # to stop the new build method
|
70
|
+
self.send :alias_method, method_alias, name
|
71
|
+
self.send :remove_method, name
|
72
|
+
self.send :alias_method, name, method_contract
|
73
|
+
|
74
|
+
@@disable_override = false
|
75
|
+
else
|
76
|
+
# puts self.inspect
|
77
|
+
# puts "defining other method #{name}"
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
108
81
|
end
|
109
82
|
|
110
|
-
|
111
|
-
|
112
|
-
|
83
|
+
# This method is used as a shorthand to mak the contract method calling pattern more DRY
|
84
|
+
# It starts by checking if you are sending in input and if so will check the input shape for
|
85
|
+
# errors. If no errors are found it calls the method via the passed in symbol(method).
|
86
|
+
#
|
87
|
+
# Output checking is more complicated because of the types of output we check for. Nil is
|
88
|
+
# never valid output. If we pass in the output shape of true, that means we are looking for
|
89
|
+
# result to be the object True. If the output shape is an array, that is actually a shorthand
|
90
|
+
# for telling our output check to look at the output as an array and compare it to the shape
|
91
|
+
# stored in output_shape[0]. If we pass in the symbol :true_false it means we are looking for
|
92
|
+
# the result to be either true or false. The default case will just check if result has the shape
|
93
|
+
# of the output_shape.
|
94
|
+
def call_method method, input, input_shape, output_shape
|
95
|
+
if input != nil && input_shape != nil
|
96
|
+
has_shape, error_field = input.has_shape? input_shape, true
|
97
|
+
unless has_shape
|
98
|
+
raise Obvious::ContractInputError, "incorrect input data format field #{error_field}"
|
99
|
+
end
|
113
100
|
|
114
|
-
|
115
|
-
if output_shape === true
|
116
|
-
if output_shape == result
|
117
|
-
return result
|
101
|
+
result = self.send method, input
|
118
102
|
else
|
119
|
-
|
103
|
+
result = self.send method
|
104
|
+
end
|
105
|
+
|
106
|
+
# check output
|
107
|
+
# output should never be nil
|
108
|
+
if result == nil
|
109
|
+
raise Obvious::ContractOutputError, 'incorrect output data format'
|
110
|
+
end
|
111
|
+
|
112
|
+
if result === {}
|
113
|
+
raise Obvious::DataNotFoundError, 'data was not found'
|
114
|
+
end
|
115
|
+
|
116
|
+
# we are looking for result to be a True object
|
117
|
+
if output_shape === true
|
118
|
+
if output_shape == result
|
119
|
+
return result
|
120
|
+
else
|
121
|
+
raise Obvious::ContractOutputError, 'incorrect output data format'
|
122
|
+
end
|
120
123
|
end
|
121
|
-
end
|
122
124
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
125
|
+
# we want to check the shape of each item in the result array
|
126
|
+
if output_shape.class == Array
|
127
|
+
if result.class == Array
|
128
|
+
inner_shape = output_shape[0]
|
129
|
+
result.each do |item|
|
130
|
+
has_shape, error_field = item.has_shape? inner_shape, true
|
131
|
+
unless has_shape
|
132
|
+
raise Obvious::ContractOutputError, "incorrect output data format field #{error_field}"
|
133
|
+
end
|
131
134
|
end
|
135
|
+
|
136
|
+
return result
|
137
|
+
end
|
138
|
+
raise Obvious::ContractOutputError, 'incorrect output data format'
|
139
|
+
end
|
140
|
+
|
141
|
+
# we want result to be true or false
|
142
|
+
if output_shape == :true_false
|
143
|
+
unless result == true || result == false
|
144
|
+
raise Obvious::ContractOutputError, 'incorrect output data format'
|
132
145
|
end
|
133
146
|
|
134
147
|
return result
|
135
148
|
end
|
136
|
-
raise ContractOutputError, 'incorrect output data format'
|
137
|
-
end
|
138
149
|
|
139
|
-
|
140
|
-
|
141
|
-
unless
|
142
|
-
raise ContractOutputError,
|
150
|
+
# we want result to be output_shape's shape
|
151
|
+
has_shape, error_field = result.has_shape? output_shape, true
|
152
|
+
unless has_shape
|
153
|
+
raise Obvious::ContractOutputError, "incorrect output data format field #{error_field}"
|
143
154
|
end
|
144
155
|
|
145
|
-
|
156
|
+
result
|
146
157
|
end
|
147
158
|
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
159
|
+
end
|
160
|
+
|
161
|
+
|
162
|
+
class ContractInputError < StandardError
|
163
|
+
end
|
153
164
|
|
154
|
-
|
165
|
+
class ContractOutputError < StandardError
|
155
166
|
end
|
156
167
|
|
168
|
+
class DataNotFoundError < StandardError
|
169
|
+
end
|
157
170
|
end
|
158
171
|
|
159
172
|
# via https://github.com/citizen428/shenanigans/blob/master/lib/shenanigans/hash/has_shape_pred.rb
|
160
173
|
class Hash
|
161
174
|
# Checks if a hash has a certain structure.
|
162
175
|
# h = { k1: 1, k2: "1" }
|
163
|
-
# h.has_shape?(k1:
|
176
|
+
# h.has_shape?(k1: Integer, k2: String)
|
164
177
|
# #=> true
|
165
178
|
# h.has_shape?(k1: Class, k2: String)
|
166
179
|
# #=> false
|
@@ -174,28 +187,28 @@ class Hash
|
|
174
187
|
if return_field
|
175
188
|
return r, f
|
176
189
|
else
|
177
|
-
return r
|
190
|
+
return r
|
178
191
|
end
|
179
192
|
}
|
180
|
-
|
193
|
+
|
181
194
|
# I added an empty check
|
182
195
|
if self.empty?
|
183
196
|
return return_value.call shape.empty?, nil
|
184
|
-
end
|
185
|
-
|
197
|
+
end
|
198
|
+
|
186
199
|
self.each do |k, v|
|
187
200
|
return return_value.call false, k if shape[k] == nil
|
188
|
-
end
|
201
|
+
end
|
189
202
|
|
190
203
|
shape.each do |k, v|
|
191
204
|
# hash_value
|
192
205
|
hv = self[k]
|
193
|
-
return return_value.call false, k unless self.has_key? k
|
206
|
+
return return_value.call false, k unless self.has_key? k
|
194
207
|
|
195
208
|
next if hv === nil
|
196
209
|
|
197
|
-
if Hash === hv
|
198
|
-
return hv.has_shape?(v, return_field)
|
210
|
+
if Hash === hv
|
211
|
+
return hv.has_shape?(v, return_field)
|
199
212
|
else
|
200
213
|
return return_value.call false, k unless v === hv
|
201
214
|
end
|
@@ -204,14 +217,11 @@ class Hash
|
|
204
217
|
return_value.call true, nil
|
205
218
|
end
|
206
219
|
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
end
|
211
|
-
|
212
|
-
class ContractOutputError < StandardError
|
213
|
-
end
|
220
|
+
def nil_fields? list
|
221
|
+
list.each do |field|
|
222
|
+
return true, field unless self[field]
|
223
|
+
end
|
214
224
|
|
215
|
-
|
225
|
+
return false, nil
|
226
|
+
end
|
216
227
|
end
|
217
|
-
|
data/lib/obvious/obj.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Obvious
|
2
|
+
module Obj
|
3
|
+
class << self
|
4
|
+
def included(base)
|
5
|
+
base.extend ClassMethods
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
|
11
|
+
def define method, input = {}, &block
|
12
|
+
define_method(method) do |method_input = {}|
|
13
|
+
block_input = {}
|
14
|
+
method_input.each do |k,v|
|
15
|
+
if input[k].nil?
|
16
|
+
raise ArgumentError.new "invalid input field #{k}"
|
17
|
+
end
|
18
|
+
|
19
|
+
unless v.is_a? input[k]
|
20
|
+
raise ArgumentError.new "invalid type for #{k} expected #{input[k]}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
input.each do |k,v|
|
25
|
+
if method_input[k].nil?
|
26
|
+
raise ArgumentError.new "missing input field #{k}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
self.instance_exec method_input, &block
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/obvious/version.rb
CHANGED
data/lib/obvious.rb
CHANGED
data/obvious.gemspec
CHANGED
@@ -6,16 +6,14 @@ require 'obvious/version'
|
|
6
6
|
Gem::Specification.new do |gem|
|
7
7
|
gem.name = "obvious"
|
8
8
|
gem.version = Obvious::VERSION
|
9
|
-
gem.authors = ["Brian Knapp"]
|
10
|
-
gem.email = ["brianknapp@gmail.com"]
|
9
|
+
gem.authors = ["Brian Knapp", "Shawn Baden"]
|
10
|
+
gem.email = ["brianknapp@gmail.com", "shawnbaden@hotmail.com"]
|
11
11
|
gem.description = "A set of tools to build apps using the Obvious Architecture"
|
12
|
-
gem.summary = "
|
13
|
-
gem.homepage = "
|
14
|
-
|
12
|
+
gem.summary = "Clean Architecture framework"
|
13
|
+
gem.homepage = "https://github.com/RetroMocha/obvious"
|
14
|
+
gem.license = "MIT"
|
15
15
|
gem.files = `git ls-files`.split($/)
|
16
16
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
17
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
18
|
gem.require_paths = ["lib"]
|
19
|
-
|
20
|
-
gem.add_development_dependency "rspec"
|
21
19
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require_relative '../lib/obvious/contract'
|
3
|
+
|
4
|
+
class TestContract < Obvious::Contract
|
5
|
+
contract_for :test, {
|
6
|
+
input: { id: Integer },
|
7
|
+
output: { id: Integer, value: String }
|
8
|
+
}
|
9
|
+
|
10
|
+
def test input
|
11
|
+
{ id: 1, value: 'this is a test' }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class ContractTest < Minitest::Test
|
16
|
+
def test_valid_input
|
17
|
+
result = TestContract.new.test(id: 1)
|
18
|
+
assert_equal({id: 1, value: 'this is a test'}, result)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_invalid_input
|
22
|
+
assert_raises Obvious::ContractInputError do
|
23
|
+
TestContract.new.test(Hash.new)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_empty_hash_return
|
28
|
+
assert_raises Obvious::DataNotFoundError do
|
29
|
+
tc = TestContract.new
|
30
|
+
tc.stub :test_alias, {} do
|
31
|
+
tc.test(id: 1)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_nil_return
|
37
|
+
assert_raises Obvious::ContractOutputError do
|
38
|
+
tc = TestContract.new
|
39
|
+
tc.stub :test_alias, nil do
|
40
|
+
tc.test(id: 1)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
class HashTest < Minitest::Test
|
47
|
+
def test_valid_has_shape
|
48
|
+
assert({id: 1}.has_shape?(id: Integer))
|
49
|
+
end
|
50
|
+
|
51
|
+
def test_invalid_has_shape
|
52
|
+
refute({id: 1}.has_shape?(id: String))
|
53
|
+
end
|
54
|
+
|
55
|
+
def test_has_shape_allow_nil_values
|
56
|
+
assert({id: nil}.has_shape?({id: String}))
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_has_shape_return_invalid_field
|
60
|
+
assert_equal([false, :id], { id: 1 }.has_shape?({id: String}, true))
|
61
|
+
end
|
62
|
+
end
|