nrser 0.0.24 → 0.0.25
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 +4 -4
- data/lib/nrser/enumerable.rb +12 -6
- data/lib/nrser/hash.rb +182 -0
- data/lib/nrser/meta/props/base.rb +22 -0
- data/lib/nrser/meta/props/prop.rb +84 -12
- data/lib/nrser/meta/props.rb +36 -15
- data/lib/nrser/refinements/hash.rb +13 -0
- data/lib/nrser/refinements/pathname.rb +11 -0
- data/lib/nrser/refinements.rb +2 -0
- data/lib/nrser/spex.rb +68 -0
- data/lib/nrser/types/attrs.rb +83 -30
- data/lib/nrser/types/combinators.rb +54 -6
- data/lib/nrser/types/paths.rb +62 -2
- data/lib/nrser/types/type.rb +122 -2
- data/lib/nrser/types.rb +2 -0
- data/lib/nrser/version.rb +1 -1
- data/lib/nrser.rb +5 -1
- data/spec/nrser/hash/bury_spec.rb +31 -0
- data/spec/nrser/hash/guess_name_type_spec.rb +47 -0
- data/spec/nrser/types/attrs_spec.rb +41 -0
- data/spec/nrser/types/paths_spec.rb +69 -0
- data/spec/nrser/types_spec.rb +0 -10
- data/spec/spec_helper.rb +46 -1
- metadata +12 -2
data/lib/nrser/types/attrs.rb
CHANGED
@@ -36,43 +36,97 @@ module NRSER::Types
|
|
36
36
|
def attrs attrs, options = {}
|
37
37
|
Attrs.new attrs, **options
|
38
38
|
end
|
39
|
-
|
39
|
+
|
40
|
+
|
41
|
+
# @overload length exact, options = {}
|
42
|
+
# Get a length attribute type that specifies an `exact` value.
|
43
|
+
#
|
44
|
+
# @example
|
45
|
+
# only_type = NRSER::Types.length 1
|
46
|
+
#
|
47
|
+
# only_type.test []
|
48
|
+
# # => false
|
49
|
+
#
|
50
|
+
# only_type.test [:x]
|
51
|
+
# # => true
|
52
|
+
#
|
53
|
+
# only_type.test [:x, :y]
|
54
|
+
# # => false
|
55
|
+
#
|
56
|
+
# @param [Integer] exact
|
57
|
+
# Exact non-negative integer that the length must be to satisfy the
|
58
|
+
# type created.
|
59
|
+
#
|
60
|
+
# @param [Hash] options
|
61
|
+
# Options hash passed up to {NRSER::Types::Type} constructor.
|
62
|
+
#
|
63
|
+
# @return [NRSER::Types::Attrs]
|
64
|
+
# Type satisfied by a `#length` attribute that is exactly `exact`.
|
65
|
+
#
|
66
|
+
#
|
67
|
+
# @overload length bounds, options = {}
|
68
|
+
# Get a length attribute type satisfied by values within a `:min` and
|
69
|
+
# `:max` (inclusive).
|
70
|
+
#
|
71
|
+
# @example
|
72
|
+
# three_to_five = NRSER::Types.length( {min: 3, max: 5}, name: '3-5' )
|
73
|
+
# three_to_five.test [1, 2] # => false
|
74
|
+
# three_to_five.test [1, 2, 3] # => true
|
75
|
+
# three_to_five.test [1, 2, 3, 4] # => true
|
76
|
+
# three_to_five.test [1, 2, 3, 4, 5] # => true
|
77
|
+
# three_to_five.test [1, 2, 3, 4, 5, 6] # => false
|
78
|
+
#
|
79
|
+
# @param [Hash] bounds
|
80
|
+
#
|
81
|
+
# @option bounds [Integer] :min
|
82
|
+
# An optional minimum value that the `#length` should not be less than.
|
83
|
+
#
|
84
|
+
# @option bounds [Integer] :max
|
85
|
+
# An optional maximum value that the `#length` should not be more than.
|
86
|
+
#
|
87
|
+
# @option bounds [Integer] :length
|
88
|
+
# An optional value for both the minimum and maximum.
|
89
|
+
#
|
90
|
+
# @param [Hash] options
|
91
|
+
# Options hash passed up to {NRSER::Types::Type} constructor.
|
92
|
+
#
|
93
|
+
# @return [NRSER::Types::Attrs]
|
94
|
+
# Type satisfied by a `#length` attribute between the `:min` and `:max`
|
95
|
+
# (inclusive).
|
96
|
+
#
|
40
97
|
def length *args
|
41
98
|
bounds = {}
|
42
|
-
options = {}
|
99
|
+
options = if args[1].is_a?( Hash ) then args[1] else {} end
|
43
100
|
|
44
|
-
case args
|
45
|
-
when
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
else
|
62
|
-
raise ArgumentError, <<-END.squish
|
63
|
-
arg must be positive integer or option hash, found:
|
64
|
-
#{ args[0].inspect } of type #{ args[0].class }
|
65
|
-
END
|
101
|
+
case args[0]
|
102
|
+
when ::Integer
|
103
|
+
# It's just a length
|
104
|
+
bounds[:min] = bounds[:max] = non_neg_int.check args[0]
|
105
|
+
|
106
|
+
when ::Hash
|
107
|
+
# It's keyword args
|
108
|
+
kwds = NRSER.symbolize_keys args[0]
|
109
|
+
|
110
|
+
# Pull any :min and :max in the keywords
|
111
|
+
bounds[:min] = kwds.delete :min
|
112
|
+
bounds[:max] = kwds.delete :max
|
113
|
+
|
114
|
+
# But override with :length if we got it
|
115
|
+
if length = kwds.delete(:length)
|
116
|
+
bounds[:min] = length
|
117
|
+
bounds[:max] = length
|
66
118
|
end
|
67
119
|
|
68
|
-
|
69
|
-
|
70
|
-
options =
|
120
|
+
# (Reverse) merge anything else into the options (options hash values
|
121
|
+
# take precedence)
|
122
|
+
options = kwds.merge options
|
71
123
|
|
72
|
-
else
|
124
|
+
else
|
73
125
|
raise ArgumentError, <<-END.squish
|
74
|
-
must
|
126
|
+
arg must be positive integer or option hash, found:
|
127
|
+
#{ args[0].inspect } of type #{ args[0].class }
|
75
128
|
END
|
129
|
+
|
76
130
|
end
|
77
131
|
|
78
132
|
bounded_type = bounded bounds
|
@@ -90,7 +144,6 @@ module NRSER::Types
|
|
90
144
|
attrs({ length: length_type }, options)
|
91
145
|
end # #length
|
92
146
|
|
93
|
-
|
94
147
|
end # class << self (Eigenclass)
|
95
148
|
|
96
149
|
end # NRSER::Types
|
@@ -8,30 +8,32 @@ module NRSER::Types
|
|
8
8
|
class Combinator < NRSER::Types::Type
|
9
9
|
attr_reader :types
|
10
10
|
|
11
|
+
|
11
12
|
def initialize *types, **options
|
12
13
|
super **options
|
13
14
|
@types = types.map {|type| NRSER::Types.make type}
|
14
15
|
end
|
15
16
|
|
17
|
+
|
16
18
|
def default_name
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
">"
|
21
|
-
)
|
19
|
+
"#{ self.class.short_name }<" +
|
20
|
+
@types.map {|type| type.name }.join(',') +
|
21
|
+
">"
|
22
22
|
end
|
23
23
|
|
24
|
+
|
24
25
|
# a combinator may attempt to parse from a string if any of it's types
|
25
26
|
# can do so
|
26
27
|
def has_from_s?
|
27
28
|
@types.any? {|type| type.has_from_s?}
|
28
29
|
end
|
29
30
|
|
31
|
+
|
30
32
|
# a combinator iterates through each of it's types, trying the
|
31
33
|
# conversion and seeing if the result satisfies the combinator type
|
32
34
|
# itself. the first such value found is returned.
|
33
35
|
def from_s s
|
34
|
-
@types.each {|type|
|
36
|
+
@types.each { |type|
|
35
37
|
if type.respond_to? :from_s
|
36
38
|
begin
|
37
39
|
return check type.from_s(s)
|
@@ -45,6 +47,52 @@ module NRSER::Types
|
|
45
47
|
"none of combinator #{ self.to_s } types could convert #{ s.inspect }"
|
46
48
|
end
|
47
49
|
|
50
|
+
|
51
|
+
# Overridden to delegate functionality to the combined types:
|
52
|
+
#
|
53
|
+
# A combinator may attempt to parse from a string if any of it's types
|
54
|
+
# can do so.
|
55
|
+
#
|
56
|
+
# @return [Boolean]
|
57
|
+
#
|
58
|
+
def has_from_s?
|
59
|
+
@types.any? {|type| type.has_from_s?}
|
60
|
+
end # has_from_s
|
61
|
+
|
62
|
+
|
63
|
+
# Overridden to delegate functionality to the combined types:
|
64
|
+
#
|
65
|
+
# A combinator can convert a value to data if *any* of it's types can.
|
66
|
+
#
|
67
|
+
# @return [Boolean]
|
68
|
+
#
|
69
|
+
def has_to_data?
|
70
|
+
@types.any? { |type| type.has_to_data? }
|
71
|
+
end # #has_to_data
|
72
|
+
|
73
|
+
|
74
|
+
# Overridden to delegate functionality to the combined types:
|
75
|
+
#
|
76
|
+
# The first of the combined types that responds to `#to_data` is used to
|
77
|
+
# dump the value.
|
78
|
+
#
|
79
|
+
# @param [Object] value
|
80
|
+
# Value of this type (though it is *not* checked).
|
81
|
+
#
|
82
|
+
# @return [Object]
|
83
|
+
# The data representation of the value.
|
84
|
+
#
|
85
|
+
def to_data value
|
86
|
+
@types.each { |type|
|
87
|
+
if type.respond_to? :to_data
|
88
|
+
return type.to_data value
|
89
|
+
end
|
90
|
+
}
|
91
|
+
|
92
|
+
raise NoMethodError, "#to_data not defined"
|
93
|
+
end # #to_data
|
94
|
+
|
95
|
+
|
48
96
|
def == other
|
49
97
|
equal?(other) || (
|
50
98
|
other.class == self.class && other.types == @types
|
data/lib/nrser/types/paths.rb
CHANGED
@@ -34,7 +34,8 @@ module NRSER::Types
|
|
34
34
|
PATHNAME = is_a \
|
35
35
|
Pathname,
|
36
36
|
name: 'PathnameType',
|
37
|
-
from_s: ->(string) { Pathname.new string }
|
37
|
+
from_s: ->(string) { Pathname.new string },
|
38
|
+
to_data: :to_s
|
38
39
|
|
39
40
|
|
40
41
|
# A type satisfied by a {Pathname} instance that's not empty (meaning it's
|
@@ -53,7 +54,7 @@ module NRSER::Types
|
|
53
54
|
#
|
54
55
|
class << self
|
55
56
|
|
56
|
-
def pathname **options
|
57
|
+
def pathname to_data: :to_s, **options
|
57
58
|
if options.empty?
|
58
59
|
PATHNAME
|
59
60
|
else
|
@@ -61,11 +62,14 @@ module NRSER::Types
|
|
61
62
|
Pathname,
|
62
63
|
name: 'PathnameType',
|
63
64
|
from_s: ->(string) { Pathname.new string },
|
65
|
+
to_data: to_data,
|
64
66
|
**options
|
65
67
|
end
|
66
68
|
end
|
67
69
|
|
70
|
+
# A path is a non-empty {String} or {Pathname}.
|
68
71
|
#
|
72
|
+
# @param **options see NRSER::Types::Type#initialize
|
69
73
|
#
|
70
74
|
# @return [NRSER::Types::Type]
|
71
75
|
#
|
@@ -77,6 +81,62 @@ module NRSER::Types
|
|
77
81
|
end
|
78
82
|
end # #path
|
79
83
|
|
84
|
+
|
85
|
+
# An absolute {#path}.
|
86
|
+
#
|
87
|
+
# @param **options see NRSER::Types::Type#initialize
|
88
|
+
#
|
89
|
+
def abs_path name: 'AbsPath', **options
|
90
|
+
intersection \
|
91
|
+
path,
|
92
|
+
where { |path| File.absolute? path },
|
93
|
+
name: name,
|
94
|
+
**options
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
# A {NRSER::Types.path} that is a directory.
|
99
|
+
#
|
100
|
+
# @param [Hash] **options
|
101
|
+
# Construction options passed to {NRSER::Types::Type#initialize}.
|
102
|
+
#
|
103
|
+
# @return [NRSER::Types::Type]
|
104
|
+
#
|
105
|
+
def dir_path name: 'DirPath', **options
|
106
|
+
intersection \
|
107
|
+
path,
|
108
|
+
where { |path| File.directory? path },
|
109
|
+
name: name,
|
110
|
+
**options
|
111
|
+
end # #dir_path
|
112
|
+
|
113
|
+
|
114
|
+
# Absolute {.path} to a directory (both an {.abs_path} and an {.dir_path}).
|
115
|
+
#
|
116
|
+
# @param [type] name:
|
117
|
+
# @todo Add name param description.
|
118
|
+
#
|
119
|
+
# @return [return_type]
|
120
|
+
# @todo Document return value.
|
121
|
+
#
|
122
|
+
def abs_dir_path name: 'AbsDirPath', **options
|
123
|
+
intersection \
|
124
|
+
abs_path,
|
125
|
+
dir_path,
|
126
|
+
name: name,
|
127
|
+
**options
|
128
|
+
end # #abs_dir_path
|
129
|
+
|
130
|
+
|
131
|
+
|
132
|
+
def file_path name: 'FilePath', **options
|
133
|
+
intersection \
|
134
|
+
path,
|
135
|
+
where { |path| File.file? path },
|
136
|
+
name: name,
|
137
|
+
**options
|
138
|
+
end
|
139
|
+
|
80
140
|
end # class << self (Eigenclass)
|
81
141
|
|
82
142
|
end # module NRSER::Types
|
data/lib/nrser/types/type.rb
CHANGED
@@ -1,6 +1,13 @@
|
|
1
|
+
# Refinements
|
2
|
+
# =======================================================================
|
3
|
+
|
1
4
|
require 'nrser/refinements'
|
2
5
|
using NRSER
|
3
6
|
|
7
|
+
|
8
|
+
# Definitions
|
9
|
+
# =======================================================================
|
10
|
+
|
4
11
|
module NRSER::Types
|
5
12
|
class Type
|
6
13
|
def self.short_name
|
@@ -25,9 +32,25 @@ module NRSER::Types
|
|
25
32
|
# value that doesn't satisfy will result in a {TypeError} being raised
|
26
33
|
# by {#from_s}.
|
27
34
|
#
|
28
|
-
|
35
|
+
# @param [nil | #call | #to_proc] to_data:
|
36
|
+
#
|
37
|
+
#
|
38
|
+
def initialize name: nil, from_s: nil, to_data: nil
|
29
39
|
@name = name
|
30
40
|
@from_s = from_s
|
41
|
+
|
42
|
+
@to_data = if to_data.nil?
|
43
|
+
nil
|
44
|
+
elsif to_data.respond_to?( :call )
|
45
|
+
to_data
|
46
|
+
elsif to_data.respond_to?( :to_proc )
|
47
|
+
to_data.to_proc
|
48
|
+
else
|
49
|
+
raise TypeError.squished <<-END
|
50
|
+
`to_data:` keyword arg must be `nil`, respond to `:call` or respond
|
51
|
+
to `:to_proc`; found #{ to_data.inspect }
|
52
|
+
END
|
53
|
+
end
|
31
54
|
end # #initialize
|
32
55
|
|
33
56
|
|
@@ -43,6 +66,7 @@ module NRSER::Types
|
|
43
66
|
raise NotImplementedError
|
44
67
|
end
|
45
68
|
|
69
|
+
|
46
70
|
def check value, &make_fail_message
|
47
71
|
# success case
|
48
72
|
return value if test value
|
@@ -58,14 +82,67 @@ module NRSER::Types
|
|
58
82
|
raise TypeError.new msg
|
59
83
|
end
|
60
84
|
|
85
|
+
|
86
|
+
# Overridden to customize behavior for the {#from_s} and {#to_data}
|
87
|
+
# methods - those methods are always defined, but we have {#respond_to?}
|
88
|
+
# return `false` if they lack the underlying instance variables needed
|
89
|
+
# to execute.
|
90
|
+
#
|
91
|
+
# @example
|
92
|
+
# t1 = t.where { |value| true }
|
93
|
+
# t1.respond_to? :from_s
|
94
|
+
# # => false
|
95
|
+
#
|
96
|
+
# t2 = t.where( from_s: ->(s){ s.split ',' } ) { |value| true }
|
97
|
+
# t2.respond_to? :from_s
|
98
|
+
# # => true
|
99
|
+
#
|
100
|
+
# @param [Symbol | String] name
|
101
|
+
# Method name to ask about.
|
102
|
+
#
|
103
|
+
# @param [Boolean] include_all
|
104
|
+
# IDK, part of Ruby API that is passed up to `super`.
|
105
|
+
#
|
106
|
+
# @return [Boolean]
|
107
|
+
#
|
61
108
|
def respond_to? name, include_all = false
|
62
109
|
if name == :from_s || name == 'from_s'
|
63
110
|
has_from_s?
|
111
|
+
elsif name == :to_data || name == 'to_data'
|
112
|
+
has_to_data?
|
64
113
|
else
|
65
114
|
super name, include_all
|
66
115
|
end
|
67
|
-
end
|
116
|
+
end # #respond_to?
|
68
117
|
|
118
|
+
|
119
|
+
# Load a value of this type from a string representation by passing `s`
|
120
|
+
# to the {@from_s} {Proc}.
|
121
|
+
#
|
122
|
+
# Checks the value {@from_s} returns with {#check} before returning it, so
|
123
|
+
# you know it satisfies this type.
|
124
|
+
#
|
125
|
+
# @param [String] s
|
126
|
+
# String representation.
|
127
|
+
#
|
128
|
+
# @return [Object]
|
129
|
+
# Value that has passed {#check}.
|
130
|
+
#
|
131
|
+
# @raise [NoMethodError]
|
132
|
+
# If this type doesn't know how to load values from strings.
|
133
|
+
#
|
134
|
+
# In basic types this happens when {NRSER::Types::Type#initialize} was
|
135
|
+
# not provided a `from_s:` {Proc} argument.
|
136
|
+
#
|
137
|
+
# {NRSER::Types::Type} subclasses may override {#from_s} entirely,
|
138
|
+
# divorcing it from the `from_s:` constructor argument and internal
|
139
|
+
# {@from_s} instance variable (which is why {@from_s} is not publicly
|
140
|
+
# exposed - it should not be assumed to dictate {#from_s} behavior
|
141
|
+
# in general).
|
142
|
+
#
|
143
|
+
# @raise [TypeError]
|
144
|
+
# If the value loaded does not pass {#check}.
|
145
|
+
#
|
69
146
|
def from_s s
|
70
147
|
if @from_s.nil?
|
71
148
|
raise NoMethodError, "#from_s not defined"
|
@@ -74,12 +151,55 @@ module NRSER::Types
|
|
74
151
|
check @from_s.call( s )
|
75
152
|
end
|
76
153
|
|
154
|
+
|
155
|
+
# Test if the type knows how to load values from strings.
|
156
|
+
#
|
157
|
+
# If this method returns `true`, then we expect {#from_s} to succeed.
|
158
|
+
#
|
159
|
+
# @return [Boolean]
|
160
|
+
#
|
77
161
|
def has_from_s?
|
78
162
|
! @from_s.nil?
|
79
163
|
end
|
80
164
|
|
165
|
+
|
166
|
+
# Test if the type has custom information about how to convert it's values
|
167
|
+
# into "data" - structures and values suitable for transportation and
|
168
|
+
# storage (JSON, etc.).
|
169
|
+
#
|
170
|
+
# If this method returns `true` then {#to_data} should succeed.
|
171
|
+
#
|
172
|
+
# @return [Boolean]
|
173
|
+
#
|
174
|
+
def has_to_data?
|
175
|
+
! @to_data.nil?
|
176
|
+
end # #has_to_data?
|
177
|
+
|
178
|
+
|
179
|
+
# Dumps a value of this type to "data" - structures and values suitable
|
180
|
+
# for transport and storage, such as dumping to JSON or YAML, etc.
|
181
|
+
#
|
182
|
+
# @param [Object] value
|
183
|
+
# Value of this type (though it is *not* checked).
|
184
|
+
#
|
185
|
+
# @return [Object]
|
186
|
+
# The data representation of the value.
|
187
|
+
#
|
188
|
+
def to_data value
|
189
|
+
if @from_s.nil?
|
190
|
+
raise NoMethodError, "#to_data not defined"
|
191
|
+
end
|
192
|
+
|
193
|
+
@to_data.call value
|
194
|
+
end # #to_data
|
195
|
+
|
196
|
+
|
197
|
+
# @return [String]
|
198
|
+
# a brief string description of the type.
|
199
|
+
#
|
81
200
|
def to_s
|
82
201
|
"`Type: #{ name }`"
|
83
202
|
end
|
203
|
+
|
84
204
|
end # Type
|
85
205
|
end # NRSER::Types
|
data/lib/nrser/types.rb
CHANGED
data/lib/nrser/version.rb
CHANGED
data/lib/nrser.rb
CHANGED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "NRSER.bury!" do
|
4
|
+
subject { NRSER.method :bury! }
|
5
|
+
|
6
|
+
it do
|
7
|
+
expect(
|
8
|
+
{}.tap { |hash|
|
9
|
+
subject.call( hash, [:a, :b, :c], 1 )
|
10
|
+
}
|
11
|
+
).to eq(
|
12
|
+
{ a: { b: { c: 1 } } }
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
context "string key path" do
|
17
|
+
context ":parsed_key_type option omitted" do
|
18
|
+
it "creates hashes and sets string keys" do
|
19
|
+
expect(
|
20
|
+
{}.tap { |hash|
|
21
|
+
subject.call( hash, 'a.b.c', 1 )
|
22
|
+
}
|
23
|
+
).to eq(
|
24
|
+
{ 'a' => { 'b' => { 'c' => 1 } } }
|
25
|
+
)
|
26
|
+
end
|
27
|
+
end # :key_type option omitted
|
28
|
+
|
29
|
+
end # string key path
|
30
|
+
|
31
|
+
end # NRSER.bury
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "NRSER.guess_name_type" do
|
4
|
+
subject { NRSER.method :guess_name_type }
|
5
|
+
|
6
|
+
it "can't guess about an empty hash" do
|
7
|
+
expect( subject.call( {} ) ).to be nil
|
8
|
+
end
|
9
|
+
|
10
|
+
it "guesses String when all keys are strings" do
|
11
|
+
expect( subject.call( {'a' => 1, 'b' => 2} ) ).to be String
|
12
|
+
end
|
13
|
+
|
14
|
+
it "guesses Symbol when all keys are symbols" do
|
15
|
+
expect( subject.call( {a: 1, b: 2} ) ).to be Symbol
|
16
|
+
end
|
17
|
+
|
18
|
+
it "guesses String when there are string keys but no symbols" do
|
19
|
+
expect(
|
20
|
+
subject.call({
|
21
|
+
'a' => 1,
|
22
|
+
[:b] => 2,
|
23
|
+
3 => 'three',
|
24
|
+
})
|
25
|
+
).to be String
|
26
|
+
end
|
27
|
+
|
28
|
+
it "guesses Symbol when there are symbol keys but no strings" do
|
29
|
+
expect(
|
30
|
+
subject.call({
|
31
|
+
a: 1,
|
32
|
+
['b'] => 2,
|
33
|
+
3 => 'three',
|
34
|
+
})
|
35
|
+
).to be Symbol
|
36
|
+
end
|
37
|
+
|
38
|
+
it "can't guess when there are string and symbol keys" do
|
39
|
+
expect(
|
40
|
+
subject.call({
|
41
|
+
a: 1,
|
42
|
+
'b' => 2,
|
43
|
+
})
|
44
|
+
).to be nil
|
45
|
+
end
|
46
|
+
|
47
|
+
end # NRSER.guess_name_type
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "NRSER::Types.length" do
|
4
|
+
subject { NRSER::Types.method :length }
|
5
|
+
|
6
|
+
context "zero length" do
|
7
|
+
kwds = {
|
8
|
+
accepts: [ '', [], {}, ],
|
9
|
+
rejects: [ 'x', [1], {x: 1} ],
|
10
|
+
}
|
11
|
+
|
12
|
+
# Three ways to cut it:
|
13
|
+
it_behaves_like 'Type maker method', args: [ length: 0 ], **kwds
|
14
|
+
it_behaves_like 'Type maker method', args: [ 0 ], **kwds
|
15
|
+
it_behaves_like 'Type maker method', args: [ min: 0, max: 0], **kwds
|
16
|
+
|
17
|
+
end # zero length
|
18
|
+
|
19
|
+
it_behaves_like 'Type maker method',
|
20
|
+
args: [ {min: 3, max: 5}, name: '3to5Type' ],
|
21
|
+
|
22
|
+
accepts: [
|
23
|
+
[1, 2, 3],
|
24
|
+
[1, 2, 3, 4],
|
25
|
+
[1, 2, 3, 4, 5],
|
26
|
+
],
|
27
|
+
|
28
|
+
rejects: [
|
29
|
+
[1, 2],
|
30
|
+
[1, 2, 3, 4, 5, 6]
|
31
|
+
],
|
32
|
+
|
33
|
+
and_is_expected: {
|
34
|
+
to: {
|
35
|
+
have_attributes: {
|
36
|
+
name: '3to5Type',
|
37
|
+
}
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
end # NRSER::Types.length
|