nrser 0.0.25 → 0.0.26

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 (77) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +39 -11
  3. data/lib/nrser.rb +5 -1
  4. data/lib/nrser/array.rb +10 -53
  5. data/lib/nrser/enumerable.rb +21 -0
  6. data/lib/nrser/hash.rb +13 -476
  7. data/lib/nrser/hash/bury.rb +154 -0
  8. data/lib/nrser/hash/deep_merge.rb +57 -0
  9. data/lib/nrser/hash/except_keys.rb +42 -0
  10. data/lib/nrser/hash/guess_label_key_type.rb +37 -0
  11. data/lib/nrser/hash/slice_keys.rb +41 -0
  12. data/lib/nrser/hash/stringify_keys.rb +37 -0
  13. data/lib/nrser/hash/symbolize_keys.rb +41 -0
  14. data/lib/nrser/hash/transform_keys.rb +45 -0
  15. data/lib/nrser/merge_by.rb +26 -0
  16. data/lib/nrser/message.rb +125 -0
  17. data/lib/nrser/meta/props.rb +2 -2
  18. data/lib/nrser/meta/props/prop.rb +5 -2
  19. data/lib/nrser/object.rb +5 -0
  20. data/lib/nrser/object/as_array.rb +37 -0
  21. data/lib/nrser/object/as_hash.rb +101 -0
  22. data/lib/nrser/{truthy.rb → object/truthy.rb} +0 -0
  23. data/lib/nrser/proc.rb +132 -0
  24. data/lib/nrser/refinements.rb +1 -2
  25. data/lib/nrser/refinements/array.rb +94 -5
  26. data/lib/nrser/refinements/enumerable.rb +5 -0
  27. data/lib/nrser/refinements/hash.rb +43 -6
  28. data/lib/nrser/refinements/object.rb +22 -2
  29. data/lib/nrser/refinements/symbol.rb +12 -0
  30. data/lib/nrser/refinements/tree.rb +41 -0
  31. data/lib/nrser/rspex.rb +329 -0
  32. data/lib/nrser/string.rb +3 -0
  33. data/lib/nrser/string/looks_like.rb +51 -0
  34. data/lib/nrser/temp/where.rb +52 -0
  35. data/lib/nrser/tree.rb +86 -0
  36. data/lib/nrser/tree/leaves.rb +92 -0
  37. data/lib/nrser/tree/map_leaves.rb +63 -0
  38. data/lib/nrser/tree/transform.rb +30 -0
  39. data/lib/nrser/types.rb +9 -4
  40. data/lib/nrser/types/any.rb +1 -1
  41. data/lib/nrser/types/array.rb +167 -25
  42. data/lib/nrser/types/{hash.rb → hashes.rb} +19 -5
  43. data/lib/nrser/types/in.rb +47 -0
  44. data/lib/nrser/types/is_a.rb +2 -2
  45. data/lib/nrser/types/labels.rb +49 -0
  46. data/lib/nrser/types/numbers.rb +63 -27
  47. data/lib/nrser/types/pairs.rb +109 -0
  48. data/lib/nrser/types/responds.rb +2 -3
  49. data/lib/nrser/types/strings.rb +17 -18
  50. data/lib/nrser/types/symbols.rb +39 -0
  51. data/lib/nrser/types/trees.rb +93 -0
  52. data/lib/nrser/types/tuples.rb +116 -0
  53. data/lib/nrser/types/type.rb +26 -2
  54. data/lib/nrser/version.rb +1 -1
  55. data/spec/nrser/hash/{guess_name_type_spec.rb → guess_label_key_type_spec.rb} +3 -3
  56. data/spec/nrser/hash_spec.rb +0 -20
  57. data/spec/nrser/merge_by_spec.rb +73 -0
  58. data/spec/nrser/meta/props_spec.rb +136 -43
  59. data/spec/nrser/op/message_spec.rb +62 -0
  60. data/spec/nrser/refinements/array_spec.rb +36 -0
  61. data/spec/nrser/refinements/hash_spec.rb +34 -0
  62. data/spec/nrser/string/looks_like_spec.rb +31 -0
  63. data/spec/nrser/tree/each_branch_spec.rb +82 -0
  64. data/spec/nrser/tree/leaves_spec.rb +112 -0
  65. data/spec/nrser/tree/transform_spec.rb +165 -0
  66. data/spec/nrser/types/array_spec.rb +82 -0
  67. data/spec/nrser/types/attrs_spec.rb +4 -4
  68. data/spec/nrser/types/pairs_spec.rb +41 -0
  69. data/spec/nrser/types/paths_spec.rb +3 -3
  70. data/spec/nrser/types/strings_spec.rb +66 -0
  71. data/spec/nrser/types/symbols_spec.rb +38 -0
  72. data/spec/nrser/types/tuples_spec.rb +37 -0
  73. data/spec/nrser/types_spec.rb +0 -13
  74. data/spec/spec_helper.rb +71 -22
  75. metadata +58 -10
  76. data/lib/nrser/spex.rb +0 -68
  77. data/lib/nrser/types/symbol.rb +0 -23
@@ -1,5 +1,3 @@
1
- require 'nrser'
2
-
3
1
  require_relative './refinements/object'
4
2
  require_relative './refinements/string'
5
3
  require_relative './refinements/array'
@@ -10,3 +8,4 @@ require_relative './refinements/binding'
10
8
  require_relative './refinements/set'
11
9
  require_relative './refinements/open_struct'
12
10
  require_relative './refinements/enumerator'
11
+ require_relative './refinements/symbol'
@@ -1,8 +1,19 @@
1
+ # Requirements
2
+ # =======================================================================
3
+
4
+ # Project / Package
5
+ # -----------------------------------------------------------------------
1
6
  require_relative './enumerable'
7
+ require_relative './tree'
8
+
9
+
10
+ # Definitions
11
+ # =======================================================================
2
12
 
3
13
  module NRSER
4
14
  refine ::Array do
5
15
  include NRSER::Refinements::Enumerable
16
+ include NRSER::Refinements::Tree
6
17
 
7
18
 
8
19
  # @return [Array]
@@ -14,6 +25,83 @@ module NRSER
14
25
  end # #rest
15
26
 
16
27
 
28
+ # `to_*` Converters
29
+ # =====================================================================
30
+
31
+ # Checks that length is 2 and returns `self`.
32
+ #
33
+ # @return [Array]
34
+ # Array of length 2.
35
+ #
36
+ # @raise [TypeError]
37
+ # If length is not 2.
38
+ #
39
+ def to_pair
40
+ unless length == 2
41
+ raise TypeError,
42
+ "Array is not of length 2: #{ self.inspect }"
43
+ end
44
+
45
+ self
46
+ end # #to_pair
47
+
48
+
49
+ # To Operation Objects
50
+ # ---------------------------------------------------------------------
51
+
52
+ # Creates a new {NRSER::Message} from the array.
53
+ #
54
+ # @example
55
+ #
56
+ # message = [:fetch, :x].to_message
57
+ # message.send_to x: 'ex', y: 'why?'
58
+ # # => 'ex'
59
+ #
60
+ # @return [NRSER::Message]
61
+ #
62
+ def to_message
63
+ NRSER::Message.new *self
64
+ end # #to_message
65
+
66
+ alias_method :to_m, :to_message
67
+
68
+
69
+ # Create a {Proc} that accepts a single `receiver` and provides this array's
70
+ # entries as the arguments to `#public_send` (or `#send` if the `publicly`
71
+ # option is `false`).
72
+ #
73
+ # Equivalent to
74
+ #
75
+ # to_message.to_proc publicly: boolean
76
+ #
77
+ # @example
78
+ #
79
+ # [:fetch, :x].sender.call x: 'ex'
80
+ # # => 'ex'
81
+ #
82
+ # @param [Boolean] publicly:
83
+ # When `true`, uses `#public_send` in liu of `#send`.
84
+ #
85
+ # @return [Proc]
86
+ #
87
+ def to_sender publicly: true
88
+ to_message.to_proc publicly: publicly
89
+ end
90
+
91
+ alias_method :sender, :to_sender
92
+ alias_method :sndr, :to_sender
93
+
94
+
95
+ # See {NRSER.chainer}.
96
+ #
97
+ def to_chainer publicly: true
98
+ NRSER.chainer self, publicly: publicly
99
+ end # #to_chainer
100
+
101
+ alias_method :chainer, :to_chainer
102
+ alias_method :chnr, :to_chainer
103
+
104
+
17
105
  # Returns a lambda that calls accepts a single arg and calls `#dig` on it
18
106
  # with the elements of *this* array as arguments.
19
107
  #
@@ -34,11 +122,12 @@ module NRSER
34
122
  # Lambda proc that accepts a single argument and calls `#dig` with this
35
123
  # array's contents as the `#dig` arguments.
36
124
  #
37
- def digger
38
- ->(digable) {
39
- digable.dig *self
40
- }
41
- end # #digable
125
+ def to_digger
126
+ NRSER::Message.new( :dig, *self ).to_proc
127
+ end # #to_digger
128
+
129
+ alias_method :digger, :to_digger
130
+ alias_method :dggr, :to_digger
42
131
 
43
132
 
44
133
  end # refine ::Array
@@ -37,5 +37,10 @@ module NRSER::Refinements::Enumerable
37
37
  NRSER.enumerate_as_values self
38
38
  end
39
39
 
40
+ # See {NRSER.only!}
41
+ def only!
42
+ NRSER.only! self
43
+ end
44
+
40
45
  end # module NRSER::Refinements::Enumerable
41
46
 
@@ -1,9 +1,21 @@
1
+ # Requirements
2
+ # =======================================================================
3
+
4
+ # Project / Package
5
+ # -----------------------------------------------------------------------
1
6
  require_relative './enumerable'
7
+ require_relative './tree'
8
+
9
+
10
+ # Definitions
11
+ # =======================================================================
2
12
 
3
13
  module NRSER
4
14
 
5
15
  refine ::Hash do
6
16
  include NRSER::Refinements::Enumerable
17
+ include NRSER::Refinements::Tree
18
+
7
19
 
8
20
  # See {NRSER.except_keys!}.
9
21
  def except! *keys
@@ -27,12 +39,6 @@ module NRSER
27
39
  end
28
40
 
29
41
 
30
- # See {NRSER.leaves}.
31
- def leaves
32
- NRSER.leaves self
33
- end # #leaves
34
-
35
-
36
42
  # See {NRSER.transform_keys!}
37
43
  def transform_keys! &block
38
44
  return enum_for(:transform_keys!) { size } unless block_given?
@@ -89,5 +95,36 @@ module NRSER
89
95
  clobber: clobber
90
96
  end
91
97
 
98
+
99
+ # Checks that `self` contains a single key/value pair (`#length` of 1)
100
+ # and returns it as an array of length 2.
101
+ #
102
+ # @return [Array]
103
+ # Array of length 2.
104
+ #
105
+ # @raise [TypeError]
106
+ # If `self` has more than one key/value pair.
107
+ #
108
+ def to_pair
109
+ unless length == 1
110
+ raise TypeError,
111
+ "Hash has more than one pair: #{ self.inspect }"
112
+ end
113
+
114
+ first
115
+ end
116
+
117
+
118
+ # See {NRSER.deep_merge}
119
+ def deep_merge other_hash, &block
120
+ NRSER.deep_merge self, other_hash, &block
121
+ end
122
+
123
+
124
+ # See {NRSER.deep_merge!}
125
+ def deep_merge! other_hash, &block
126
+ NRSER.deep_merge! self, other_hash, &block
127
+ end
128
+
92
129
  end # refine ::Hash
93
130
  end # NRSER
@@ -1,24 +1,44 @@
1
+ # Requirements
2
+ # =======================================================================
3
+
4
+ # Stdlib
5
+ # -----------------------------------------------------------------------
6
+
7
+ # Deps
8
+ # -----------------------------------------------------------------------
9
+
10
+ # Project / Package
11
+ # -----------------------------------------------------------------------
12
+ require 'nrser/object'
13
+
14
+
15
+ # Definitions
16
+ # =======================================================================
17
+
1
18
  module NRSER
2
19
  refine Object do
20
+ # Yield `self`. Analogous to {#tap} but returns the result of the invoked
21
+ # block.
3
22
  def pipe
4
23
  yield self
5
24
  end
6
25
 
26
+ # See {NRSER.truthy?}.
7
27
  def truthy?
8
28
  NRSER.truthy? self
9
29
  end
10
30
 
31
+ # See {NRSER.falsy?}.
11
32
  def falsy?
12
33
  NRSER.falsy? self
13
34
  end
14
35
 
15
36
  # Calls {NRSER.as_hash} on `self` with the provided `key`.
16
- #
17
37
  def as_hash key = nil
18
38
  NRSER.as_hash self, key
19
39
  end
20
40
 
21
- # Calls {NRSER.as_array} in `self`.
41
+ # Call {NRSER.as_array} on `self`.
22
42
  def as_array
23
43
  NRSER.as_array self
24
44
  end
@@ -0,0 +1,12 @@
1
+ module NRSER
2
+ refine ::Symbol do
3
+
4
+ def to_retriever
5
+ NRSER.retriever self
6
+ end
7
+
8
+ alias_method :retriever, :to_retriever
9
+ alias_method :rtvr, :to_retriever
10
+
11
+ end # refine ::Symbol
12
+ end # NRSER
@@ -0,0 +1,41 @@
1
+ # Requirements
2
+ # =======================================================================
3
+
4
+ # Stdlib
5
+ # -----------------------------------------------------------------------
6
+
7
+ # Deps
8
+ # -----------------------------------------------------------------------
9
+
10
+ # Project / Package
11
+ # -----------------------------------------------------------------------
12
+
13
+
14
+ # Declarations
15
+ # =======================================================================
16
+
17
+ module NRSER; end
18
+ module NRSER::Refinements; end
19
+
20
+
21
+ # Definitions
22
+ # =======================================================================
23
+
24
+ # Instance methods that are refined in to the Ruby built-ins that we consider
25
+ # trees: {Array}, {Hash} and {OpenStruct}.
26
+ #
27
+ module NRSER::Refinements::Tree
28
+
29
+ # Sends `self` to {NRSER.leaves}.
30
+ def leaves
31
+ NRSER.leaves self
32
+ end # #leaves
33
+
34
+
35
+ # Sends `self` and the optional `block` to {NRSER.each_branch}.
36
+ def each_branch &block
37
+ NRSER.each_branch self, &block
38
+ end
39
+
40
+ end # module NRSER::Refinements::Tree
41
+
@@ -0,0 +1,329 @@
1
+ # encoding: utf-8
2
+
3
+ ##############################################################################
4
+ # RSpec helpers, shared examples, extensions, and other goodies.
5
+ #
6
+ # This file is *not* required by default when `nrser` is since it **defines
7
+ # global methods** and is not needed unless you're in [Rspec][].
8
+ #
9
+ # [Rspec]: http://rspec.info/
10
+ #
11
+ ##############################################################################
12
+
13
+
14
+ # Requirements
15
+ # =======================================================================
16
+
17
+ # Stdlib
18
+ # -----------------------------------------------------------------------
19
+
20
+ # Deps
21
+ # -----------------------------------------------------------------------
22
+
23
+ # Project / Package
24
+ # -----------------------------------------------------------------------
25
+ require_relative './message'
26
+
27
+
28
+ # Helpers
29
+ # =====================================================================
30
+
31
+ # Merge "expectation" hashes by appending all clauses for each state.
32
+ #
33
+ # @example
34
+ #
35
+ #
36
+ # @param [Array<Hash>] *expectations
37
+ # Splat of "expectation" hashes - see the examples.
38
+ #
39
+ def merge_expectations *expectations
40
+ Hash.new { |result, state|
41
+ result[state] = []
42
+ }.tap { |result|
43
+ expectations.each { |ex|
44
+ ex.each { |state, clauses|
45
+ result[state] += clauses.to_a
46
+ }
47
+ }
48
+ }
49
+ end
50
+
51
+ class Wrapper
52
+ def initialize description: nil, &block
53
+ @description = description
54
+ @block = block
55
+ end
56
+
57
+ def unwrap context: nil
58
+ if context
59
+ context.instance_exec &@block
60
+ else
61
+ @block.call
62
+ end
63
+ end
64
+
65
+ def to_s
66
+ if @description
67
+ @description.to_s
68
+ else
69
+ "#<Wrapper ?>"
70
+ end
71
+ end
72
+ end
73
+
74
+ def wrap description = nil, &block
75
+ Wrapper.new description: description, &block
76
+ end
77
+
78
+ def unwrap obj, context: nil
79
+ if obj.is_a? Wrapper
80
+ obj.unwrap context: context
81
+ else
82
+ obj
83
+ end
84
+ end
85
+
86
+ # Extensions
87
+ # =====================================================================
88
+
89
+ module NRSER; end
90
+
91
+ module NRSER::RSpex
92
+ PREFIXES = {
93
+ # module: "𝓜 𝓸𝓭𝓾𝓵𝓮",
94
+ module: "𝞛",
95
+ section: '§',
96
+ method: '𝞴',
97
+ }
98
+
99
+ # Instance methods to extend example groups with.
100
+ #
101
+ module ExampleGroup
102
+
103
+ # Create a new {RSpec.describe} section where the subject is set by
104
+ # calling the parent subject with `args` and evaluate `block` in it.
105
+ #
106
+ # @example
107
+ # describe "hi sayer" do
108
+ # subject{ ->( name ) { "Hi #{ name }!" } }
109
+ #
110
+ # describe_called_with 'Mom' do
111
+ # it { is_expected.to eq 'Hi Mom!' }
112
+ # end
113
+ # end
114
+ #
115
+ # @param [Array] *args
116
+ # Arguments to call `subject` with to produce the new subject.
117
+ #
118
+ # @param [#call] &block
119
+ # Block to execute in the context of the example group after refining
120
+ # the subject.
121
+ #
122
+ def describe_called_with *args, &block
123
+ describe "called with #{ args.map( &:inspect ).join( ', ' ) }" do
124
+ subject { super().call *args }
125
+ instance_exec &block
126
+ end
127
+ end # #describe_called_with
128
+
129
+ # Aliases to other names I was using at first... not preferring their use
130
+ # at the moment.
131
+ #
132
+ # The `when_` one sucks because Atom de-dents the line, and `describe_`
133
+ # is just clearer what the block is doing for people reading it.
134
+ alias_method :called_with, :describe_called_with
135
+ alias_method :when_called_with, :describe_called_with
136
+
137
+
138
+ def describe_message symbol, *args, &body
139
+ description = \
140
+ "message #{ [symbol, *args].map( &:inspect ).join( ', ' ) }"
141
+
142
+ describe description, type: :message do
143
+ subject { NRSER::Message.new symbol, *args }
144
+ instance_exec &body
145
+ end
146
+ end
147
+
148
+
149
+ # For use when `subject` is a {NRSER::Message}. Create a new context for
150
+ # the `receiver` where the subject is the result of sending that message
151
+ # to the receiver.
152
+ #
153
+ # @param [Object] receiver
154
+ # Object that will receive the message to create the new subject.
155
+ #
156
+ # @param [Boolean] publicly:
157
+ # Send message publicly via {Object#public_send} (default) or privately
158
+ # via {Object.send}.
159
+ #
160
+ # @return
161
+ # Whatever the `context` call returns.
162
+ #
163
+ def describe_sent_to receiver, publicly: true, &block
164
+ mode = if publicly
165
+ "publicly"
166
+ else
167
+ "privately"
168
+ end
169
+
170
+ describe "sent to #{ receiver } (#{ mode })" do
171
+ subject { super().send_to unwrap( receiver, context: self ) }
172
+ instance_exec &block
173
+ end
174
+ end # #describe_sent_to
175
+
176
+ # Aliases to other names I was using at first... not preferring their use
177
+ # at the moment.
178
+ #
179
+ # The `when_` one sucks because Atom de-dents the line, and `describe_`
180
+ # is just clearer what the block is doing for people reading it.
181
+ alias_method :sent_to, :describe_sent_to
182
+ alias_method :when_sent_to, :describe_sent_to
183
+
184
+
185
+ # Describe a "section". Just like {RSpec.describe} except it:
186
+ #
187
+ # 1. Expects a string title.
188
+ #
189
+ # 2. Prepends a little section squiggle `§` to the title so sections are
190
+ # easier to pick out visually.
191
+ #
192
+ # 3. Adds `type: :section` metadata.
193
+ #
194
+ # @param [String] title
195
+ # String title for the section.
196
+ #
197
+ # @param [Hash<Symbol, Object>] **metadata
198
+ # Additional [RSpec metadata][] for the example group.
199
+ #
200
+ # [RSpec metadata]: https://relishapp.com/rspec/rspec-core/docs/metadata/user-defined-metadata
201
+ #
202
+ # @return
203
+ # Whatever {RSpec.describe} returns.
204
+ #
205
+ def describe_section title, **metadata, &block
206
+ describe(
207
+ "#{ NRSER::RSpex::PREFIXES[:section] } #{ title }",
208
+ type: :section,
209
+ **metadata
210
+ ) do
211
+ instance_exec &block
212
+ end
213
+ end # #describe_section
214
+
215
+ # Old name
216
+ alias_method :describe_topic, :describe_section
217
+
218
+
219
+ def describe_module mod, **metadata, &block
220
+ describe(
221
+ "#{ NRSER::RSpex::PREFIXES[:module] } #{ mod.name }",
222
+ type: :module,
223
+ **metadata
224
+ ) do
225
+ instance_exec &block
226
+ end
227
+ end # #describe_module
228
+
229
+
230
+ def describe_method name, **metadata, &block
231
+ describe(
232
+ "#{ NRSER::RSpex::PREFIXES[:method] } #{ name }",
233
+ type: :method,
234
+ **metadata
235
+ ) do
236
+ instance_exec &block
237
+ end
238
+ end # #describe_section
239
+
240
+
241
+ # Define a `context` block with `let` bindings and evaluate the `body`
242
+ # block in it.
243
+ #
244
+ # @param [Hash<Symbol, Object>] **bindings
245
+ # Map of symbol names to value to bind using `let`.
246
+ #
247
+ # @param [#call] &body
248
+ # Body block to evaluate in the context.
249
+ #
250
+ # @return
251
+ # Whatever `context` returns.
252
+ #
253
+ def context_where **bindings, &body
254
+ description = bindings.map { |name, value|
255
+ "let #{ name } = #{ value }"
256
+ }.join( ', ' )
257
+
258
+ context "| #{ description } |", type: :where do
259
+ bindings.each { |name, value|
260
+ let( name ) { unwrap value, context: self }
261
+ }
262
+
263
+ instance_exec &body
264
+ end
265
+ end
266
+
267
+
268
+ end # module ExampleGroup
269
+
270
+ end # module NRSER:RSpex
271
+
272
+ RSpec.configure do |config|
273
+ config.extend NRSER::RSpex::ExampleGroup
274
+ end
275
+
276
+
277
+ include NRSER::RSpex::ExampleGroup
278
+
279
+
280
+ # Shared Examples
281
+ # =====================================================================
282
+
283
+ shared_examples "expect subject" do |*expectations|
284
+ merge_expectations( *expectations ).each { |state, specs|
285
+ specs.each { |verb, noun|
286
+ it {
287
+ # like: is_expected.to(include(noun))
288
+ is_expected.send state, self.send(verb, noun)
289
+ }
290
+ }
291
+ }
292
+ end # is expected
293
+
294
+
295
+ # Shared example for a functional method that compares input and output pairs.
296
+ #
297
+ shared_examples "function" do |mapping: {}, raising: {}|
298
+ mapping.each { |args, expected|
299
+ args = NRSER.as_array args
300
+
301
+ context "called with #{ args.map( &:inspect ).join ', ' }" do
302
+ subject { super().call *args }
303
+
304
+ it {
305
+ expected = unwrap expected, context: self
306
+
307
+ matcher = if expected.respond_to?( :matches? )
308
+ expected
309
+ elsif expected.is_a? NRSER::Message
310
+ expected.send_to self
311
+ else
312
+ eq expected
313
+ end
314
+
315
+ is_expected.to matcher
316
+ }
317
+ end
318
+ }
319
+
320
+ raising.each { |args, error|
321
+ args = NRSER.as_array args
322
+
323
+ context "called with #{ args.map( &:inspect ).join ', ' }" do
324
+ # it "rejects #{ args.map( &:inspect ).join ', ' }" do
325
+ it { expect { subject.call *args }.to raise_error( *error ) }
326
+ end
327
+ }
328
+ end # function
329
+