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,86 @@
1
+ require 'nrser/meta/source/location'
2
+
3
+ describe_spec_file(
4
+ spec_path: __FILE__,
5
+ class: NRSER::Meta::Source::Location,
6
+ ) do
7
+
8
+ shared_examples described_class do |valid:, file:, line:|
9
+ it { is_expected.to be_a( described_class ) }
10
+
11
+ describe_attribute :valid? do
12
+ it { is_expected.to be valid }
13
+ end # Attribute valid? Description
14
+
15
+ describe_attribute :file do
16
+ it { is_expected.to eq file }
17
+ end # Attribute file Description
18
+
19
+ describe_attribute :line do
20
+ it { is_expected.to eq line }
21
+ end # Attribute line Description
22
+
23
+ describe_attribute :to_s do
24
+ it { is_expected.to eq "#{ file || '???' }:#{ line || '???' }" }
25
+ end # Attribute to_s Description
26
+
27
+ describe_method :[] do
28
+ describe_called_with 0 do
29
+ it { is_expected.to eq file }
30
+ end # called with 0
31
+
32
+ describe_called_with 1 do
33
+ it { is_expected.to eq line }
34
+ end # called with 1
35
+ end # Method [] Description
36
+
37
+ describe_attribute :to_a do
38
+ it { is_expected.to eq [file, line] }
39
+ end # Attribute to_a Description
40
+
41
+ describe_attribute :to_h do
42
+ it { is_expected.to be_a Hash }
43
+ it { is_expected.to eq( {file: file, line: line}.compact ) }
44
+ end # Attribute to_h Description
45
+
46
+ end # described_class from:
47
+
48
+
49
+ context "{Method#source_location} with non-nil file and line" do
50
+ loc = NRSER.own_class_Methods.
51
+ map( &:source_location ).
52
+ find { |src_loc|
53
+ !src_loc.nil? &&
54
+ !src_loc[0].nil? &&
55
+ !src_loc[1].nil?
56
+ }
57
+
58
+ context "instantiate from source location array" do
59
+ describe_instance loc do
60
+ it_behaves_like described_class,
61
+ valid: true,
62
+ file: loc[0],
63
+ line: loc[1]
64
+ end # instance loc
65
+ end
66
+
67
+
68
+ context "instantiate from prop value hash" do
69
+ describe_instance file: loc[0], line: loc[1] do
70
+ it_behaves_like described_class,
71
+ valid: true,
72
+ file: loc[0],
73
+ line: loc[1]
74
+ end
75
+ end
76
+ end # {Method#source_location} with non-nil file and line
77
+
78
+
79
+ context "`nil` source location" do
80
+ describe_instance nil do
81
+ it_behaves_like described_class, valid: false, file: nil, line: nil
82
+ end
83
+ end # `nil` source location
84
+
85
+
86
+ end # spec file
@@ -0,0 +1,297 @@
1
+ # Propertied Immutable Hashes
2
+ # ============================================================================
3
+ #
4
+ # The {NRSER::Props::Immutable::Hash} module can be mixed into subclasses of
5
+ # {Hamster::Hash} (an immutable {Hash}) to provide property encoding,
6
+ # decoding and reading backed by the elements of the hash itself.
7
+ #
8
+ # This is extremely similar to {NRSER::Props::Immutable::Vector} - which I
9
+ # wrote and tested first - so check their for more comments / details.
10
+ #
11
+ # Requirements
12
+ # ----------------------------------------------------------------------------
13
+ #
14
+ # We're going to use the {NRSER::Types} refinements to specify types for our
15
+ # props.
16
+
17
+ require 'nrser/refinements/types'
18
+
19
+ # And we're going to need the mixin module itself.
20
+
21
+ require 'nrser/props/immutable/hash'
22
+
23
+ #
24
+ # Refinements
25
+ # ----------------------------------------------------------------------------
26
+ #
27
+ # Declare that we're going to use the {NRSER::Types} refinements, which just
28
+ # provide the global `t` shortcut to {NRSER::Types}.
29
+
30
+ using NRSER::Types
31
+
32
+ #
33
+ # Examples
34
+ # ----------------------------------------------------------------------------
35
+ #
36
+
37
+ describe_spec_file(
38
+ spec_path: __FILE__,
39
+ module: NRSER::Props::Immutable::Hash,
40
+ ) do
41
+
42
+ describe_setup "Simple 2D Integer Point" do
43
+ # ==========================================================================
44
+
45
+ # Complete Fixture Class
46
+ # --------------------------------------------------------------------------
47
+ #
48
+ # First, let's look at a working example:
49
+ #
50
+
51
+ subject :point_2d_int do
52
+ Class.new( Hamster::Hash ) do
53
+ include NRSER::Props::Immutable::Hash
54
+
55
+ # So that error messages look right. You don't need this in "regularly"
56
+ # defined classes.
57
+ def self.name; 'Point2DInt'; end
58
+ def self.inspect; name; end
59
+
60
+ # It's vital that we include the `key:` keyword argument, and that the
61
+ # values are non-negative integer indexes for the vector.
62
+ prop :x, type: t.int
63
+ prop :y, type: t.int
64
+
65
+ # Just for fun and to show the capabilities, define a `#to_s` to nicely
66
+ # print the point.
67
+ def to_s
68
+ "(#{ x }, #{ y })"
69
+ end
70
+ end
71
+ end
72
+
73
+ # and let's define our expectations for a successfully created point:
74
+
75
+ shared_examples "Point2DInt" do |x:, y:|
76
+
77
+ # It should be an instance of the class
78
+
79
+ it { is_expected.to be_a point_2d_int }
80
+
81
+ # as well as {Hamster::Vector}!
82
+
83
+ it { is_expected.to be_a Hamster::Hash }
84
+
85
+
86
+ # and it should have the `x` and `y` accessible as hash values via
87
+ # it's `#[]` method:
88
+
89
+ describe_method :[] do
90
+ describe_called_with :x do
91
+ it { is_expected.to be x }
92
+ end
93
+
94
+ describe_called_with :y do
95
+ it { is_expected.to be y }
96
+ end
97
+ end # Method [] Description
98
+
99
+ # as well as via the `#x` and `#y` attribute reader methods that
100
+ # {NRSER::Props} creates:
101
+
102
+ describe_attribute :x do
103
+ it { is_expected.to be x }
104
+ end
105
+
106
+ describe_attribute :y do
107
+ it { is_expected.to be y }
108
+ end
109
+
110
+ # Converting to an array should be equal to `[[:x, x], [:y, y]]`:
111
+
112
+ describe_attribute :to_a do
113
+ it { is_expected.to include [:x, x], [:y, y] }
114
+ end
115
+
116
+ # We should get a regular hash out of `#to_h`
117
+
118
+ describe_attribute :to_h do
119
+ it { is_expected.to eq x: x, y: y }
120
+ end
121
+
122
+ # We should see our custom `#to_s` value
123
+
124
+ describe_attribute :to_s do
125
+ it { is_expected.to eq "(#{ x }, #{ y })" }
126
+ end
127
+
128
+ end
129
+
130
+
131
+ describe_setup "Creating a Point from `source`" do
132
+ # ========================================================================
133
+
134
+ # Our subject will be a Point2dInt created from a `source` value that we
135
+ # will define for each example.
136
+
137
+ subject do
138
+ point_2d_int.new source
139
+ end
140
+
141
+
142
+ describe_case "From an `{x: Integer, y: Integer}` literal `Hash`" do
143
+ # ------------------------------------------------------------------------
144
+
145
+ # Let the `source` be the {Hash} `{x: 1, y: 2}`.
146
+
147
+ describe_when source: {x: 1, y: 2} do
148
+
149
+ # Now we should be able to construct a point. Test that it behaves
150
+ # like we defined above:
151
+
152
+ it_behaves_like "Point2DInt", source
153
+
154
+ end
155
+ end
156
+
157
+
158
+ describe_case "From a `Hamster::Hash[x: Integer, y: Integer]`" do
159
+ # --------------------------------------------------------------------
160
+ #
161
+ # All that `#initialize` cares about is that it can access the `source`
162
+ # via `#[]` and that it can tell if it should use names or integer
163
+ # index by looking for `#each_pair` and `#each_index`, respectively.
164
+ #
165
+
166
+ describe_when source: Hamster::Hash[x: 1, y: 2] do
167
+
168
+ # Now we should be able to construct a point. Test that it behaves
169
+ # like we defined above:
170
+
171
+ it_behaves_like "Point2DInt", source.to_h
172
+
173
+ end
174
+ end
175
+ end # .new - Creating a Point
176
+ # ************************************************************************
177
+
178
+
179
+ describe_section "Deriving a new point from another" do
180
+ # ========================================================================
181
+ #
182
+ # Our point instances are immutable, but we can use {Hamster::Vector}'s
183
+ # methods for deriving new instances to derive new points.
184
+ #
185
+
186
+ # Let's create a point subject to start off with
187
+
188
+ subject do
189
+ point_2d_int.new x: 1, y: 2
190
+ end
191
+
192
+ # and we can play around with a few {Hamster::Vector} methods...
193
+
194
+ describe_method :put do
195
+
196
+ # Change the value at entry 0 to 2
197
+
198
+ describe_called_with :x, 2 do
199
+ it_behaves_like "Point2DInt", x: 2, y: 2
200
+ end # called with 0, 2
201
+
202
+ # Type checking should still be in effect, of course
203
+
204
+ describe_called_with :x, 'hey' do
205
+ it { expect { subject }.to raise_error TypeError }
206
+ end # called with 0, 'hey'
207
+
208
+ end # Method put Description
209
+
210
+
211
+ describe_method :map do
212
+
213
+ # We have to do some manual funkiness here 'cause
214
+ # `describe_called_with` doesn't take a block...
215
+ #
216
+ ### TODO
217
+ #
218
+ # We should be able to handle this by passing a {NRSER::Message}
219
+ # to `describe_called_with`, but that's not yet implemented
220
+ # (RSpex need s a *lot* of work).
221
+ #
222
+ # describe_called_with(
223
+ # NRSER::Message.new { |value| value + 1 }
224
+ # ) do ...
225
+ #
226
+ # or something like that.
227
+ #
228
+
229
+ describe "add 1 to each entry" do
230
+ subject do
231
+ super().call { |key, value| [key, value + 1] }
232
+ end
233
+
234
+ it_behaves_like "Point2DInt", x: 2, y: 3
235
+ end
236
+
237
+ # Type checking should still be enforced
238
+
239
+ describe "try to turn entries into Strings" do
240
+ subject do
241
+ super().call { |key, value| [key, "value: #{ value }"] }
242
+ end
243
+
244
+ it do
245
+ expect { subject }.to raise_error TypeError
246
+ end
247
+ end # "try to turn entries into Strings"
248
+
249
+ end # Method map Description
250
+
251
+
252
+ describe_section "Adding extra keys and values" do
253
+ # ======================================================================
254
+ #
255
+ # At the moment, we *can* add *extra* keys and values to the point.
256
+ #
257
+
258
+ # Let's try adding a `:z` key with value `zee`
259
+
260
+ describe_method :put do
261
+ describe_called_with :z, 'zee' do
262
+
263
+ # It works! And the new point still behaves as expected.
264
+
265
+ it_behaves_like "Point2DInt", x: 1, y: 2
266
+
267
+ # but it *also* has a `:z` key with value `'zee'`, which is not
268
+ # type checked in any way because it has no corresponding prop
269
+
270
+ it "also has `z: 'zee`" do
271
+ expect( subject[:z] ).to eq 'zee'
272
+ end
273
+
274
+ ### TODO
275
+ #
276
+ # In the future (soon?!), we will have options to configure how
277
+ # to handle extra values... thinking:
278
+ #
279
+ # 1. Allow (what happens now)
280
+ # 2. Prohibit (raise an error)
281
+ # 3. Discard (just toss them)
282
+ #
283
+ # Of course, we will still discard derived prop values found in
284
+ # the source so that we can always dump and re-load data values.
285
+ #
286
+
287
+ end # called with :z, 3
288
+ end # Method put Description
289
+
290
+ end # section Adding extra keys and values
291
+ # **********************************************************************
292
+
293
+ end # section Deriving a new point from another
294
+ # ************************************************************************
295
+
296
+ end # Simple 2D Integer Point
297
+ end
@@ -0,0 +1,296 @@
1
+ # Propertied Immutable Vectors
2
+ # ============================================================================
3
+ #
4
+ # The {NRSER::Props::Immutable::Vector} module can be mixed into subclasses of
5
+ # {Hamster::Vector} (an immutable {Array}) to provide property encoding,
6
+ # decoding and reading backed by the elements of the vector itself.
7
+ #
8
+ #
9
+ # Requirements
10
+ # ----------------------------------------------------------------------------
11
+ #
12
+ # We're going to use the {NRSER::Types} refinements to specify types for our
13
+ # props.
14
+
15
+ require 'nrser/refinements/types'
16
+
17
+ # And we're going to need the mixin module itself.
18
+
19
+ require 'nrser/props/immutable/vector'
20
+
21
+ #
22
+ # Refinements
23
+ # ----------------------------------------------------------------------------
24
+ #
25
+ # Declare that we're going to use the {NRSER::Types} refinements, which just
26
+ # provide the global `t` shortcut to {NRSER::Types}.
27
+
28
+ using NRSER::Types
29
+
30
+ #
31
+ # Examples
32
+ # ----------------------------------------------------------------------------
33
+ #
34
+
35
+ describe NRSER::Props::Immutable::Vector do
36
+
37
+ # TODO Fix this shit!
38
+ #
39
+ # describe_spec_file(
40
+ # spec_path: __FILE__,
41
+ # ) do
42
+
43
+ describe "Simple 2D Integer Point" do
44
+ # ==========================================================================
45
+
46
+ # Complete Fixture Class
47
+ # --------------------------------------------------------------------------
48
+ #
49
+ # First, let's look at a working example:
50
+ #
51
+
52
+ let :point_2d_int do
53
+ Class.new( Hamster::Vector ) do
54
+ include NRSER::Props::Immutable::Vector
55
+
56
+ # So that error messages look right. You don't need this in "regularly"
57
+ # defined classes.
58
+ def self.name; 'Point2DInt'; end
59
+
60
+ # It's vital that we include the `key:` keyword argument, and that the
61
+ # values are non-negative integer indexes for the vector.
62
+ prop :x, type: t.int, index: 0
63
+ prop :y, type: t.int, index: 1
64
+
65
+ # Just for fun and to show the capabilities, define a `#to_s` to nicely
66
+ # print the point.
67
+ def to_s
68
+ "(#{ x }, #{ y })"
69
+ end
70
+ end
71
+ end
72
+
73
+ # and let's define our expectations for a successfully created point:
74
+
75
+ shared_examples "Point2DInt" do |x:, y:|
76
+
77
+ # It should be an instance of the class
78
+
79
+ it { is_expected.to be_a point_2d_int }
80
+
81
+ # as well as {Hamster::Vector}!
82
+
83
+ it { is_expected.to be_a Hamster::Vector }
84
+
85
+
86
+ # and it should have the `x` and `y` accessible as vector entries via
87
+ # it's `#[]` method:
88
+
89
+ describe_method :[] do
90
+ describe_called_with 0 do
91
+ it { is_expected.to be x }
92
+ end
93
+
94
+ describe_called_with 1 do
95
+ it { is_expected.to be y }
96
+ end
97
+ end # Method [] Description
98
+
99
+ # as well as via the `#x` and `#y` attribute reader methods that
100
+ # {NRSER::Props} creates:
101
+
102
+ describe_attribute :x do
103
+ it { is_expected.to be x }
104
+ end
105
+
106
+ describe_attribute :y do
107
+ it { is_expected.to be y }
108
+ end
109
+
110
+ # Converting to an array should be equal to `[x, y]`:
111
+
112
+ describe_attribute :to_a do
113
+ it { is_expected.to eq [x, y] }
114
+ end
115
+
116
+ # However, {NRSER::Props} *overrides* `#to_h` to provide a {Hash} of
117
+ # the props by name, so that will behave differently
118
+
119
+ describe_attribute :to_h do
120
+ it { is_expected.to eq x: x, y: y }
121
+ end
122
+
123
+ end
124
+
125
+
126
+ describe_section "Creating a Point" do
127
+ # ========================================================================
128
+
129
+ # Our subject will be a Point2dInt created from a `source` value that we
130
+ # will define for each example.
131
+
132
+ subject do
133
+ point_2d_int.new source
134
+ end
135
+
136
+ # We're going to define these sources at the example group level so that
137
+ # we can use them easily in `it_behaves_like`, but `subject` needs them
138
+ # at the example level, so we provide this little helper:
139
+
140
+ def source
141
+ self.class.source
142
+ end
143
+
144
+
145
+ describe "From an [x, y] pair Array" do
146
+ # ------------------------------------------------------------------------
147
+
148
+ # Let the `source` be the {Array} `[1, 2]`.
149
+
150
+ def self.source
151
+ [1, 2]
152
+ end
153
+
154
+ # Now we should be able to construct a point. Test that it behaves like
155
+ # we defined above:
156
+
157
+ it_behaves_like "Point2DInt", x: source[0], y: source[1]
158
+
159
+ end
160
+
161
+
162
+ describe "From an {x:, y:} Hash" do
163
+ # ------------------------------------------------------------------------
164
+
165
+ # Let the `source` be the {Hash} `{x: 1, y: 2}`.
166
+
167
+ def self.source
168
+ {x: 1, y: 2}
169
+ end
170
+
171
+ # Now we should be able to construct a point. Test that it behaves like
172
+ # we defined above:
173
+
174
+ it_behaves_like "Point2DInt", source
175
+
176
+ end
177
+
178
+ describe "From a Hamster::Vector[x, y] pair" do
179
+ # ------------------------------------------------------------------------
180
+ #
181
+ # All that `#initialize` cares about is that it can access the `source`
182
+ # via `#[]` and that it can tell if it should use names or integer
183
+ # index by looking for `#each_pair` and `#each_index`, respectively.
184
+ #
185
+
186
+ # Let the `source` be the `Hamster::Vector[1, 2]`.
187
+
188
+ def self.source
189
+ Hamster::Vector[1, 2]
190
+ end
191
+
192
+ # Now we should be able to construct a point. Test that it behaves like
193
+ # we defined above:
194
+
195
+ it_behaves_like "Point2DInt", x: source[0], y: source[1]
196
+
197
+ end
198
+
199
+ describe "From a Hamster::Hash[x:, y:]" do
200
+ # ------------------------------------------------------------------------
201
+ #
202
+ # All that `#initialize` cares about is that it can access the `source`
203
+ # via `#[]` and that it can tell if it should use names or integer
204
+ # index by looking for `#each_pair` and `#each_index`, respectively.
205
+ #
206
+
207
+ # Let the `source` be the `Hamster::Hash[x: 1, y: 2]`.
208
+
209
+ def self.source
210
+ Hamster::Hash[x: 1, y: 2]
211
+ end
212
+
213
+ # Now we should be able to construct a point. Test that it behaves like
214
+ # we defined above:
215
+
216
+ it_behaves_like "Point2DInt", source.to_h
217
+
218
+ end
219
+ end # section Creating a Point
220
+ # ************************************************************************
221
+
222
+
223
+ describe_section "Deriving a new point from another" do
224
+ # ========================================================================
225
+ #
226
+ # Our point instances are immutable, but we can use {Hamster::Vector}'s
227
+ # methods for deriving new instances to derive new points.
228
+ #
229
+
230
+ # Let's create a point subject to start off with
231
+
232
+ subject do
233
+ point_2d_int.new x: 1, y: 2
234
+ end
235
+
236
+ # and we can play around with a few {Hamster::Vector} methods...
237
+
238
+ describe_method :put do
239
+
240
+ # Change the value at entry 0 to 2
241
+
242
+ describe_called_with 0, 2 do
243
+ it_behaves_like "Point2DInt", x: 2, y: 2
244
+ end # called with 0, 2
245
+
246
+ # Type checking should still be in effect, of course
247
+
248
+ describe_called_with 0, 'hey' do
249
+ it { expect { subject }.to raise_error TypeError }
250
+ end # called with 0, 'hey'
251
+
252
+ end # Method put Description
253
+
254
+
255
+ describe_method :map do
256
+
257
+ # We have to do some manual funkiness here 'cause
258
+ # `describe_called_with` doesn't take a block...
259
+ #
260
+ # TODO We should be able to handle this by passing a {NRSER::Message}
261
+ # to `describe_called_with`, but that's not yet implemented
262
+ # (RSpex need s a *lot* of work).
263
+ #
264
+ # describe_called_with(
265
+ # NRSER::Message.new { |value| value + 1 }
266
+ # ) do ...
267
+ #
268
+ # or something like that.
269
+
270
+ describe "add 1 to each entry" do
271
+ subject do
272
+ super().call { |value| value + 1 }
273
+ end
274
+
275
+ it_behaves_like "Point2DInt", x: 2, y: 3
276
+ end
277
+
278
+ # Type checking should still be enforced
279
+
280
+ describe "try to turn entries into Strings" do
281
+ subject do
282
+ super().call { |value| "value: #{ value }" }
283
+ end
284
+
285
+ it do
286
+ expect { subject }.to raise_error TypeError
287
+ end
288
+ end # "try to turn entries into Strings"
289
+
290
+ end # Method map Description
291
+
292
+ end # section Deriving a new point from another
293
+ # ************************************************************************
294
+
295
+ end # Simple 2D Integer Point
296
+ end