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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/lib/nrser.rb +1 -0
  3. data/lib/nrser/array.rb +15 -0
  4. data/lib/nrser/binding.rb +7 -1
  5. data/lib/nrser/enumerable.rb +21 -1
  6. data/lib/nrser/errors.rb +56 -6
  7. data/lib/nrser/hash/deep_merge.rb +1 -1
  8. data/lib/nrser/message.rb +33 -0
  9. data/lib/nrser/meta/props.rb +77 -15
  10. data/lib/nrser/meta/props/prop.rb +276 -44
  11. data/lib/nrser/proc.rb +7 -3
  12. data/lib/nrser/refinements/array.rb +5 -0
  13. data/lib/nrser/refinements/enumerable.rb +5 -0
  14. data/lib/nrser/refinements/hash.rb +8 -0
  15. data/lib/nrser/refinements/object.rb +11 -1
  16. data/lib/nrser/refinements/string.rb +17 -3
  17. data/lib/nrser/refinements/symbol.rb +8 -0
  18. data/lib/nrser/refinements/tree.rb +22 -0
  19. data/lib/nrser/rspex.rb +312 -70
  20. data/lib/nrser/rspex/shared_examples.rb +116 -0
  21. data/lib/nrser/string.rb +159 -27
  22. data/lib/nrser/temp/unicode_math.rb +48 -0
  23. data/lib/nrser/text.rb +3 -0
  24. data/lib/nrser/text/indentation.rb +210 -0
  25. data/lib/nrser/text/lines.rb +52 -0
  26. data/lib/nrser/text/word_wrap.rb +29 -0
  27. data/lib/nrser/tree.rb +4 -78
  28. data/lib/nrser/tree/each_branch.rb +76 -0
  29. data/lib/nrser/tree/map_branches.rb +91 -0
  30. data/lib/nrser/tree/map_tree.rb +97 -0
  31. data/lib/nrser/tree/transform.rb +56 -13
  32. data/lib/nrser/types.rb +1 -0
  33. data/lib/nrser/types/array.rb +15 -3
  34. data/lib/nrser/types/is_a.rb +40 -1
  35. data/lib/nrser/types/nil.rb +17 -0
  36. data/lib/nrser/types/paths.rb +17 -2
  37. data/lib/nrser/types/strings.rb +57 -22
  38. data/lib/nrser/types/tuples.rb +5 -0
  39. data/lib/nrser/types/type.rb +47 -6
  40. data/lib/nrser/version.rb +1 -1
  41. data/spec/nrser/errors/abstract_method_error_spec.rb +46 -0
  42. data/spec/nrser/meta/props/to_and_from_data_spec.rb +74 -0
  43. data/spec/nrser/meta/props_spec.rb +6 -2
  44. data/spec/nrser/refinements/erb_spec.rb +100 -1
  45. data/spec/nrser/{common_prefix_spec.rb → string/common_prefix_spec.rb} +9 -0
  46. data/spec/nrser/text/dedent_spec.rb +80 -0
  47. data/spec/nrser/tree/map_branch_spec.rb +83 -0
  48. data/spec/nrser/tree/map_tree_spec.rb +123 -0
  49. data/spec/nrser/tree/transform_spec.rb +26 -29
  50. data/spec/nrser/tree/transformer_spec.rb +179 -0
  51. data/spec/nrser/types/paths_spec.rb +73 -45
  52. data/spec/spec_helper.rb +10 -0
  53. metadata +27 -7
  54. data/spec/nrser/dedent_spec.rb +0 -36
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 5695621d94d38882e9dcce612c022ac723febf5e
4
- data.tar.gz: 825fe26548e53f4f8a72138fabb9554832392c8a
3
+ metadata.gz: 9a096ba06bf0367538de51b0411db1b77e2bcd8e
4
+ data.tar.gz: fc229e219af929a41b380c820e3fb797bc3f823f
5
5
  SHA512:
6
- metadata.gz: 35da0260954c54d63cb02aadd26d110810bdcf776bfc9e7dfe5966512411d9b42b6a2d49474e4db3025891b976b9d8c72f140c440f96a402931b5141811b2fd6
7
- data.tar.gz: 17a5658d7d56c4a93a8f1eda5fa8782f41452fbc64a4fb195daeca9e1b22fe5e34f6329b595db6e73a23651e61f2b3ab47cec66e74e90a20a2d3d37819ada1b5
6
+ metadata.gz: 02e902e272bf5ab0b42d74d903fdb4179fd82f3476a25a92bba11d5c6ea7a8812b4e08652355be0179eb7873f0ae344296fa93b4639cb79020f32e61b7cf143a
7
+ data.tar.gz: 343deeec75ba3b162c8fea5aa26a342cf20c3e66df228250dfae60e2a1214962b475d6322a710b15d797ea99b4d7d4b4efb7c18513b907ed0484ce07886dc8fc
@@ -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'
@@ -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
@@ -5,7 +5,13 @@ module NRSER
5
5
 
6
6
  def erb bnd, str
7
7
  require 'erb'
8
- filter_repeated_blank_lines ERB.new(dedent(str)).result(bnd)
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
@@ -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.length == 1
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
@@ -1,9 +1,59 @@
1
-
2
1
  module NRSER
3
-
4
- class Error < StandardError; end
5
2
 
6
- class ConflictError < Error; end
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
@@ -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]
@@ -10,7 +10,8 @@ module NRSER
10
10
  module Meta
11
11
 
12
12
  module Props
13
- CLASS_KEY = '__class__';
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
- # @return [Object]
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.from_data data
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?(CLASS_KEY)
64
- raise ArgumentError.new <<-END.dedent
65
- Data is missing #{ CLASS_KEY } key - no idea what class to instantiate.
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
- #{ data.pretty_inspect }
68
- END
72
+ <%= data.pretty_inspect %>
73
+
74
+ ERB
69
75
  end
70
76
 
71
- class_name = t.str.check data[CLASS_KEY]
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
- end # .from_data
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
- self.new data.symbolize_keys
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, only_primary: false, add_class: true
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[CLASS_KEY] = self.class.name if add_class
312
+ hash[class_key] = self.class.name if add_class
251
313
  }
252
314
  end # #to_data
253
315
 
@@ -1,4 +1,18 @@
1
- require 'nrser/no_arg'
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
- module NRSER
10
- module Meta
11
- module Props
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: NRSER::NO_ARG,
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
- @source = source
31
- @default = default
99
+
32
100
  @to_data = to_data
101
+ @from_data = from_data
102
+
103
+ # Source
33
104
 
34
- if @source.nil?
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
- end
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
- # @todo Document default? method.
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 default?
52
- @default != NRSER::NO_ARG
53
- end # #default?
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
- unless type.test value
147
- raise TypeError.new NRSER.squish <<-END
148
- #{ defined_in }##{ name } must be of type #{ type };
149
- found #{ value.inspect }
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
- default.dup
172
- else
173
- default
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 NRSER.squish <<-END
177
- Prop #{ name } has no default value and no value was provided in
178
- values #{ values.inspect }.
179
- END
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
- <<-END.squish
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? PROP_VALUES_VARIABLE_NAME
277
- instance.instance_variable_set PROP_VALUES_VARIABLE_NAME, {}
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 PROP_VALUES_VARIABLE_NAME
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