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
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
|