nrser 0.0.26 → 0.0.27
Sign up to get free protection for your applications and to get access to all the features.
- 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
|