nrser 0.0.25 → 0.0.26

Sign up to get free protection for your applications and to get access to all the features.
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
+