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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a096ba06bf0367538de51b0411db1b77e2bcd8e
|
4
|
+
data.tar.gz: fc229e219af929a41b380c820e3fb797bc3f823f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 02e902e272bf5ab0b42d74d903fdb4179fd82f3476a25a92bba11d5c6ea7a8812b4e08652355be0179eb7873f0ae344296fa93b4639cb79020f32e61b7cf143a
|
7
|
+
data.tar.gz: 343deeec75ba3b162c8fea5aa26a342cf20c3e66df228250dfae60e2a1214962b475d6322a710b15d797ea99b4d7d4b4efb7c18513b907ed0484ce07886dc8fc
|
data/lib/nrser.rb
CHANGED
@@ -11,6 +11,7 @@ require_relative './nrser/proc'
|
|
11
11
|
require_relative './nrser/collection'
|
12
12
|
require_relative './nrser/object'
|
13
13
|
require_relative './nrser/string'
|
14
|
+
require_relative './nrser/text'
|
14
15
|
require_relative './nrser/binding'
|
15
16
|
require_relative './nrser/exception'
|
16
17
|
require_relative './nrser/enumerable'
|
data/lib/nrser/array.rb
CHANGED
@@ -12,4 +12,19 @@ module NRSER
|
|
12
12
|
array[1..-1]
|
13
13
|
end # .rest
|
14
14
|
|
15
|
+
|
16
|
+
# A destructive partition.
|
17
|
+
def self.extract_from_array! array, &block
|
18
|
+
extracted = []
|
19
|
+
array.reject! { |entry|
|
20
|
+
test = block.call entry
|
21
|
+
if test
|
22
|
+
extracted << entry
|
23
|
+
end
|
24
|
+
test
|
25
|
+
}
|
26
|
+
extracted
|
27
|
+
end
|
28
|
+
|
29
|
+
|
15
30
|
end # module NRSER
|
data/lib/nrser/binding.rb
CHANGED
@@ -5,7 +5,13 @@ module NRSER
|
|
5
5
|
|
6
6
|
def erb bnd, str
|
7
7
|
require 'erb'
|
8
|
-
|
8
|
+
|
9
|
+
filter_repeated_blank_lines(
|
10
|
+
with_indent_tagged( dedent( str ) ) { |tagged_str|
|
11
|
+
ERB.new( tagged_str ).result( bnd )
|
12
|
+
},
|
13
|
+
remove_leading: true
|
14
|
+
)
|
9
15
|
end # erb
|
10
16
|
|
11
17
|
alias_method :template, :erb
|
data/lib/nrser/enumerable.rb
CHANGED
@@ -107,6 +107,26 @@ module NRSER
|
|
107
107
|
end # #find_only
|
108
108
|
|
109
109
|
|
110
|
+
# Return the only entry if the enumerable has `#count` one. Otherwise,
|
111
|
+
# return `default` (which defaults to `nil`).
|
112
|
+
#
|
113
|
+
# @param [Enumerable] enum
|
114
|
+
# Enumerable in question.
|
115
|
+
#
|
116
|
+
# @param [Object] default:
|
117
|
+
# Value to return if `enum` does not have only one entry.
|
118
|
+
#
|
119
|
+
# @return [Object]
|
120
|
+
# The only entry in `enum` if it has only one, else `default`.
|
121
|
+
#
|
122
|
+
def only enum, default: nil
|
123
|
+
if enum.count == 1
|
124
|
+
enum.first
|
125
|
+
else
|
126
|
+
default
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
110
130
|
|
111
131
|
# @todo Document only method.
|
112
132
|
#
|
@@ -117,7 +137,7 @@ module NRSER
|
|
117
137
|
# @todo Document return value.
|
118
138
|
#
|
119
139
|
def only! enum
|
120
|
-
unless enum.
|
140
|
+
unless enum.count == 1
|
121
141
|
raise TypeError.new squish <<-END
|
122
142
|
Expected enumerable #{ enum.inspect } to have exactly one entry.
|
123
143
|
END
|
data/lib/nrser/errors.rb
CHANGED
@@ -1,9 +1,59 @@
|
|
1
|
-
|
2
1
|
module NRSER
|
3
|
-
|
4
|
-
class Error < StandardError; end
|
5
2
|
|
6
|
-
|
7
|
-
|
8
|
-
end
|
3
|
+
# Indicates some piece of application state is in conflict with the attempted
|
4
|
+
# operation.
|
5
|
+
class ConflictError < StandardError; end
|
6
|
+
|
7
|
+
|
8
|
+
# Extension of Ruby's {NotImplementedError} to provide a useful message
|
9
|
+
# and convenient constructor for abstract methods.
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
#
|
13
|
+
# def f
|
14
|
+
# raise NRSER::AbstractMethodError.new( self, __method__ )
|
15
|
+
#
|
16
|
+
#
|
17
|
+
class AbstractMethodError < NotImplementedError
|
18
|
+
|
19
|
+
# Construct a new `AbstractMethodError`.
|
20
|
+
#
|
21
|
+
# @param [Object] instance
|
22
|
+
# Instance that invoked the abstract method.
|
23
|
+
#
|
24
|
+
# @param [Symbol | String] method_name
|
25
|
+
# Name of abstract method.
|
26
|
+
#
|
27
|
+
def initialize instance, method_name
|
28
|
+
@instance = instance
|
29
|
+
@method_name = method_name
|
30
|
+
@method = instance.method @method_name
|
31
|
+
|
32
|
+
message = if @method.owner == instance.class
|
33
|
+
NRSER.dedent <<-END
|
34
|
+
Method #{ @method.owner.name }##{ @method_name } is abstract, meaning
|
35
|
+
#{ @method.owner.name } is an abstract class and the invoking
|
36
|
+
instance #{ @instance } should NOT have been constructed.
|
37
|
+
END
|
38
|
+
else
|
39
|
+
NRSER.squish <<-END
|
40
|
+
Method #{ @method.owner.name }##{ @method_name } is abstract and
|
41
|
+
has not been implemented in invoking class #{ @instance.class }.
|
42
|
+
|
43
|
+
If you *are* developing the invoking class #{ @instance.class } it
|
44
|
+
(or a parent class between it and #{ @method.owner.name }) must
|
45
|
+
implement ##{ @method_name }.
|
46
|
+
|
47
|
+
If you *are not* developing #{ @instance.class } it should be treated
|
48
|
+
as an abstract base class and should NOT be constructed. You need to
|
49
|
+
find a subclass of #{ @instance.class } to instantiate or write
|
50
|
+
your own.
|
51
|
+
END
|
52
|
+
end
|
53
|
+
|
54
|
+
super message
|
55
|
+
end # #initialize
|
56
|
+
|
57
|
+
end # class AbstractMethodError
|
9
58
|
|
59
|
+
end # module NRSER
|
@@ -42,7 +42,7 @@ module NRSER
|
|
42
42
|
other_value.is_a?(Hash)
|
43
43
|
deep_merge this_value, other_value, &block
|
44
44
|
else
|
45
|
-
if block_given? && key?(current_key)
|
45
|
+
if block_given? && base_hash.key?( current_key )
|
46
46
|
block.call(current_key, this_value, other_value)
|
47
47
|
else
|
48
48
|
other_value
|
data/lib/nrser/message.rb
CHANGED
@@ -20,6 +20,39 @@ module NRSER
|
|
20
20
|
# sent to an object as a method call, especially in testing.
|
21
21
|
#
|
22
22
|
class Message
|
23
|
+
# Class Methods
|
24
|
+
# =====================================================================
|
25
|
+
|
26
|
+
# Instantiate a message from the arguments, unless they already are one.
|
27
|
+
#
|
28
|
+
# @overload from symbol, *args, &block
|
29
|
+
# Create a new instance from the arguments by passing them to
|
30
|
+
# {NRSER::Message.new}.
|
31
|
+
#
|
32
|
+
# @param [NRSER::Message] message
|
33
|
+
# An already instantiated message, which is simple returned.
|
34
|
+
#
|
35
|
+
# @return [NRSER::Message]
|
36
|
+
# Message created from the arguments.
|
37
|
+
#
|
38
|
+
# @overload from message
|
39
|
+
# Convenience method to return the message if it's the only argument.
|
40
|
+
#
|
41
|
+
# @param [NRSER::Message] message
|
42
|
+
# An already instantiated message, which is simple returned.
|
43
|
+
#
|
44
|
+
# @return [NRSER::Message]
|
45
|
+
# The `message` argument.
|
46
|
+
#
|
47
|
+
def self.from *args, &block
|
48
|
+
if args.length == 1 && args[0].is_a?( Message )
|
49
|
+
args[0]
|
50
|
+
else
|
51
|
+
new *args, &block
|
52
|
+
end
|
53
|
+
end # .from
|
54
|
+
|
55
|
+
|
23
56
|
# Name of method the message is for.
|
24
57
|
#
|
25
58
|
# @return [Symbol | String]
|
data/lib/nrser/meta/props.rb
CHANGED
@@ -10,7 +10,8 @@ module NRSER
|
|
10
10
|
module Meta
|
11
11
|
|
12
12
|
module Props
|
13
|
-
|
13
|
+
DEFAULT_CLASS_KEY = '__class__';
|
14
|
+
|
14
15
|
PROPS_VARIABLE_NAME = :@__NRSER_props
|
15
16
|
PROP_VALUES_VARIABLE_NAME = :@__NRSER_prop_values
|
16
17
|
|
@@ -51,27 +52,67 @@ module Props
|
|
51
52
|
# reasonable to use on untrusted data.
|
52
53
|
#
|
53
54
|
# @param [Hash<String, Object>] data
|
55
|
+
# Data hash to load from.
|
54
56
|
#
|
55
|
-
# @
|
56
|
-
# @todo Document return value.
|
57
|
-
#
|
57
|
+
# @param
|
58
58
|
#
|
59
|
+
# @return [NRSER::Meta::Props]
|
60
|
+
# Instance of a propertied class.
|
59
61
|
#
|
60
|
-
def self.
|
62
|
+
def self.UNSAFE_load_instance_from_data data, class_key: DEFAULT_CLASS_KEY
|
61
63
|
t.hash_.check data
|
62
64
|
|
63
|
-
unless data.key?(
|
64
|
-
raise ArgumentError.new <<-
|
65
|
-
Data is missing
|
65
|
+
unless data.key?( class_key )
|
66
|
+
raise ArgumentError.new binding.erb <<-ERB
|
67
|
+
Data is missing <%= class_key %> key - no idea what class to
|
68
|
+
instantiate.
|
69
|
+
|
70
|
+
Data:
|
66
71
|
|
67
|
-
|
68
|
-
|
72
|
+
<%= data.pretty_inspect %>
|
73
|
+
|
74
|
+
ERB
|
69
75
|
end
|
70
76
|
|
71
|
-
|
77
|
+
# Get the class name from the data hash using the key, checking that it's
|
78
|
+
# a non-empty string.
|
79
|
+
class_name = t.non_empty_str.check data[class_key]
|
80
|
+
|
81
|
+
# Resolve the constant at that name.
|
72
82
|
klass = class_name.to_const
|
83
|
+
|
84
|
+
# Make sure it's one of ours
|
85
|
+
unless klass.included_modules.include?( NRSER::Meta::Props )
|
86
|
+
raise ArgumentError.new binding.erb <<-ERB
|
87
|
+
Can not load instance from data - bad class name.
|
88
|
+
|
89
|
+
Extracted class name
|
90
|
+
|
91
|
+
<%= class_name.inspect %>
|
92
|
+
|
93
|
+
from class key
|
94
|
+
|
95
|
+
<%= class_key.inspect %>
|
96
|
+
|
97
|
+
which resolved to constant
|
98
|
+
|
99
|
+
<%= klass.inspect %>
|
100
|
+
|
101
|
+
but that class does not include the NRSER::Meta::Props mixin, which we
|
102
|
+
check for to help protect against executing an unrelated `.from_data`
|
103
|
+
class method when attempting to load.
|
104
|
+
|
105
|
+
Data:
|
106
|
+
|
107
|
+
<%= data.pretty_inspect %>
|
108
|
+
|
109
|
+
ERB
|
110
|
+
end
|
111
|
+
|
112
|
+
# Kick off the restore and return the result
|
73
113
|
klass.from_data data
|
74
|
-
|
114
|
+
|
115
|
+
end # .UNSAFE_load_instance_from_data
|
75
116
|
|
76
117
|
|
77
118
|
# Hook to extend the including class with {NRSER::Meta::Props:ClassMethods}
|
@@ -176,7 +217,24 @@ module Props
|
|
176
217
|
# @return [self]
|
177
218
|
#
|
178
219
|
def from_data data
|
179
|
-
|
220
|
+
values = {}
|
221
|
+
props = self.props
|
222
|
+
|
223
|
+
data.each { |data_key, data_value|
|
224
|
+
prop_key = case data_key
|
225
|
+
when Symbol
|
226
|
+
data_key
|
227
|
+
when String
|
228
|
+
data_key.to_sym
|
229
|
+
end
|
230
|
+
|
231
|
+
if prop_key &&
|
232
|
+
prop = props[prop_key]
|
233
|
+
values[prop_key] = prop.value_from_data data_value
|
234
|
+
end
|
235
|
+
}
|
236
|
+
|
237
|
+
self.new values
|
180
238
|
end # #from_data
|
181
239
|
|
182
240
|
|
@@ -240,14 +298,18 @@ module Props
|
|
240
298
|
# @return [Hash<String, Object>]
|
241
299
|
# @todo Document return value.
|
242
300
|
#
|
243
|
-
def to_data only_own: false,
|
301
|
+
def to_data only_own: false,
|
302
|
+
only_primary: false,
|
303
|
+
add_class: true,
|
304
|
+
class_key: NRSER::Meta::Props::DEFAULT_CLASS_KEY
|
305
|
+
|
244
306
|
self.class.props(only_own: false, only_primary: false).
|
245
307
|
map { |name, prop|
|
246
308
|
[name.to_s, prop.to_data(self)]
|
247
309
|
}.
|
248
310
|
to_h.
|
249
311
|
tap { |hash|
|
250
|
-
hash[
|
312
|
+
hash[class_key] = self.class.name if add_class
|
251
313
|
}
|
252
314
|
end # #to_data
|
253
315
|
|
@@ -1,4 +1,18 @@
|
|
1
|
-
|
1
|
+
# Requirements
|
2
|
+
# =======================================================================
|
3
|
+
|
4
|
+
# Stdlib
|
5
|
+
# -----------------------------------------------------------------------
|
6
|
+
|
7
|
+
# Deps
|
8
|
+
# -----------------------------------------------------------------------
|
9
|
+
|
10
|
+
# Project / Package
|
11
|
+
# -----------------------------------------------------------------------
|
12
|
+
|
13
|
+
|
14
|
+
# Refinements
|
15
|
+
# =======================================================================
|
2
16
|
|
3
17
|
require 'nrser/refinements'
|
4
18
|
using NRSER
|
@@ -6,41 +20,188 @@ using NRSER
|
|
6
20
|
require 'nrser/refinements/types'
|
7
21
|
using NRSER::Types
|
8
22
|
|
9
|
-
|
10
|
-
|
11
|
-
|
23
|
+
|
24
|
+
# Declarations
|
25
|
+
# =======================================================================
|
26
|
+
|
27
|
+
module NRSER; end
|
28
|
+
module NRSER::Meta; end
|
29
|
+
module NRSER::Meta::Props; end
|
30
|
+
|
31
|
+
|
32
|
+
# Definitions
|
33
|
+
# =======================================================================
|
34
|
+
|
35
|
+
# `Prop` instances hold the configuration for a property defined on propertied
|
36
|
+
# classes.
|
37
|
+
#
|
38
|
+
# Props are immutable by design.
|
39
|
+
#
|
40
|
+
class NRSER::Meta::Props::Prop
|
41
|
+
|
42
|
+
# The class the prop was defined in.
|
43
|
+
#
|
44
|
+
# @return [Class]
|
45
|
+
#
|
46
|
+
attr_reader :defined_in
|
12
47
|
|
13
|
-
class Prop
|
14
|
-
attr_accessor :defined_in,
|
15
|
-
:name,
|
16
|
-
:type,
|
17
|
-
:source
|
18
48
|
|
49
|
+
# The name of the prop, which is used as it's method name and key where
|
50
|
+
# applicable.
|
51
|
+
#
|
52
|
+
# @return [Symbol]
|
53
|
+
#
|
54
|
+
attr_reader :name
|
19
55
|
|
56
|
+
|
57
|
+
# The type of the valid values for the property.
|
58
|
+
#
|
59
|
+
# @return [NRSER::Types::Type]
|
60
|
+
#
|
61
|
+
attr_reader :type
|
62
|
+
|
63
|
+
|
64
|
+
# Optional name of instance variable (including the `@` prefix) or getter
|
65
|
+
# method (method that takes no arguments) that provides the property's
|
66
|
+
# value.
|
67
|
+
#
|
68
|
+
# Props that have a source are considered *derived*, those that don't are
|
69
|
+
# called *primary*.
|
70
|
+
#
|
71
|
+
# @return [Symbol | String]
|
72
|
+
#
|
73
|
+
attr_reader :source
|
74
|
+
|
75
|
+
|
76
|
+
# Constructor
|
77
|
+
# =====================================================================
|
78
|
+
|
79
|
+
# Instantiate a new `Prop` instance.
|
80
|
+
#
|
81
|
+
# You should not need to construct a `Prop` directly unless you are doing
|
82
|
+
# custom meta-programming - they should be constructed for you via the
|
83
|
+
# `.prop` "macro" defined at {NRSER::Meta::Props::ClassMethods#prop}
|
84
|
+
# that is extended in to classes including {NRSER::Meta::Props}.
|
85
|
+
#
|
20
86
|
def initialize defined_in,
|
21
87
|
name,
|
22
88
|
type: t.any,
|
23
|
-
default:
|
89
|
+
default: nil,
|
90
|
+
default_from: nil,
|
24
91
|
source: nil,
|
25
|
-
to_data: nil
|
92
|
+
to_data: nil,
|
93
|
+
from_data: nil
|
26
94
|
|
95
|
+
# Set these up first so {#to_s} works in case we need to raise errors.
|
27
96
|
@defined_in = defined_in
|
28
97
|
@name = name
|
29
98
|
@type = t.make type
|
30
|
-
|
31
|
-
@default = default
|
99
|
+
|
32
100
|
@to_data = to_data
|
101
|
+
@from_data = from_data
|
102
|
+
|
103
|
+
# Source
|
33
104
|
|
34
|
-
|
105
|
+
@source = source # TODO fix this: t.maybe( t.label ).check source
|
106
|
+
|
107
|
+
# Detect if the source
|
108
|
+
if source.nil?
|
35
109
|
@instance_variable_source = false
|
36
110
|
else
|
111
|
+
# TODO Check that default and default_from are `nil`, make no sense here
|
112
|
+
|
37
113
|
source_str = source.to_s
|
38
114
|
@instance_variable_source = source_str[0] == '@'
|
39
115
|
end
|
40
|
-
|
116
|
+
|
117
|
+
# Defaults
|
118
|
+
|
119
|
+
# Can't provide both default and default_from
|
120
|
+
unless default.nil? || default_from.nil?
|
121
|
+
raise NRSER::ConflictError.new binding.erb <<-ERB
|
122
|
+
Both `default:` and `default_from:` keyword args provided when
|
123
|
+
constructing <%= self %>. At least one must be `nil`.
|
124
|
+
|
125
|
+
default:
|
126
|
+
<%= default.pretty_inspect %>
|
127
|
+
|
128
|
+
default_from:
|
129
|
+
<%= default_from.pretty_inspect %>
|
130
|
+
|
131
|
+
ERB
|
132
|
+
end
|
133
|
+
|
134
|
+
if default_from.nil?
|
135
|
+
# We are going to use `default`
|
136
|
+
|
137
|
+
# Validate `default` value
|
138
|
+
if default.nil?
|
139
|
+
# If it's `nil` we still want to see if it's a valid value for the type
|
140
|
+
# so we can report if this prop has a default value or not.
|
141
|
+
#
|
142
|
+
# However, we only need to do that if there is no `source`
|
143
|
+
#
|
144
|
+
@has_default = if source.nil?
|
145
|
+
@type.test default
|
146
|
+
else
|
147
|
+
# NOTE This is up for debate... does a derived property have a
|
148
|
+
# default? What does that even mean?
|
149
|
+
true # false ?
|
150
|
+
end
|
151
|
+
|
152
|
+
else
|
153
|
+
# Check that the default value is valid for the type, raising TypeError
|
154
|
+
# if it isn't.
|
155
|
+
@type.check( default ) { |type:, value:|
|
156
|
+
binding.erb <<-ERB
|
157
|
+
Default value is not valid for <%= self %>:
|
158
|
+
|
159
|
+
<%= value.pretty_inspect %>
|
160
|
+
|
161
|
+
ERB
|
162
|
+
}
|
163
|
+
|
164
|
+
# If we passed the check we know the value is valid
|
165
|
+
@has_default = true
|
166
|
+
|
167
|
+
# Set the default value to `default`, freezing it since it will be
|
168
|
+
# set on instances without any attempt at duplication, which seems like
|
169
|
+
# it *might be ok* since a lot of prop'd classes are being used
|
170
|
+
# immutably.
|
171
|
+
@default_value = default.freeze
|
172
|
+
end
|
173
|
+
|
174
|
+
else
|
175
|
+
# `default_from` is not `nil`, so we're going to use that.
|
176
|
+
|
177
|
+
# This means we "have" a default since we believe we can use it to make
|
178
|
+
# one - the actual values will have to be validates at that point.
|
179
|
+
@has_default = true
|
180
|
+
|
181
|
+
# And set it.
|
182
|
+
#
|
183
|
+
# TODO validate it's something reasonable here?
|
184
|
+
#
|
185
|
+
@default_from = default_from
|
186
|
+
end
|
187
|
+
|
188
|
+
end # #initialize
|
189
|
+
|
190
|
+
|
191
|
+
# Full name with class prop was defined in.
|
192
|
+
#
|
193
|
+
# @example
|
194
|
+
# MyMod::SomeClass.props[:that_prop].full_name
|
195
|
+
# # => 'MyMod::SomeClass#full_name'
|
196
|
+
#
|
197
|
+
# @return [String]
|
198
|
+
#
|
199
|
+
def full_name
|
200
|
+
"#{ defined_in.name }##{ name }"
|
201
|
+
end # #full_name
|
41
202
|
|
42
203
|
|
43
|
-
#
|
204
|
+
# Test if this prop is configured to provide default values -
|
44
205
|
#
|
45
206
|
# @param [type] arg_name
|
46
207
|
# @todo Add name param description.
|
@@ -48,9 +209,12 @@ class Prop
|
|
48
209
|
# @return [return_type]
|
49
210
|
# @todo Document return value.
|
50
211
|
#
|
51
|
-
def
|
52
|
-
@
|
53
|
-
end # #
|
212
|
+
def has_default?
|
213
|
+
@has_default
|
214
|
+
end # #has_default?
|
215
|
+
|
216
|
+
# Old name
|
217
|
+
alias_method :default?, :has_default?
|
54
218
|
|
55
219
|
|
56
220
|
def default
|
@@ -143,10 +307,19 @@ class Prop
|
|
143
307
|
# @todo Document return value.
|
144
308
|
#
|
145
309
|
def set instance, value
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
310
|
+
type.check( value ) do
|
311
|
+
binding.erb <<-END
|
312
|
+
Value of type <%= value.class.name %> for prop <%= self.full_name %>
|
313
|
+
failed type check.
|
314
|
+
|
315
|
+
Must satisfy type:
|
316
|
+
|
317
|
+
<%= type %>
|
318
|
+
|
319
|
+
Given value:
|
320
|
+
|
321
|
+
<%= value.pretty_inspect %>
|
322
|
+
|
150
323
|
END
|
151
324
|
end
|
152
325
|
|
@@ -167,16 +340,20 @@ class Prop
|
|
167
340
|
set instance, values[name]
|
168
341
|
else
|
169
342
|
if default?
|
170
|
-
set instance, if !default.nil? && default.respond_to?( :dup )
|
171
|
-
|
172
|
-
else
|
173
|
-
|
174
|
-
end
|
343
|
+
# set instance, if !default.nil? && default.respond_to?( :dup )
|
344
|
+
# default.dup
|
345
|
+
# else
|
346
|
+
# default
|
347
|
+
# end
|
348
|
+
set instance, default
|
175
349
|
else
|
176
|
-
raise TypeError.new
|
177
|
-
Prop
|
178
|
-
|
179
|
-
|
350
|
+
raise TypeError.new binding.erb <<-ERB
|
351
|
+
Prop <#= full_name %> has no default value and no value was provided
|
352
|
+
in values:
|
353
|
+
|
354
|
+
<%= values.pretty_inspect %>
|
355
|
+
|
356
|
+
ERB
|
180
357
|
end
|
181
358
|
end
|
182
359
|
end # #set_from_hash
|
@@ -251,14 +428,69 @@ class Prop
|
|
251
428
|
end # #to_data
|
252
429
|
|
253
430
|
|
431
|
+
# @todo Document value_from_data method.
|
432
|
+
#
|
433
|
+
# @param [type] arg_name
|
434
|
+
# @todo Add name param description.
|
435
|
+
#
|
436
|
+
# @return [return_type]
|
437
|
+
# @todo Document return value.
|
438
|
+
#
|
439
|
+
def value_from_data data
|
440
|
+
value = case @from_data
|
441
|
+
when nil
|
442
|
+
# This {Prop} does not have any custom `from_data` instructions, which
|
443
|
+
# means we must rely on the {#type} to covert *data* to a *value*.
|
444
|
+
#
|
445
|
+
if type.has_from_data?
|
446
|
+
type.from_data data
|
447
|
+
else
|
448
|
+
data
|
449
|
+
end
|
450
|
+
|
451
|
+
when Symbol, String
|
452
|
+
# The custom `from_data` configuration specifies a string or symbol name,
|
453
|
+
# which we interpret as a class method on the defining class and call
|
454
|
+
# with the data to produce a value.
|
455
|
+
@defined_in.send @to_data, data
|
456
|
+
|
457
|
+
when Proc
|
458
|
+
# The custom `from_data` configuration provides a procedure, which we
|
459
|
+
# call with the data to produce the value.
|
460
|
+
@from_data.call data
|
461
|
+
|
462
|
+
else
|
463
|
+
raise TypeError.new binding.erb <<-ERB
|
464
|
+
Expected `@from_data` to be Symbol, String or Proc;
|
465
|
+
found <%= @from_data.class %>.
|
466
|
+
|
467
|
+
Acceptable types:
|
468
|
+
|
469
|
+
- Symbol or String
|
470
|
+
- Name of class method on the class this property is defined in
|
471
|
+
(<%= @defined_in %>) to call with data to convert it to a
|
472
|
+
property value.
|
473
|
+
|
474
|
+
- Proc
|
475
|
+
- Procedure to call with data to convert it to a property value.
|
476
|
+
|
477
|
+
Found `@from_data`:
|
478
|
+
|
479
|
+
<%= @from_data.pretty_inspect %>
|
480
|
+
|
481
|
+
(type <%= @from_data.class %>)
|
482
|
+
|
483
|
+
ERB
|
484
|
+
end
|
485
|
+
|
486
|
+
end # #value_from_data
|
487
|
+
|
488
|
+
|
254
489
|
# @return [String]
|
255
490
|
# a short string describing the instance.
|
256
491
|
#
|
257
492
|
def to_s
|
258
|
-
|
259
|
-
#<#{ self.class.name }
|
260
|
-
#{ @defined_in.name }##{ @name }:#{ @type }>
|
261
|
-
END
|
493
|
+
"#<#{ self.class.name } #{ full_name }:#{ type }>"
|
262
494
|
end # #to_s
|
263
495
|
|
264
496
|
|
@@ -273,15 +505,15 @@ class Prop
|
|
273
505
|
# @todo Document return value.
|
274
506
|
#
|
275
507
|
def values instance
|
276
|
-
unless instance.instance_variable_defined?
|
277
|
-
|
508
|
+
unless instance.instance_variable_defined?(
|
509
|
+
NRSER::Meta::Props::PROP_VALUES_VARIABLE_NAME
|
510
|
+
)
|
511
|
+
instance.instance_variable_set \
|
512
|
+
NRSER::Meta::Props::PROP_VALUES_VARIABLE_NAME, {}
|
278
513
|
end
|
279
514
|
|
280
|
-
instance.instance_variable_get
|
515
|
+
instance.instance_variable_get \
|
516
|
+
NRSER::Meta::Props::PROP_VALUES_VARIABLE_NAME
|
281
517
|
end # #value
|
282
518
|
|
283
|
-
end # class Prop
|
284
|
-
|
285
|
-
end # module Props
|
286
|
-
end # module Meta
|
287
|
-
end # module NRSER
|
519
|
+
end # class NRSER::Meta::Props::Prop
|