nrser 0.2.2 → 0.3.0

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