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