nrser 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (188) hide show
  1. checksums.yaml +4 -4
  2. data/lib/nrser/char/alpha_numeric_sub.rb +1 -2
  3. data/lib/nrser/char.rb +0 -6
  4. data/lib/nrser/core_ext/array.rb +120 -0
  5. data/lib/nrser/core_ext/binding.rb +44 -0
  6. data/lib/nrser/{functions → core_ext}/enumerable/find_map.rb +18 -15
  7. data/lib/nrser/{ext → core_ext}/enumerable.rb +10 -24
  8. data/lib/nrser/core_ext/exception.rb +30 -0
  9. data/lib/nrser/core_ext/hash/extract_values_at.rb +49 -0
  10. data/lib/nrser/core_ext/hash/transform_values_with_keys.rb +24 -0
  11. data/lib/nrser/core_ext/hash.rb +50 -0
  12. data/lib/nrser/core_ext/module/method_objects.rb +96 -0
  13. data/lib/nrser/core_ext/module/names.rb +69 -0
  14. data/lib/nrser/core_ext/module/source_locations.rb +214 -0
  15. data/lib/nrser/core_ext/module.rb +2 -0
  16. data/lib/nrser/core_ext/object/lazy_var.rb +31 -0
  17. data/lib/nrser/core_ext/object.rb +46 -0
  18. data/lib/nrser/core_ext/open_struct.rb +6 -0
  19. data/lib/nrser/{ext → core_ext}/pathname.rb +8 -5
  20. data/lib/nrser/{ext → core_ext}/string.rb +6 -12
  21. data/lib/nrser/core_ext/symbol.rb +13 -0
  22. data/lib/nrser/core_ext/time.rb +46 -0
  23. data/lib/nrser/core_ext.rb +13 -0
  24. data/lib/nrser/errors/abstract_method_error.rb +150 -0
  25. data/lib/nrser/errors/argument_error.rb +42 -0
  26. data/lib/nrser/errors/nicer_error.rb +298 -72
  27. data/lib/nrser/errors/type_error.rb +46 -0
  28. data/lib/nrser/errors.rb +4 -53
  29. data/lib/nrser/ext/tree.rb +3 -0
  30. data/lib/nrser/functions/enumerable/associate.rb +6 -9
  31. data/lib/nrser/functions/enumerable/include_slice.rb +2 -3
  32. data/lib/nrser/functions/enumerable.rb +1 -3
  33. data/lib/nrser/functions/exception.rb +1 -1
  34. data/lib/nrser/functions/hash.rb +0 -6
  35. data/lib/nrser/functions/merge_by.rb +2 -2
  36. data/lib/nrser/functions/module/method_objects.rb +77 -0
  37. data/lib/nrser/functions/module.rb +1 -2
  38. data/lib/nrser/functions/open_struct.rb +25 -35
  39. data/lib/nrser/functions/proc.rb +1 -6
  40. data/lib/nrser/functions/string/looks_like.rb +32 -1
  41. data/lib/nrser/functions/string.rb +1 -40
  42. data/lib/nrser/functions/text/lines.rb +2 -1
  43. data/lib/nrser/functions.rb +0 -1
  44. data/lib/nrser/graph/tsorter.rb +41 -0
  45. data/lib/nrser/labs/core_ext/binding.rb +37 -0
  46. data/lib/nrser/labs/stash.rb +372 -0
  47. data/lib/nrser/{logging → log}/appender/sync.rb +3 -3
  48. data/lib/nrser/log/appender.rb +3 -0
  49. data/lib/nrser/{logging → log}/formatters/color.rb +47 -20
  50. data/lib/nrser/log/formatters/mixin.rb +270 -0
  51. data/lib/nrser/{logging → log}/formatters.rb +0 -0
  52. data/lib/nrser/log/logger.rb +229 -0
  53. data/lib/nrser/log/mixin.rb +56 -0
  54. data/lib/nrser/log.rb +723 -0
  55. data/lib/nrser/message.rb +24 -3
  56. data/lib/nrser/meta/source/location.rb +158 -0
  57. data/lib/nrser/meta.rb +1 -1
  58. data/lib/nrser/props/class_methods.rb +118 -0
  59. data/lib/nrser/props/immutable/hash.rb +111 -0
  60. data/lib/nrser/props/immutable/hash_variable.rb +82 -0
  61. data/lib/nrser/props/immutable/instance_variables.rb +48 -0
  62. data/lib/nrser/props/immutable/vector.rb +107 -0
  63. data/lib/nrser/props/instance_methods.rb +184 -0
  64. data/lib/nrser/props/metadata.rb +359 -0
  65. data/lib/nrser/props/mutable/instance_variables.rb +60 -0
  66. data/lib/nrser/props/mutable/stash.rb +199 -0
  67. data/lib/nrser/{meta/props → props}/prop.rb +217 -112
  68. data/lib/nrser/props/storage/instance_variable.rb +85 -0
  69. data/lib/nrser/props/storage/instance_variables.rb +67 -0
  70. data/lib/nrser/props/storage/key.rb +88 -0
  71. data/lib/nrser/props.rb +9 -0
  72. data/lib/nrser/refinements/sugar.rb +41 -0
  73. data/lib/nrser/refinements/types.rb +2 -2
  74. data/lib/nrser/refinements.rb +14 -16
  75. data/lib/nrser/rspex/example_group/describe_attribute.rb +24 -0
  76. data/lib/nrser/rspex/example_group/describe_called_with.rb +1 -6
  77. data/lib/nrser/rspex/example_group/{describe_use_case.rb → describe_case.rb} +6 -3
  78. data/lib/nrser/rspex/example_group/describe_class.rb +1 -0
  79. data/lib/nrser/rspex/example_group/describe_group.rb +29 -0
  80. data/lib/nrser/rspex/example_group/describe_instance_method.rb +2 -2
  81. data/lib/nrser/rspex/example_group/describe_message.rb +35 -0
  82. data/lib/nrser/rspex/example_group/describe_method.rb +23 -2
  83. data/lib/nrser/rspex/example_group/describe_module.rb +19 -0
  84. data/lib/nrser/rspex/example_group/describe_response_to.rb +32 -0
  85. data/lib/nrser/rspex/example_group/describe_section.rb +38 -0
  86. data/lib/nrser/rspex/example_group/describe_sent_to.rb +52 -0
  87. data/lib/nrser/rspex/example_group/describe_source_file.rb +49 -0
  88. data/lib/nrser/rspex/example_group/describe_spec_file.rb +41 -108
  89. data/lib/nrser/rspex/example_group/describe_when.rb +14 -7
  90. data/lib/nrser/rspex/example_group/describe_x.rb +39 -12
  91. data/lib/nrser/rspex/example_group/overrides.rb +66 -0
  92. data/lib/nrser/rspex/example_group.rb +20 -252
  93. data/lib/nrser/rspex/format.rb +83 -17
  94. data/lib/nrser/rspex.rb +4 -34
  95. data/lib/nrser/sugar/method_missing_forwarder.rb +50 -0
  96. data/lib/nrser/{env → sys/env}/path.rb +1 -2
  97. data/lib/nrser/{env.rb → sys/env.rb} +2 -1
  98. data/lib/nrser/sys.rb +5 -0
  99. data/lib/nrser/types/any.rb +36 -7
  100. data/lib/nrser/types/{array.rb → arrays.rb} +32 -81
  101. data/lib/nrser/types/attrs.rb +68 -15
  102. data/lib/nrser/types/booleans.rb +95 -34
  103. data/lib/nrser/types/bounded.rb +12 -10
  104. data/lib/nrser/types/combinators.rb +74 -37
  105. data/lib/nrser/types/errors/check_error.rb +86 -0
  106. data/lib/nrser/types/errors/from_string_error.rb +82 -0
  107. data/lib/nrser/types/factory.rb +91 -0
  108. data/lib/nrser/types/hashes.rb +171 -26
  109. data/lib/nrser/types/in.rb +25 -12
  110. data/lib/nrser/types/is.rb +50 -18
  111. data/lib/nrser/types/is_a.rb +52 -33
  112. data/lib/nrser/types/labels.rb +6 -33
  113. data/lib/nrser/types/maybe.rb +12 -4
  114. data/lib/nrser/types/nil.rb +24 -4
  115. data/lib/nrser/types/not.rb +6 -16
  116. data/lib/nrser/types/numbers.rb +94 -57
  117. data/lib/nrser/types/pairs.rb +57 -57
  118. data/lib/nrser/types/paths.rb +112 -133
  119. data/lib/nrser/types/responds.rb +64 -74
  120. data/lib/nrser/types/shape.rb +29 -24
  121. data/lib/nrser/types/strings.rb +25 -17
  122. data/lib/nrser/types/symbols.rb +19 -17
  123. data/lib/nrser/types/trees.rb +18 -70
  124. data/lib/nrser/types/tuples.rb +36 -40
  125. data/lib/nrser/types/type.rb +342 -91
  126. data/lib/nrser/types/when.rb +40 -18
  127. data/lib/nrser/types/where.rb +94 -9
  128. data/lib/nrser/types.rb +72 -63
  129. data/lib/nrser/version.rb +1 -1
  130. data/lib/nrser.rb +18 -18
  131. data/spec/lib/nrser/{functions/binding/template_spec.rb → core_ext/binding/erb_spec.rb} +5 -5
  132. data/spec/lib/nrser/{functions → core_ext}/enumerable/find_map_spec.rb +8 -6
  133. data/spec/lib/nrser/{refinements → core_ext}/hash_spec.rb +9 -22
  134. data/spec/lib/nrser/errors/abstract_method_error_spec.rb +12 -5
  135. data/spec/lib/nrser/functions/enumerable/{to_h_by_spec.rb → associate_spec.rb} +1 -1
  136. data/spec/lib/nrser/functions/merge_by_spec.rb +1 -1
  137. data/spec/lib/nrser/functions/tree/each_branch_spec.rb +3 -3
  138. data/spec/lib/nrser/functions/tree/transform_spec.rb +14 -15
  139. data/spec/lib/nrser/gem_ext/hamster/json_spec.rb +4 -0
  140. data/spec/lib/nrser/meta/source/location_spec.rb +86 -0
  141. data/spec/lib/nrser/props/immutable/hash_spec.rb +297 -0
  142. data/spec/lib/nrser/props/immutable/vector_spec.rb +296 -0
  143. data/spec/lib/nrser/{meta/props_spec.rb → props/original_props_spec.rb} +11 -16
  144. data/spec/lib/nrser/{meta/props → props}/to_and_from_data_spec.rb +10 -8
  145. data/spec/lib/nrser/refinements/array_spec.rb +2 -15
  146. data/spec/lib/nrser/refinements/erb_spec.rb +5 -7
  147. data/spec/lib/nrser/refinements/set_spec.rb +2 -15
  148. data/spec/lib/nrser/{env → sys/env}/path/insert_spec.rb +4 -2
  149. data/spec/lib/nrser/{env → sys/env}/path_spec.rb +4 -2
  150. data/spec/lib/nrser/types/array_spec.rb +8 -8
  151. data/spec/lib/nrser/types/paths_spec.rb +15 -18
  152. data/spec/spec_helper.rb +4 -0
  153. metadata +109 -69
  154. data/lib/nrser/ext/binding.rb +0 -36
  155. data/lib/nrser/ext/module.rb +0 -62
  156. data/lib/nrser/ext.rb +0 -8
  157. data/lib/nrser/functions/binding.rb +0 -76
  158. data/lib/nrser/functions/enumerable/map_keys.rb +0 -0
  159. data/lib/nrser/functions/enumerable/map_values.rb +0 -94
  160. data/lib/nrser/functions/hash/deep_merge.rb +0 -57
  161. data/lib/nrser/functions/hash/except_keys.rb +0 -44
  162. data/lib/nrser/functions/hash/slice_keys.rb +0 -43
  163. data/lib/nrser/functions/hash/stringify_keys.rb +0 -55
  164. data/lib/nrser/functions/hash/symbolize_keys.rb +0 -57
  165. data/lib/nrser/functions/hash/transform_keys.rb +0 -140
  166. data/lib/nrser/functions/module/methods.rb +0 -206
  167. data/lib/nrser/functions/module/source_locations.rb +0 -213
  168. data/lib/nrser/logging/appender.rb +0 -3
  169. data/lib/nrser/logging.rb +0 -353
  170. data/lib/nrser/meta/props/base.rb +0 -31
  171. data/lib/nrser/meta/props.rb +0 -357
  172. data/lib/nrser/refinements/array.rb +0 -133
  173. data/lib/nrser/refinements/binding.rb +0 -6
  174. data/lib/nrser/refinements/enumerator.rb +0 -5
  175. data/lib/nrser/refinements/exception.rb +0 -35
  176. data/lib/nrser/refinements/hash.rb +0 -150
  177. data/lib/nrser/refinements/module.rb +0 -5
  178. data/lib/nrser/refinements/object.rb +0 -42
  179. data/lib/nrser/refinements/open_struct.rb +0 -28
  180. data/lib/nrser/refinements/pathname.rb +0 -5
  181. data/lib/nrser/refinements/set.rb +0 -5
  182. data/lib/nrser/refinements/string.rb +0 -5
  183. data/lib/nrser/refinements/symbol.rb +0 -20
  184. data/lib/nrser/rspex/described.rb +0 -99
  185. data/spec/design/mapping_spec.rb +0 -42
  186. data/spec/lib/nrser/functions/hash_spec.rb +0 -41
  187. data/spec/lib/nrser/functions/string/truncate_spec.rb +0 -11
  188. data/spec/lib/nrser/refinements/truncate_spec.rb +0 -10
@@ -0,0 +1,184 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # Requirements
5
+ # =======================================================================
6
+
7
+ # Stdlib
8
+ # -----------------------------------------------------------------------
9
+
10
+ # Deps
11
+ # -----------------------------------------------------------------------
12
+
13
+ # Project / Package
14
+ # -----------------------------------------------------------------------
15
+
16
+
17
+ # Refinements
18
+ # =======================================================================
19
+
20
+
21
+ # Declarations
22
+ # =======================================================================
23
+
24
+ module NRSER::Props; end
25
+
26
+
27
+ # Definitions
28
+ # =======================================================================
29
+
30
+ # Instance methods to mix in to classes that include {NRSER::Props}.
31
+ #
32
+ module NRSER::Props::InstanceMethods
33
+
34
+ # Initialize the properties from a hash.
35
+ #
36
+ # Called from `#initialize` in {NRSER::Props::Base}, but if you just
37
+ # mix in {NRSER::Props::Props} you need to call it yourself.
38
+ #
39
+ # @param [Hash<(String | Symbol) => Object>] values
40
+ # Property values. Keys will be normalized to symbols.
41
+ #
42
+ # @return [nil]
43
+ #
44
+ # def initialize_props source_values
45
+ # array = []
46
+ # hash = {}
47
+ #
48
+ # self.class.props(only_primary: true).values.each { |prop|
49
+ # value = prop.create_value self, source_values
50
+ # case prop.key
51
+ # when Integer
52
+ # array[key] = value
53
+ # when Symbol
54
+ # hash[key] = value
55
+ # else
56
+ # raise "SHOULD NEVER HAPPEN, prop.key: #{ prop.pretty_inspect }"
57
+ # end
58
+ # }
59
+ #
60
+ # # TODO Now trigger all eager defaults (check prop getting trigger
61
+ # # correctly)
62
+ #
63
+ # # Check additional type invariants
64
+ # self.class.invariants.each do |type|
65
+ # type.check self
66
+ # end
67
+ #
68
+ # nil
69
+ # end # #initialize_props
70
+
71
+
72
+ # @param [Proc<(KEY, CURRENT, UPDATE) => VALUE>] &block
73
+ # Optional block to handle conflicts.
74
+ #
75
+ def merge other, &block
76
+ self.class.new \
77
+ self.to_h( only_primary: true ).
78
+ merge( other.symbolize_keys, &block )
79
+ end
80
+
81
+
82
+ def dup
83
+ self.class.new \
84
+ self.to_h( only_primary: true )
85
+ end
86
+
87
+
88
+ # Create a new hash with property names mapped to values.
89
+ #
90
+ # @param [Boolean] only_own:
91
+ # When `true`, don't include parent properties.
92
+ #
93
+ # @param [Boolean] only_primary:
94
+ # When `true`, don't include sourced properties.
95
+ #
96
+ # @return [Hash<Symbol, Object>]
97
+ # Map of prop names to values.
98
+ #
99
+ def to_h only_own: false, only_primary: false, compact: true
100
+ hash = self.class.
101
+ props(only_own: only_own, only_primary: only_primary).
102
+ transform_values { |prop| prop.get self }
103
+
104
+ hash.compact! if compact
105
+
106
+ hash
107
+ end # #to_h
108
+
109
+
110
+ # Create a "data" representation suitable for transport, storage, etc.
111
+ #
112
+ # The result is meant to consist of only basic data types and structures -
113
+ # strings, numbers, arrays, hashes, datetimes, etc... though it depends on
114
+ # any custom objects it encounters correctly responding to `#to_data` for
115
+ # this to happen (as is implemented from classes that mix in Props here).
116
+ #
117
+ # Prop names are converted to strings (from symbols) since though YAML
118
+ # supports symbol values, they have poor portability across languages,
119
+ # and they mean the same thing in this situation.
120
+ #
121
+ # @param [Boolean] only_own:
122
+ # When `true`, don't include parent properties.
123
+ #
124
+ # @param [Boolean] only_primary:
125
+ # When `true`, don't include sourced properties.
126
+ #
127
+ # @param [Boolean] add_class:
128
+ # Add a special key with the class' name as the value.
129
+ #
130
+ # @param [String] class_key:
131
+ # Name for special class key.
132
+ #
133
+ # @return [Hash<String, *>]
134
+ # Map of property names as strings to their "data" value, plus the special
135
+ # class identifier key and value, if requested.
136
+ #
137
+ def to_data only_own: false,
138
+ only_primary: false,
139
+ add_class: true,
140
+ class_key: '__class__',
141
+ compact: true
142
+ # class_key: NRSER::Props::DEFAULT_CLASS_KEY
143
+
144
+ hash = self.class.props(only_own: only_own, only_primary: only_primary).
145
+ map { |name, prop|
146
+ [name.to_s, prop.to_data(self)]
147
+ }.
148
+ to_h
149
+
150
+ hash.compact! if compact
151
+ hash[class_key] = self.class.safe_name if add_class
152
+
153
+ hash
154
+ end # #to_data
155
+
156
+
157
+ # Language Inter-Op
158
+ # ---------------------------------------------------------------------
159
+
160
+ # Get a JSON {String} encoding the instance's data.
161
+ #
162
+ # @param [Array] *args
163
+ # I really don't know. `#to_json` takes at last one argument, but I've
164
+ # had trouble finding a spec for it :/
165
+ #
166
+ # @return [String]
167
+ #
168
+ def to_json *args
169
+ to_data.to_json *args
170
+ end # #to_json
171
+
172
+
173
+ # Get a YAML {String} encoding the instance's data.
174
+ #
175
+ # @param [Array] *args
176
+ # I really don't know... whatever {YAML.dump} sends to it i guess.
177
+ #
178
+ # @return [String]
179
+ #
180
+ def to_yaml *args
181
+ to_data.to_yaml *args
182
+ end
183
+
184
+ end # module NRSER::Props::ClassMethods
@@ -0,0 +1,359 @@
1
+ # encoding: UTF-8
2
+ # frozen_string_literal: true
3
+
4
+ # Requirements
5
+ # =======================================================================
6
+
7
+ # Project / Package
8
+ # -----------------------------------------------------------------------
9
+
10
+ require 'nrser/refinements/types'
11
+
12
+ require 'nrser/graph/tsorter'
13
+
14
+ require_relative './prop'
15
+
16
+
17
+ # Refinements
18
+ # =======================================================================
19
+
20
+ using NRSER::Types
21
+
22
+
23
+ # Declarations
24
+ # =======================================================================
25
+
26
+ module NRSER::Props; end
27
+
28
+
29
+ # Definitions
30
+ # =======================================================================
31
+
32
+ # @todo document NRSER::Props::ClassMetadata class.
33
+ class NRSER::Props::Metadata
34
+
35
+ # Constants
36
+ # ======================================================================
37
+
38
+ VARIABLE_NAME = :@__NRSER_metadata__
39
+
40
+
41
+ # Class Methods
42
+ # ======================================================================
43
+
44
+ # @todo Document has_metadata? method.
45
+ #
46
+ # @param [type] arg_name
47
+ # @todo Add name param description.
48
+ #
49
+ # @return [return_type]
50
+ # @todo Document return value.
51
+ #
52
+ def self.has_metadata? klass
53
+ klass.instance_variable_defined? VARIABLE_NAME
54
+ end # .has_metadata?
55
+
56
+
57
+ def self.metadata_for klass
58
+ klass.instance_variable_get VARIABLE_NAME
59
+ end
60
+
61
+
62
+ # Attributes
63
+ # ======================================================================
64
+
65
+
66
+ # Constructor
67
+ # ======================================================================
68
+
69
+ # Instantiate a new `NRSER::Props::ClassMetadata`.
70
+ def initialize klass
71
+ @klass = klass
72
+ @props = {}
73
+ @invariants = Set.new
74
+ @storage = nil
75
+ end # #initialize
76
+
77
+
78
+ # Instance Methods
79
+ # ======================================================================
80
+
81
+ def superclass_has_metadata?
82
+ @klass.superclass.respond_to? :metadata
83
+ end
84
+
85
+
86
+ def superclass_metadata
87
+ @klass.superclass.metadata
88
+ end
89
+
90
+
91
+ def get_prop name
92
+ name = name.to_s.to_sym unless name.is_a?( Symbol )
93
+
94
+ return @props[name] if @props.key?( name )
95
+
96
+ if superclass_has_metadata?
97
+ superclass_metadata.get_prop name
98
+ else
99
+ nil
100
+ end
101
+ end
102
+
103
+ alias_method :[], :get_prop
104
+
105
+
106
+ # @todo
107
+ # Cache / optimize
108
+ #
109
+ def prop_names only_own: false, only_primary: false
110
+ Set.new props( only_own: only_own, only_primary: only_primary ).keys
111
+ end
112
+
113
+
114
+ # Get a map of property names to property instances.
115
+ #
116
+ # @param [Boolean] only_own:
117
+ # Don't include super-class properties.
118
+ #
119
+ # @param [Boolean] only_primary:
120
+ # Don't include properties that have a {NRSER::Props::Prop#source}.
121
+ #
122
+ # @return [Hash{ Symbol => NRSER::Props::Prop }]
123
+ # Hash mapping property name to property instance.
124
+ #
125
+ def props only_own: false, only_primary: false
126
+ result = if !only_own && superclass_has_metadata?
127
+ superclass_metadata.props only_own: only_own,
128
+ only_primary: only_primary
129
+ else
130
+ {}
131
+ end
132
+
133
+ if only_primary
134
+ @props.each {|name, prop| result[name] = prop if prop.primary? }
135
+ else
136
+ result.merge! @props
137
+ end
138
+
139
+ result
140
+ end # #props
141
+
142
+
143
+ # Define a property.
144
+ #
145
+ # @param [Symbol] name
146
+ # The name of the property.
147
+ #
148
+ # @param [Hash{ Symbol => Object }] **opts
149
+ # Constructor options for {NRSER::Props::Prop}.
150
+ #
151
+ # @return [NRSER::Props::Prop]
152
+ # The newly created prop, thought you probably don't need it (it's
153
+ # already all bound up on the class at this point), but why not?
154
+ #
155
+ def prop name, **opts
156
+ t.non_empty_sym.check! name
157
+
158
+ if @props.key? name
159
+ raise ArgumentError.new binding.erb <<~END
160
+ Prop <%= name.inspect %> already set for <%= @klass %>:
161
+
162
+ <%= @props[name].inspect %>
163
+ END
164
+ end
165
+
166
+ prop = NRSER::Props::Prop.new @klass, name, **opts
167
+ @props[name] = prop
168
+
169
+ prop.names.each do |name|
170
+ if prop.create_reader? name
171
+ @klass.class_eval do
172
+ define_method name do
173
+ prop.get self
174
+ end
175
+ end
176
+ end
177
+
178
+ if prop.create_writer? name
179
+ @klass.class_eval do
180
+ define_method "#{ name }=" do |value|
181
+ prop.set self, value
182
+ end
183
+ end
184
+ end
185
+ end
186
+
187
+ prop
188
+ end # #prop
189
+
190
+
191
+ # Check primary prop values and fill in defaults, yielding `(Prop, VALUE)`
192
+ # to the `&block`.
193
+ #
194
+ # Used when initializing instances.
195
+ #
196
+ # @param [#each_pair | #each_index] values
197
+ # Collection of prop values iterable by key/value pairs or by indexed
198
+ # entries.
199
+ #
200
+ # @param [Proc<(NRSER::Props::Prop, VALUE)>] &block
201
+ # Block that will receive primary prop and value pairs.
202
+ #
203
+ # @raise [TypeError]
204
+ # If a value is does not satisfy it's {NRSER::Props::Prop#type}.
205
+ #
206
+ # @raise [ArgumentError]
207
+ # If `values` doesn't respond to `#each_pair` or `#each_index`.
208
+ #
209
+ # @raise [NameError]
210
+ # If a value is not provided for a primary prop and a default can not
211
+ # be created.
212
+ #
213
+ # @raise [TSort::Cyclic]
214
+ # If any of the primary prop's {NRSER::Props::Prop#deps} for dependency
215
+ # cycles.
216
+ #
217
+ def each_primary_prop_value_from values, &block
218
+ primary_props = props only_primary: true
219
+
220
+ # Normalize values to a `Hash<Symbol, VALUE>` so everything can deal with
221
+ # one form. Default values will be set here as they're resolved and made
222
+ # available to subsequent {Prop#default} calls.
223
+ values_by_name = {}
224
+
225
+ if values.respond_to? :each_pair
226
+ values.each_pair { |key, value|
227
+ # Figure out the prop name {Symbol}
228
+ name = case key
229
+ when Symbol
230
+ key
231
+ when String
232
+ key.to_sym
233
+ else
234
+ key.to_s.to_sym
235
+ end
236
+
237
+ # If the `name` corresponds to a primary prop set it in the values by
238
+ # name
239
+ #
240
+ # TODO Should check that the name is not already set?
241
+ #
242
+ values_by_name[name] = value if primary_props.key? name
243
+ }
244
+ elsif values.respond_to? :each_index
245
+ indexed = []
246
+
247
+ primary_props.each_value do |prop|
248
+ indexed[prop.index] = prop unless prop.index.nil?
249
+ end
250
+
251
+ values.each_index { |index|
252
+ prop = indexed[index]
253
+ values_by_name[prop.name] = values[index] if prop
254
+ }
255
+ else
256
+ raise ArgumentError.new binding.erb <<~END
257
+ `source` argument must respond to `#each_pair` or `#each_index`
258
+
259
+ Found:
260
+
261
+ <%= source.pretty_inspect %>
262
+
263
+ END
264
+ end
265
+
266
+ # Topological sort the primary props by their default dependencies.
267
+ #
268
+ NRSER::Graph::TSorter.new(
269
+ primary_props.each_value
270
+ ) { |prop, &on_dep_prop|
271
+ #
272
+ # This block is responsible for receiving a {Prop} and a callback
273
+ # block and invoking that callback block on each of the prop's
274
+ # dependencies (if any).
275
+ #
276
+ prop.deps.each { |name|
277
+ if primary_props.key? name
278
+ on_dep_prop.call primary_props[name]
279
+ else
280
+ raise RuntimeError.new binding.erb <<~END
281
+ Property <%= prop.full_name %> depends on prop `<%= name %>`,
282
+ but no primary prop with that name could be found!
283
+ END
284
+ end
285
+ }
286
+ }.tsort_each do |prop|
287
+ # {Prop} instances will now be yielded in an order that allows any
288
+ # inter-dependencies to be resolved (as long as there weren't dependency
289
+ # cycles, which {NRSER::Graph::TSorter} will raise if it finds)
290
+
291
+ # If we have a value for the prop, just check that
292
+ if values_by_name.key? prop.name
293
+ prop.check! values_by_name[prop.name]
294
+ else
295
+ # Otherwise, get the default value, providing the values we already
296
+ # know in case the default is a {Proc} that needs some of them.
297
+ #
298
+ # We set that value in `values_by_name` so that subsequent
299
+ # {Prop#default} calls can use it.
300
+ #
301
+ values_by_name[prop.name] = prop.default **values_by_name
302
+ end
303
+
304
+ # Yield the {Prop} and it's value back to the `&block`
305
+ block.call prop, values_by_name[prop.name]
306
+ end # .tsort_each
307
+ end # #each_primary_prop_value_from
308
+
309
+
310
+ def invariants only_own: false
311
+ result = if !only_own && superclass_has_metadata?
312
+ superclass_metadata.invariants only_own: false
313
+ else
314
+ Set.new
315
+ end
316
+
317
+ result + @invariants
318
+ end
319
+
320
+
321
+ def invariant type
322
+ @invariants.add type
323
+ end
324
+
325
+
326
+ def default_storage
327
+ if superclass_has_metadata?
328
+ superclass_metadata.storage
329
+ else
330
+ @storage = NRSER::Props::Storage::InstanceVariable.new
331
+ end
332
+ end
333
+
334
+
335
+ def storage value = nil
336
+ if value.nil?
337
+ if @storage.nil?
338
+ default_storage
339
+ else
340
+ @storage
341
+ end
342
+ else
343
+ @storage = value
344
+ end
345
+ end
346
+
347
+
348
+ def freeze
349
+ super()
350
+
351
+ if superclass_has_metadata? &&
352
+ !superclass_metadata.frozen?
353
+ superclass_metadata.freeze
354
+ end
355
+
356
+ end
357
+
358
+
359
+ end # class NRSER::Props::ClassMetadata
@@ -0,0 +1,60 @@
1
+ # Requirements
2
+ # =======================================================================
3
+
4
+ # Stdlib
5
+ # -----------------------------------------------------------------------
6
+
7
+ # Deps
8
+ # -----------------------------------------------------------------------
9
+
10
+ # Project / Package
11
+ # -----------------------------------------------------------------------
12
+ require_relative '../../props'
13
+ require_relative '../storage/key'
14
+ require_relative '../storage/instance_variables'
15
+
16
+
17
+ # Declarations
18
+ # ========================================================================
19
+
20
+ module NRSER::Props::Mutable; end
21
+
22
+
23
+ # Definitions
24
+ # =======================================================================
25
+
26
+ # Mix-in to store property values in instance variables of the same name.
27
+ #
28
+ module NRSER::Props::Mutable::InstanceVariables
29
+
30
+ STORAGE = NRSER::Props::Storage::InstanceVariables.new immutable: false
31
+
32
+
33
+ # Class Methods
34
+ # ======================================================================
35
+
36
+ def self.included base
37
+ base.include NRSER::Props
38
+ base.metadata.storage STORAGE
39
+ base.metadata.freeze
40
+ end
41
+
42
+
43
+ # Instance Methods
44
+ # ======================================================================
45
+
46
+ # Since the {NRSER::Props::Immutable::InstanceVariables} mix-in does *not*
47
+ # need to tap into the initialize chain,
48
+ #
49
+ def initialize_props values = {}
50
+ self.class.metadata.each_primary_prop_value_from( values ) { |prop, value|
51
+ instance_variable_set "@#{ prop.name }", value
52
+ }
53
+
54
+ # Check additional type invariants
55
+ self.class.invariants.each do |type|
56
+ type.check self
57
+ end
58
+ end # #initialize_props
59
+
60
+ end # module NRSER::Props::Immutable