nrser 0.0.26 → 0.0.27

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