nrser 0.0.26 → 0.0.27

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