nrser 0.0.26 → 0.0.27
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.rb +1 -0
- data/lib/nrser/array.rb +15 -0
- data/lib/nrser/binding.rb +7 -1
- data/lib/nrser/enumerable.rb +21 -1
- data/lib/nrser/errors.rb +56 -6
- data/lib/nrser/hash/deep_merge.rb +1 -1
- data/lib/nrser/message.rb +33 -0
- data/lib/nrser/meta/props.rb +77 -15
- data/lib/nrser/meta/props/prop.rb +276 -44
- data/lib/nrser/proc.rb +7 -3
- data/lib/nrser/refinements/array.rb +5 -0
- data/lib/nrser/refinements/enumerable.rb +5 -0
- data/lib/nrser/refinements/hash.rb +8 -0
- data/lib/nrser/refinements/object.rb +11 -1
- data/lib/nrser/refinements/string.rb +17 -3
- data/lib/nrser/refinements/symbol.rb +8 -0
- data/lib/nrser/refinements/tree.rb +22 -0
- data/lib/nrser/rspex.rb +312 -70
- data/lib/nrser/rspex/shared_examples.rb +116 -0
- data/lib/nrser/string.rb +159 -27
- data/lib/nrser/temp/unicode_math.rb +48 -0
- data/lib/nrser/text.rb +3 -0
- data/lib/nrser/text/indentation.rb +210 -0
- data/lib/nrser/text/lines.rb +52 -0
- data/lib/nrser/text/word_wrap.rb +29 -0
- data/lib/nrser/tree.rb +4 -78
- data/lib/nrser/tree/each_branch.rb +76 -0
- data/lib/nrser/tree/map_branches.rb +91 -0
- data/lib/nrser/tree/map_tree.rb +97 -0
- data/lib/nrser/tree/transform.rb +56 -13
- data/lib/nrser/types.rb +1 -0
- data/lib/nrser/types/array.rb +15 -3
- data/lib/nrser/types/is_a.rb +40 -1
- data/lib/nrser/types/nil.rb +17 -0
- data/lib/nrser/types/paths.rb +17 -2
- data/lib/nrser/types/strings.rb +57 -22
- data/lib/nrser/types/tuples.rb +5 -0
- data/lib/nrser/types/type.rb +47 -6
- data/lib/nrser/version.rb +1 -1
- data/spec/nrser/errors/abstract_method_error_spec.rb +46 -0
- data/spec/nrser/meta/props/to_and_from_data_spec.rb +74 -0
- data/spec/nrser/meta/props_spec.rb +6 -2
- data/spec/nrser/refinements/erb_spec.rb +100 -1
- data/spec/nrser/{common_prefix_spec.rb → string/common_prefix_spec.rb} +9 -0
- data/spec/nrser/text/dedent_spec.rb +80 -0
- data/spec/nrser/tree/map_branch_spec.rb +83 -0
- data/spec/nrser/tree/map_tree_spec.rb +123 -0
- data/spec/nrser/tree/transform_spec.rb +26 -29
- data/spec/nrser/tree/transformer_spec.rb +179 -0
- data/spec/nrser/types/paths_spec.rb +73 -45
- data/spec/spec_helper.rb +10 -0
- metadata +27 -7
- data/spec/nrser/dedent_spec.rb +0 -36
data/lib/nrser/types.rb
CHANGED
data/lib/nrser/types/array.rb
CHANGED
@@ -30,6 +30,7 @@ module NRSER; end
|
|
30
30
|
# =======================================================================
|
31
31
|
|
32
32
|
module NRSER::Types
|
33
|
+
|
33
34
|
class ArrayType < IsA
|
34
35
|
# Default value to split strings with in {#from_s} if the string provided
|
35
36
|
# does is not recognized as an encoding format (as of writing, JSON is
|
@@ -51,6 +52,11 @@ module NRSER::Types
|
|
51
52
|
end
|
52
53
|
|
53
54
|
|
55
|
+
def item_type
|
56
|
+
NRSER::Types.any
|
57
|
+
end
|
58
|
+
|
59
|
+
|
54
60
|
# Called on an array of string items that have been split
|
55
61
|
# from a single string by {#from_s} to convert each individual item before
|
56
62
|
# {#check} is called on the value.
|
@@ -179,12 +185,17 @@ module NRSER::Types
|
|
179
185
|
#
|
180
186
|
class << self
|
181
187
|
|
182
|
-
#
|
188
|
+
# @!group Type Factory Functions
|
189
|
+
|
190
|
+
# {NRSER::Types::ArrayType} / {NRSER::Types::ArrayOfType} factory function.
|
191
|
+
#
|
192
|
+
# @param [Type | Object] item_type
|
193
|
+
# Optional type of items.
|
183
194
|
#
|
184
195
|
# @return [NRSER::Types::Type]
|
185
196
|
#
|
186
|
-
def array item_type =
|
187
|
-
if item_type ==
|
197
|
+
def array item_type = any, **options
|
198
|
+
if item_type == any
|
188
199
|
if options.empty?
|
189
200
|
ARRAY
|
190
201
|
else
|
@@ -199,4 +210,5 @@ module NRSER::Types
|
|
199
210
|
|
200
211
|
end # class << self (Eigenclass)
|
201
212
|
|
213
|
+
|
202
214
|
end # NRSER::Types
|
data/lib/nrser/types/is_a.rb
CHANGED
@@ -7,19 +7,58 @@ module NRSER::Types
|
|
7
7
|
attr_reader :klass
|
8
8
|
|
9
9
|
def initialize klass, **options
|
10
|
+
unless klass.is_a?( Class ) || klass.is_a?( Module )
|
11
|
+
raise ArgumentError.new binding.erb <<-ERB
|
12
|
+
`klass` argument must be a Class or Module, found:
|
13
|
+
|
14
|
+
<%= klass.pretty_inspect %>
|
15
|
+
|
16
|
+
ERB
|
17
|
+
end
|
18
|
+
|
10
19
|
super **options
|
11
20
|
@klass = klass
|
12
21
|
end
|
13
22
|
|
14
23
|
def default_name
|
15
|
-
"#{ self.class.short_name }(#{ @klass })"
|
24
|
+
"#{ self.class.short_name }(#{ @klass.name })"
|
16
25
|
end
|
17
26
|
|
18
27
|
def test value
|
19
28
|
value.is_a? @klass
|
20
29
|
end
|
30
|
+
|
31
|
+
|
32
|
+
# If {#klass} responds to `#from_data`, call that and check results.
|
33
|
+
#
|
34
|
+
# Otherwise, forward up to {NRSER::Types::Type#from_data}.
|
35
|
+
#
|
36
|
+
# @param [Object] data
|
37
|
+
# Data to create the value from that will satisfy the type.
|
38
|
+
#
|
39
|
+
# @return [Object]
|
40
|
+
# Instance of {#klass}.
|
41
|
+
#
|
42
|
+
def from_data data
|
43
|
+
if @from_data.nil?
|
44
|
+
if @klass.respond_to? :from_data
|
45
|
+
check @klass.from_data( data )
|
46
|
+
else
|
47
|
+
super data
|
48
|
+
end
|
49
|
+
else
|
50
|
+
@from_data.call data
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
def has_from_data?
|
56
|
+
@from_data || @klass.respond_to?( :from_data )
|
57
|
+
end
|
58
|
+
|
21
59
|
end # IsA
|
22
60
|
|
61
|
+
|
23
62
|
# class membership
|
24
63
|
def self.is_a klass, **options
|
25
64
|
IsA.new klass, **options
|
data/lib/nrser/types/paths.rb
CHANGED
@@ -45,7 +45,6 @@ module NRSER::Types
|
|
45
45
|
where { |value| value.to_s.length > 0 },
|
46
46
|
name: 'NonEmptyPathnameType'
|
47
47
|
|
48
|
-
|
49
48
|
PATH = union non_empty_str, NON_EMPTY_PATHNAME, name: 'Path'
|
50
49
|
|
51
50
|
|
@@ -53,9 +52,10 @@ module NRSER::Types
|
|
53
52
|
# ========================================================================
|
54
53
|
#
|
55
54
|
class << self
|
55
|
+
# @!group Type Factory Functions
|
56
56
|
|
57
57
|
def pathname to_data: :to_s, **options
|
58
|
-
if options.empty?
|
58
|
+
if options.empty? && to_data == :to_s
|
59
59
|
PATHNAME
|
60
60
|
else
|
61
61
|
is_a \
|
@@ -82,6 +82,19 @@ module NRSER::Types
|
|
82
82
|
end # #path
|
83
83
|
|
84
84
|
|
85
|
+
def path_segment **options
|
86
|
+
if options.empty?
|
87
|
+
POSIX_PATH_SEGMENT
|
88
|
+
else
|
89
|
+
intersection non_empty_str,
|
90
|
+
where { |string| ! string.include?( '/' ) },
|
91
|
+
name: 'POSIXPathSegment'
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
alias_method :path_seg, :path_segment
|
96
|
+
|
97
|
+
|
85
98
|
# An absolute {#path}.
|
86
99
|
#
|
87
100
|
# @param **options see NRSER::Types::Type#initialize
|
@@ -139,5 +152,7 @@ module NRSER::Types
|
|
139
152
|
|
140
153
|
end # class << self (Eigenclass)
|
141
154
|
|
155
|
+
POSIX_PATH_SEGMENT = path_segment name: 'POSIXPathSegment'
|
156
|
+
|
142
157
|
end # module NRSER::Types
|
143
158
|
|
data/lib/nrser/types/strings.rb
CHANGED
@@ -6,33 +6,68 @@ require 'nrser/types/attrs'
|
|
6
6
|
using NRSER
|
7
7
|
|
8
8
|
module NRSER::Types
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
9
|
+
|
10
|
+
# Eigenclass (Singleton Class)
|
11
|
+
# ========================================================================
|
12
|
+
#
|
13
|
+
class << self
|
14
|
+
# @!group Type Factory Functions
|
15
|
+
|
16
|
+
def str length: nil, **options
|
17
|
+
if length.nil? && options.empty?
|
18
|
+
# if there are no options can point to the constant for efficiency
|
19
|
+
STR
|
16
20
|
else
|
17
|
-
|
18
|
-
IsA.new
|
19
|
-
|
20
|
-
|
21
|
+
if length.nil?
|
22
|
+
IsA.new String, from_s: ->(s) { s }, **options
|
23
|
+
else
|
24
|
+
intersection \
|
25
|
+
IsA.new( String, from_s: ->(s) { s } ),
|
26
|
+
NRSER::Types.length( length ),
|
27
|
+
**options
|
28
|
+
end
|
21
29
|
end
|
30
|
+
end # string
|
31
|
+
|
32
|
+
alias_method :string, :str
|
33
|
+
|
34
|
+
|
35
|
+
def empty_str
|
36
|
+
EMPTY_STR
|
22
37
|
end
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
38
|
+
|
39
|
+
|
40
|
+
def non_empty_str **options
|
41
|
+
return NON_EMPTY_STR if options.empty?
|
42
|
+
|
43
|
+
str( length: {min: 1}, **options )
|
44
|
+
end # .non_empty_str
|
45
|
+
|
46
|
+
|
47
|
+
private
|
48
|
+
# ========================================================================
|
49
|
+
|
50
|
+
|
51
|
+
# @todo Document make_string_type method.
|
52
|
+
#
|
53
|
+
# @param [type] arg_name
|
54
|
+
# @todo Add name param description.
|
55
|
+
#
|
56
|
+
# @return [return_type]
|
57
|
+
# @todo Document return value.
|
58
|
+
#
|
59
|
+
def make_string_type length: nil, match: nil
|
60
|
+
# method body...
|
61
|
+
end # #make_string_type
|
62
|
+
|
63
|
+
|
64
|
+
# end private
|
65
|
+
|
66
|
+
end # class << self (Eigenclass)
|
28
67
|
|
29
|
-
|
68
|
+
STR = str( name: 'StringType' ).freeze
|
30
69
|
|
31
|
-
|
32
|
-
return NON_EMPTY_STR if options.empty?
|
33
|
-
|
34
|
-
str( length: {min: 1}, **options )
|
35
|
-
end # .non_empty_str
|
70
|
+
EMPTY_STR = str( name: 'EmptyStringType', length: 0 ).freeze
|
36
71
|
|
37
72
|
NON_EMPTY_STR = non_empty_str( name: 'NonEmptyStr' ).freeze
|
38
73
|
|
data/lib/nrser/types/tuples.rb
CHANGED
data/lib/nrser/types/type.rb
CHANGED
@@ -35,7 +35,7 @@ module NRSER::Types
|
|
35
35
|
# @param [nil | #call | #to_proc] to_data:
|
36
36
|
#
|
37
37
|
#
|
38
|
-
def initialize name: nil, from_s: nil, to_data: nil
|
38
|
+
def initialize name: nil, from_s: nil, to_data: nil, from_data: nil
|
39
39
|
@name = name
|
40
40
|
@from_s = from_s
|
41
41
|
|
@@ -46,10 +46,37 @@ module NRSER::Types
|
|
46
46
|
elsif to_data.respond_to?( :to_proc )
|
47
47
|
to_data.to_proc
|
48
48
|
else
|
49
|
-
raise TypeError.
|
50
|
-
`to_data:` keyword arg must be `nil`, respond to
|
51
|
-
to
|
52
|
-
|
49
|
+
raise TypeError.new binding.erb <<-ERB
|
50
|
+
`to_data:` keyword arg must be `nil`, respond to `#call` or respond
|
51
|
+
to `#to_proc`.
|
52
|
+
|
53
|
+
Found value:
|
54
|
+
|
55
|
+
<%= to_data.pretty_inspect %>
|
56
|
+
|
57
|
+
(type <%= to_data.class %>)
|
58
|
+
|
59
|
+
ERB
|
60
|
+
end
|
61
|
+
|
62
|
+
@from_data = if from_data.nil?
|
63
|
+
nil
|
64
|
+
elsif from_data.respond_to?( :call )
|
65
|
+
from_data
|
66
|
+
elsif from_data.respond_to?( :to_proc )
|
67
|
+
from_data.to_proc
|
68
|
+
else
|
69
|
+
raise TypeError.new binding.erb <<-ERB
|
70
|
+
`to_data:` keyword arg must be `nil`, respond to `#call` or respond
|
71
|
+
to `#to_proc`.
|
72
|
+
|
73
|
+
Found value:
|
74
|
+
|
75
|
+
<%= from_data.pretty_inspect %>
|
76
|
+
|
77
|
+
(type <%= from_data.class %>)
|
78
|
+
|
79
|
+
ERB
|
53
80
|
end
|
54
81
|
end # #initialize
|
55
82
|
|
@@ -152,6 +179,15 @@ module NRSER::Types
|
|
152
179
|
end
|
153
180
|
|
154
181
|
|
182
|
+
def from_data data
|
183
|
+
if @from_data.nil?
|
184
|
+
raise NoMethodError, "#from_data not defined"
|
185
|
+
end
|
186
|
+
|
187
|
+
check @from_data.call( data )
|
188
|
+
end
|
189
|
+
|
190
|
+
|
155
191
|
# Test if the type knows how to load values from strings.
|
156
192
|
#
|
157
193
|
# If this method returns `true`, then we expect {#from_s} to succeed.
|
@@ -176,6 +212,11 @@ module NRSER::Types
|
|
176
212
|
end # #has_to_data?
|
177
213
|
|
178
214
|
|
215
|
+
def has_from_data?
|
216
|
+
! @from_data.nil?
|
217
|
+
end
|
218
|
+
|
219
|
+
|
179
220
|
# Dumps a value of this type to "data" - structures and values suitable
|
180
221
|
# for transport and storage, such as dumping to JSON or YAML, etc.
|
181
222
|
#
|
@@ -186,7 +227,7 @@ module NRSER::Types
|
|
186
227
|
# The data representation of the value.
|
187
228
|
#
|
188
229
|
def to_data value
|
189
|
-
if @
|
230
|
+
if @to_data.nil?
|
190
231
|
raise NoMethodError, "#to_data not defined"
|
191
232
|
end
|
192
233
|
|
data/lib/nrser/version.rb
CHANGED
@@ -0,0 +1,46 @@
|
|
1
|
+
|
2
|
+
module NRSER::TestFixtures::AbstractMethodError
|
3
|
+
|
4
|
+
class Base
|
5
|
+
def f
|
6
|
+
raise NRSER::AbstractMethodError.new( self, __method__ )
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Sub < Base; end
|
11
|
+
|
12
|
+
end # module NRSER::TestFixtures::AbstractMethodError
|
13
|
+
|
14
|
+
|
15
|
+
describe_class NRSER::AbstractMethodError do
|
16
|
+
|
17
|
+
context(
|
18
|
+
"when raising method is invoked through instance of defining class"
|
19
|
+
) do
|
20
|
+
|
21
|
+
it "explains that the instance's class is abstract" do
|
22
|
+
expect {
|
23
|
+
NRSER::TestFixtures::AbstractMethodError::Base.new.f
|
24
|
+
}.to raise_error(
|
25
|
+
NRSER::AbstractMethodError,
|
26
|
+
/Base is an abstract class/
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
end # when raising method is invoked through instance of defining class
|
31
|
+
|
32
|
+
context "when raising method is invoked through instance of a subclass" do
|
33
|
+
|
34
|
+
it "explains that an implementing class needs to be found or written" do
|
35
|
+
expect {
|
36
|
+
NRSER::TestFixtures::AbstractMethodError::Sub.new.f
|
37
|
+
}.to raise_error(
|
38
|
+
NRSER::AbstractMethodError,
|
39
|
+
/find a subclass of .*Sub to instantiate or write your own/
|
40
|
+
)
|
41
|
+
end
|
42
|
+
|
43
|
+
end # when raising method is invoked through instance of a subclass
|
44
|
+
|
45
|
+
|
46
|
+
end # Class NRSER::AbstractMethodError Description
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'nrser/meta/props'
|
2
|
+
|
3
|
+
require 'nrser/refinements'
|
4
|
+
using NRSER
|
5
|
+
|
6
|
+
require 'nrser/refinements/types'
|
7
|
+
using NRSER::Types
|
8
|
+
|
9
|
+
|
10
|
+
describe NRSER::Meta::Props do
|
11
|
+
|
12
|
+
describe_section "to and from data" do
|
13
|
+
# ========================================================================
|
14
|
+
|
15
|
+
before( :all ) {
|
16
|
+
@cat_class = Class.new( NRSER::Meta::Props::Base ) do
|
17
|
+
prop :name, type: t.non_empty_str
|
18
|
+
prop :breed, type: t.non_empty_str
|
19
|
+
prop :age, type: t.unsigned
|
20
|
+
|
21
|
+
def self.name; 'Cat'; end
|
22
|
+
end
|
23
|
+
}
|
24
|
+
|
25
|
+
describe_group "simple nesting" do
|
26
|
+
|
27
|
+
before( :all ) {
|
28
|
+
# IMPORTANT!!! must bind *outside* the class declaration; can't use
|
29
|
+
# @cat_class in there because it resolves to a (nil)
|
30
|
+
# instance variable of the new class.
|
31
|
+
cat_class = @cat_class
|
32
|
+
|
33
|
+
@owner_class = Class.new( NRSER::Meta::Props::Base ) do
|
34
|
+
prop :name, type: t.non_empty_str
|
35
|
+
|
36
|
+
prop :cat, type: cat_class
|
37
|
+
|
38
|
+
def self.name; 'Owner'; end
|
39
|
+
end
|
40
|
+
|
41
|
+
@cat = @cat_class.new name: "Hooty", breed: "American Shorthair", age: 2
|
42
|
+
|
43
|
+
@owner = @owner_class.new name: "Neil", cat: @cat
|
44
|
+
}
|
45
|
+
|
46
|
+
it "is setup correctly" do
|
47
|
+
expect( @cat_class ).to be_a Class
|
48
|
+
expect( @cat_class.is_a? ::Class ).to be true
|
49
|
+
expect( @cat_class.name ).to eq "Cat"
|
50
|
+
expect( t.make @cat_class ).to be_a NRSER::Types::IsA
|
51
|
+
|
52
|
+
name_prop = @owner_class.props[:name]
|
53
|
+
expect( name_prop.type ).to be t.non_empty_str
|
54
|
+
|
55
|
+
cat_prop = @owner_class.props[:cat]
|
56
|
+
expect( cat_prop.type ).to be_a NRSER::Types::IsA
|
57
|
+
expect( cat_prop.type.klass ).to be @cat_class
|
58
|
+
expect( @cat_class.respond_to? :from_data ).to be true
|
59
|
+
expect( cat_prop.type.has_from_data? ).to be true
|
60
|
+
end
|
61
|
+
|
62
|
+
it "dumps to and loads from data" do
|
63
|
+
data = @owner.to_data
|
64
|
+
restored = @owner_class.from_data data
|
65
|
+
expect( restored.to_data ).to eq data
|
66
|
+
end
|
67
|
+
|
68
|
+
end # Group "simple nesting" Description
|
69
|
+
|
70
|
+
|
71
|
+
end # section to and from data
|
72
|
+
# ************************************************************************
|
73
|
+
|
74
|
+
end
|