nrser 0.0.26 → 0.0.27

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/lib/nrser.rb +1 -0
  3. data/lib/nrser/array.rb +15 -0
  4. data/lib/nrser/binding.rb +7 -1
  5. data/lib/nrser/enumerable.rb +21 -1
  6. data/lib/nrser/errors.rb +56 -6
  7. data/lib/nrser/hash/deep_merge.rb +1 -1
  8. data/lib/nrser/message.rb +33 -0
  9. data/lib/nrser/meta/props.rb +77 -15
  10. data/lib/nrser/meta/props/prop.rb +276 -44
  11. data/lib/nrser/proc.rb +7 -3
  12. data/lib/nrser/refinements/array.rb +5 -0
  13. data/lib/nrser/refinements/enumerable.rb +5 -0
  14. data/lib/nrser/refinements/hash.rb +8 -0
  15. data/lib/nrser/refinements/object.rb +11 -1
  16. data/lib/nrser/refinements/string.rb +17 -3
  17. data/lib/nrser/refinements/symbol.rb +8 -0
  18. data/lib/nrser/refinements/tree.rb +22 -0
  19. data/lib/nrser/rspex.rb +312 -70
  20. data/lib/nrser/rspex/shared_examples.rb +116 -0
  21. data/lib/nrser/string.rb +159 -27
  22. data/lib/nrser/temp/unicode_math.rb +48 -0
  23. data/lib/nrser/text.rb +3 -0
  24. data/lib/nrser/text/indentation.rb +210 -0
  25. data/lib/nrser/text/lines.rb +52 -0
  26. data/lib/nrser/text/word_wrap.rb +29 -0
  27. data/lib/nrser/tree.rb +4 -78
  28. data/lib/nrser/tree/each_branch.rb +76 -0
  29. data/lib/nrser/tree/map_branches.rb +91 -0
  30. data/lib/nrser/tree/map_tree.rb +97 -0
  31. data/lib/nrser/tree/transform.rb +56 -13
  32. data/lib/nrser/types.rb +1 -0
  33. data/lib/nrser/types/array.rb +15 -3
  34. data/lib/nrser/types/is_a.rb +40 -1
  35. data/lib/nrser/types/nil.rb +17 -0
  36. data/lib/nrser/types/paths.rb +17 -2
  37. data/lib/nrser/types/strings.rb +57 -22
  38. data/lib/nrser/types/tuples.rb +5 -0
  39. data/lib/nrser/types/type.rb +47 -6
  40. data/lib/nrser/version.rb +1 -1
  41. data/spec/nrser/errors/abstract_method_error_spec.rb +46 -0
  42. data/spec/nrser/meta/props/to_and_from_data_spec.rb +74 -0
  43. data/spec/nrser/meta/props_spec.rb +6 -2
  44. data/spec/nrser/refinements/erb_spec.rb +100 -1
  45. data/spec/nrser/{common_prefix_spec.rb → string/common_prefix_spec.rb} +9 -0
  46. data/spec/nrser/text/dedent_spec.rb +80 -0
  47. data/spec/nrser/tree/map_branch_spec.rb +83 -0
  48. data/spec/nrser/tree/map_tree_spec.rb +123 -0
  49. data/spec/nrser/tree/transform_spec.rb +26 -29
  50. data/spec/nrser/tree/transformer_spec.rb +179 -0
  51. data/spec/nrser/types/paths_spec.rb +73 -45
  52. data/spec/spec_helper.rb +10 -0
  53. metadata +27 -7
  54. data/spec/nrser/dedent_spec.rb +0 -36
@@ -31,8 +31,12 @@ module NRSER
31
31
  #
32
32
  # @return [NRSER::Message]
33
33
  #
34
- def self.message symbol, *args, &block
35
- NRSER::Message.new symbol, *args, &block
34
+ def self.message *args, &block
35
+ if args.length == 1 && args[0].is_a?( Message )
36
+ args[0]
37
+ else
38
+ Message.new *args, &block
39
+ end
36
40
  end # #message
37
41
 
38
42
  singleton_class.send :alias_method, :msg, :message
@@ -102,7 +106,7 @@ module NRSER
102
106
  # @return [Proc]
103
107
  #
104
108
  def self.chainer mappable, publicly: true
105
- messages = mappable.map { |value| NRSER::Message.new *value }
109
+ messages = mappable.map { |value| message *value }
106
110
 
107
111
  ->( receiver ) {
108
112
  messages.reduce( receiver ) { |receiver, message|
@@ -25,6 +25,11 @@ module NRSER
25
25
  end # #rest
26
26
 
27
27
 
28
+ def extract! &block
29
+ NRSER.extract_from_array! self, &block
30
+ end
31
+
32
+
28
33
  # `to_*` Converters
29
34
  # =====================================================================
30
35
 
@@ -37,6 +37,11 @@ module NRSER::Refinements::Enumerable
37
37
  NRSER.enumerate_as_values self
38
38
  end
39
39
 
40
+ # Calls {NRSER.only} on `self`.
41
+ def only **options
42
+ NRSER.only self, **options
43
+ end
44
+
40
45
  # See {NRSER.only!}
41
46
  def only!
42
47
  NRSER.only! self
@@ -58,24 +58,32 @@ module NRSER
58
58
  NRSER.symbolize_keys! self
59
59
  end
60
60
 
61
+ alias_method :sym_keys!, :symbolize_keys!
62
+
61
63
 
62
64
  # See {NRSER.symbolize_keys}
63
65
  def symbolize_keys
64
66
  NRSER.symbolize_keys self
65
67
  end
66
68
 
69
+ alias_method :sym_keys, :symbolize_keys
70
+
67
71
 
68
72
  # See {NRSER.stringify_keys!}
69
73
  def stringify_keys!
70
74
  NRSER.stringify_keys! self
71
75
  end
72
76
 
77
+ alias_method :str_keys!, :stringify_keys!
78
+
73
79
 
74
80
  # See {NRSER.stringify_keys}
75
81
  def stringify_keys
76
82
  NRSER.stringify_keys self
77
83
  end
78
84
 
85
+ alias_method :str_keys, :stringify_keys
86
+
79
87
 
80
88
  # See {NRSER.map_hash_keys}
81
89
  def map_keys &block
@@ -19,10 +19,20 @@ module NRSER
19
19
  refine Object do
20
20
  # Yield `self`. Analogous to {#tap} but returns the result of the invoked
21
21
  # block.
22
- def pipe
22
+ def thru
23
23
  yield self
24
24
  end
25
25
 
26
+ # Older name, depreciated because though 'pipe' was the natural name to me,
27
+ # it was probably a poor choice... it's widely used and usually denotes
28
+ # streaming of some sort (and rightfully so given Unix pipes).
29
+ #
30
+ # I think I want to move over to {Object#thru}, but will leave the old
31
+ # name for the moment.
32
+ #
33
+ alias_method :pipe, :thru
34
+
35
+
26
36
  # See {NRSER.truthy?}.
27
37
  def truthy?
28
38
  NRSER.truthy? self
@@ -1,27 +1,35 @@
1
1
  require 'pathname'
2
2
 
3
+ require 'nrser/string'
4
+ require 'nrser/text'
5
+
3
6
  module NRSER
4
7
  refine String do
5
8
  def squish
6
9
  NRSER.squish self
7
10
  end
8
11
 
12
+
9
13
  def unblock
10
14
  NRSER.unblock self
11
15
  end
12
-
16
+
17
+
13
18
  def dedent
14
19
  NRSER.dedent self
15
20
  end
16
-
21
+
22
+
17
23
  def indent *args
18
24
  NRSER.indent self, *args
19
25
  end
20
-
26
+
27
+
21
28
  def truncate *args
22
29
  NRSER.truncate self, *args
23
30
  end
24
31
 
32
+
25
33
  # See {NRSER.constantize}
26
34
  def constantize
27
35
  NRSER.constantize self
@@ -29,6 +37,7 @@ module NRSER
29
37
 
30
38
  alias_method :to_const, :constantize
31
39
 
40
+
32
41
  # @return [Pathname]
33
42
  # Convert self into a {Pathname}
34
43
  #
@@ -36,5 +45,10 @@ module NRSER
36
45
  Pathname.new self
37
46
  end
38
47
 
48
+
49
+ def whitespace?
50
+ NRSER.whitespace? self
51
+ end
52
+
39
53
  end # refine String
40
54
  end # NRSER
@@ -1,6 +1,7 @@
1
1
  module NRSER
2
2
  refine ::Symbol do
3
3
 
4
+ # See {NRSER.retriever}.
4
5
  def to_retriever
5
6
  NRSER.retriever self
6
7
  end
@@ -8,5 +9,12 @@ module NRSER
8
9
  alias_method :retriever, :to_retriever
9
10
  alias_method :rtvr, :to_retriever
10
11
 
12
+
13
+ # Alias 'sender' methods to built-in {#to_proc} so symbols can behave like
14
+ # arrays in this way
15
+ alias_method :to_sender, :to_proc
16
+ alias_method :sender, :to_sender
17
+ alias_method :sndr, :to_sender
18
+
11
19
  end # refine ::Symbol
12
20
  end # NRSER
@@ -32,10 +32,32 @@ module NRSER::Refinements::Tree
32
32
  end # #leaves
33
33
 
34
34
 
35
+ # Calls {NRSER.map_leaves} on `self` with `&block`.
36
+ #
37
+ def map_leaves &block
38
+ NRSER.map_leaves self, &block
39
+ end
40
+
41
+
35
42
  # Sends `self` and the optional `block` to {NRSER.each_branch}.
43
+ #
36
44
  def each_branch &block
37
45
  NRSER.each_branch self, &block
38
46
  end
39
47
 
48
+
49
+ # Calls {NRSER.map_branches} on `self` with `&block`.
50
+ #
51
+ def map_branches &block
52
+ NRSER.map_branches self, &block
53
+ end # #map_branches
54
+
55
+
56
+ # Calls {NRSER.map_tree} on `self` with `&block`.
57
+ #
58
+ def map_tree **options, &block
59
+ NRSER.map_tree self, **options, &block
60
+ end
61
+
40
62
  end # module NRSER::Refinements::Tree
41
63
 
@@ -22,7 +22,8 @@
22
22
 
23
23
  # Project / Package
24
24
  # -----------------------------------------------------------------------
25
- require_relative './message'
25
+ require 'nrser/message'
26
+ require 'nrser/rspex/shared_examples'
26
27
 
27
28
 
28
29
  # Helpers
@@ -83,23 +84,214 @@ def unwrap obj, context: nil
83
84
  end
84
85
  end
85
86
 
87
+
88
+ def List *args
89
+ NRSER::RSpex::List.new args
90
+ end
91
+
92
+ def Args *args
93
+ NRSER::RSpex::Args.new args
94
+ end
95
+
86
96
  # Extensions
87
97
  # =====================================================================
88
98
 
89
99
  module NRSER; end
90
100
 
91
- module NRSER::RSpex
92
- PREFIXES = {
93
- # module: "𝓜 𝓸𝓭𝓾𝓵𝓮",
94
- module: "𝞛",
101
+ module NRSER::RSpex
102
+
103
+ # Constants
104
+ # =====================================================================
105
+
106
+
107
+ # Symbols
108
+ # ---------------------------------------------------------------------
109
+ #
110
+ # Sources:
111
+ #
112
+ # - https://en.wikipedia.org/wiki/Mathematical_operators_and_symbols_in_Unicode
113
+ #
114
+
115
+ PREFIXES_BASE = {
95
116
  section: '§',
96
- method: '𝞴',
117
+ group: '',
118
+ invocation: '⟮⟯',
97
119
  }
98
120
 
121
+ PREFIXES_MATH_ITALIC = PREFIXES_BASE.merge(
122
+ module: '𝑀',
123
+ method: '𝑚',
124
+ class: '𝐶',
125
+ attribute: '𝑎',
126
+ file: '𝐹',
127
+ )
128
+
129
+ PREFIXES_MATH_CURSIVE_WORDS = PREFIXES_BASE.merge(
130
+ module: '𝓜 𝓸𝓭𝓾𝓵𝓮',
131
+ method: '𝓶𝓮𝓽',
132
+ class: '𝐶',
133
+ attribute: '𝑎',
134
+ file: '𝐹',
135
+ )
136
+
137
+ # PREFIXES_MATH_GREEK = PREFIXES_BASE.merge(
138
+ # # module: "𝓜 𝓸𝓭𝓾𝓵𝓮",
139
+ # module: '𝛭',
140
+ # method: '𝜆',
141
+ # class: '𝛤',
142
+ # attribute: '𝛼',
143
+ # )
144
+
145
+ PREFIXES = PREFIXES_MATH_ITALIC
146
+
147
+
148
+ # Module (Class) Functions
149
+ # =====================================================================
150
+
151
+
152
+ # @todo Document short_s method.
153
+ #
154
+ # @param [type] arg_name
155
+ # @todo Add name param description.
156
+ #
157
+ # @return [return_type]
158
+ # @todo Document return value.
159
+ #
160
+ def self.short_s value, max = 64
161
+ NRSER.smart_ellipsis value.inspect, max
162
+ end # .short_s
163
+
164
+
165
+
166
+ # @todo Document format_type method.
167
+ #
168
+ # @param [type] arg_name
169
+ # @todo Add name param description.
170
+ #
171
+ # @return [return_type]
172
+ # @todo Document return value.
173
+ #
174
+ def self.format_type type, description
175
+ prefixes = RSpec.configuration.x_type_prefixes
176
+
177
+ return description if type.nil? || !prefixes.key?( type )
178
+
179
+ "#{ prefixes[type] } #{ description }"
180
+ end # .format_type
181
+
182
+
183
+
184
+ # @todo Document format method.
185
+ #
186
+ # @param [type] arg_name
187
+ # @todo Add name param description.
188
+ #
189
+ # @return [return_type]
190
+ # @todo Document return value.
191
+ #
192
+ def self.format *parts, type: nil
193
+ format_type \
194
+ type,
195
+ parts.
196
+ map { |part|
197
+ if part.respond_to? :to_desc
198
+ part.to_desc
199
+ elsif part.is_a? String
200
+ part
201
+ else
202
+ short_s part
203
+ end
204
+ }.
205
+ join( ' ' )
206
+ end # .format
207
+
208
+
209
+ class List < Array
210
+ def to_desc max = nil
211
+ max = [16, 64 / self.length].max if max.nil?
212
+ map { |entry| NRSER::RSpex.short_s entry, max }.join ", "
213
+ end
214
+ end
215
+
216
+
217
+ class Opts < Hash
218
+ def to_desc max = nil
219
+ max = [16, ( 64 / self.count )].max if max.nil?
220
+
221
+ map { |key, value|
222
+ if key.is_a? Symbol
223
+ "#{ key }: #{ NRSER::RSpex.short_s value, max }"
224
+ else
225
+ "#{ NRSER::RSpex.short_s key, max } => #{ NRSER::RSpex.short_s value, max }"
226
+ end
227
+ }.join( ", " )
228
+ end
229
+ end
230
+
231
+
232
+ class Args < Array
233
+ def to_desc max = nil
234
+ if last.is_a?( Hash )
235
+ [
236
+ List.new( self[0..-2] ).to_desc,
237
+ Opts[ last ].to_desc,
238
+ ].reject( &:empty? ).join( ", " )
239
+ else
240
+ super
241
+ end
242
+ end
243
+ end
244
+
245
+
99
246
  # Instance methods to extend example groups with.
100
247
  #
101
248
  module ExampleGroup
102
249
 
250
+
251
+ # @todo Document describe_x method.
252
+ #
253
+ # @param [type] arg_name
254
+ # @todo Add name param description.
255
+ #
256
+ # @return [return_type]
257
+ # @todo Document return value.
258
+ #
259
+ def describe_x_type *description_parts,
260
+ type:,
261
+ metadata: {},
262
+ subject_block: nil,
263
+ &body
264
+
265
+ description = NRSER::RSpex.format *description_parts, type: type
266
+
267
+ describe description, **metadata, type: type do
268
+ subject( &subject_block ) if subject_block
269
+ instance_exec &body
270
+ end # description,
271
+
272
+ end # #describe_x
273
+
274
+
275
+ # @todo Document describe_instance method.
276
+ #
277
+ # @param [type] arg_name
278
+ # @todo Add name param description.
279
+ #
280
+ # @return [return_type]
281
+ # @todo Document return value.
282
+ #
283
+ def describe_instance *constructor_args, &body
284
+ describe_x_type ".new(", Args(*constructor_args), ")",
285
+ type: :instance,
286
+ metadata: {
287
+ constructor_args: constructor_args,
288
+ },
289
+ # subject_block: -> { super().new *described_args },
290
+ subject_block: -> { super().new *described_constructor_args },
291
+ &body
292
+ end # #describe_instance
293
+
294
+
103
295
  # Create a new {RSpec.describe} section where the subject is set by
104
296
  # calling the parent subject with `args` and evaluate `block` in it.
105
297
  #
@@ -119,11 +311,11 @@ module NRSER::RSpex
119
311
  # Block to execute in the context of the example group after refining
120
312
  # the subject.
121
313
  #
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
314
+ def describe_called_with *args, &body
315
+ describe_x_type "called with", List(*args),
316
+ type: :invocation,
317
+ subject_block: -> { super().call *args },
318
+ &body
127
319
  end # #describe_called_with
128
320
 
129
321
  # Aliases to other names I was using at first... not preferring their use
@@ -137,7 +329,7 @@ module NRSER::RSpex
137
329
 
138
330
  def describe_message symbol, *args, &body
139
331
  description = \
140
- "message #{ [symbol, *args].map( &:inspect ).join( ', ' ) }"
332
+ "message #{ [symbol, *args].map( &NRSER::RSpex.method( :short_s ) ).join( ', ' ) }"
141
333
 
142
334
  describe description, type: :message do
143
335
  subject { NRSER::Message.new symbol, *args }
@@ -182,6 +374,16 @@ module NRSER::RSpex
182
374
  alias_method :when_sent_to, :describe_sent_to
183
375
 
184
376
 
377
+ def describe_return_value *args, &body
378
+ msg = NRSER::Message.from *args
379
+
380
+ describe "return value from #{ msg }" do
381
+ subject { msg.send_to super() }
382
+ instance_exec &body
383
+ end # "return value from #{ msg }"
384
+ end
385
+
386
+
185
387
  # Describe a "section". Just like {RSpec.describe} except it:
186
388
  #
187
389
  # 1. Expects a string title.
@@ -216,6 +418,20 @@ module NRSER::RSpex
216
418
  alias_method :describe_topic, :describe_section
217
419
 
218
420
 
421
+ def describe_file path, **metadata, &body
422
+ title = path
423
+
424
+ describe(
425
+ "#{ NRSER::RSpex::PREFIXES[:file] } #{ title }",
426
+ type: :file,
427
+ file: path,
428
+ **metadata
429
+ ) do
430
+ instance_exec &body
431
+ end
432
+ end
433
+
434
+
219
435
  def describe_module mod, **metadata, &block
220
436
  describe(
221
437
  "#{ NRSER::RSpex::PREFIXES[:module] } #{ mod.name }",
@@ -227,15 +443,69 @@ module NRSER::RSpex
227
443
  end # #describe_module
228
444
 
229
445
 
446
+ def describe_class klass, bind_subject: true, **metadata, &block
447
+ description = "#{ NRSER::RSpex::PREFIXES[:class] } #{ klass.name }"
448
+
449
+ describe(
450
+ description,
451
+ type: :class,
452
+ class: klass,
453
+ **metadata
454
+ ) do
455
+ if bind_subject
456
+ subject { klass }
457
+ end
458
+
459
+ instance_exec &block
460
+ end
461
+ end # #describe_class
462
+
463
+
464
+ def described_class
465
+ metadata[:class] || super()
466
+ end
467
+
468
+
469
+ def describe_group title, **metadata, &block
470
+ describe(
471
+ "#{ NRSER::RSpex::PREFIXES[:group] } #{ title }",
472
+ type: :group,
473
+ **metadata
474
+ ) do
475
+ instance_exec &block
476
+ end
477
+ end # #describe_class
478
+
479
+
230
480
  def describe_method name, **metadata, &block
231
481
  describe(
232
482
  "#{ NRSER::RSpex::PREFIXES[:method] } #{ name }",
233
483
  type: :method,
484
+ method_name: name,
234
485
  **metadata
235
486
  ) do
487
+ if name.is_a? Symbol
488
+ subject { super().method name }
489
+ end
490
+
236
491
  instance_exec &block
237
492
  end
238
- end # #describe_section
493
+ end # #describe_method
494
+
495
+
496
+ def describe_attribute symbol, **metadata, &block
497
+ describe(
498
+ "#{ NRSER::RSpex::PREFIXES[:attribute] } ##{ symbol }",
499
+ type: :attribute,
500
+ **metadata
501
+ ) do
502
+ subject { super().public_send symbol }
503
+ instance_exec &block
504
+ end
505
+ end # #describe_attribute
506
+
507
+ # Shorter name
508
+ alias_method :describe_attr, :describe_attribute
239
509
 
240
510
 
241
511
  # Define a `context` block with `let` bindings and evaluate the `body`
@@ -250,12 +520,15 @@ module NRSER::RSpex
250
520
  # @return
251
521
  # Whatever `context` returns.
252
522
  #
253
- def context_where **bindings, &body
254
- description = bindings.map { |name, value|
255
- "let #{ name } = #{ value }"
256
- }.join( ', ' )
523
+ def context_where description = nil, **bindings, &body
257
524
 
258
- context "| #{ description } |", type: :where do
525
+ if description.nil?
526
+ description = bindings.map { |name, value|
527
+ "#{ name }: #{ NRSER::RSpex.short_s value }"
528
+ }.join( ", " )
529
+ end
530
+
531
+ context "△ #{ description }", type: :where do
259
532
  bindings.each { |name, value|
260
533
  let( name ) { unwrap value, context: self }
261
534
  }
@@ -267,63 +540,32 @@ module NRSER::RSpex
267
540
 
268
541
  end # module ExampleGroup
269
542
 
543
+
544
+ # Extensions available in examples themselves via RSpec's `config.include`.
545
+ #
546
+ module Example
547
+ def described_class
548
+ self.class.metadata[:class] || super
549
+ end
550
+
551
+ def described_constructor_args
552
+ self.class.metadata[:constructor_args]
553
+ end
554
+
555
+ end
556
+
270
557
  end # module NRSER:RSpex
271
558
 
559
+
272
560
  RSpec.configure do |config|
273
561
  config.extend NRSER::RSpex::ExampleGroup
562
+ config.include NRSER::RSpex::Example
563
+
564
+ config.add_setting :x_type_prefixes
565
+ config.x_type_prefixes = \
566
+ NRSER::RSpex::PREFIXES_BASE.merge( NRSER::RSpex::PREFIXES_MATH_ITALIC )
274
567
  end
275
568
 
276
-
569
+ # Make available at the top-level
277
570
  include NRSER::RSpex::ExampleGroup
278
571
 
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
-