nrser 0.1.1 → 0.1.2
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.
- checksums.yaml +4 -4
- data/lib/nrser/env/path.rb +325 -0
- data/lib/nrser/env.rb +12 -0
- data/lib/nrser/errors/attr_error.rb +73 -0
- data/lib/nrser/errors/count_error.rb +19 -0
- data/lib/nrser/errors/nicer_error.rb +110 -0
- data/lib/nrser/errors/value_error.rb +72 -0
- data/lib/nrser/errors.rb +23 -6
- data/lib/nrser/functions/enumerable.rb +9 -2
- data/lib/nrser/functions/path.rb +3 -1
- data/lib/nrser/functions/string.rb +28 -11
- data/lib/nrser/functions/text/indentation.rb +16 -4
- data/lib/nrser/mean_streak/document.rb +151 -0
- data/lib/nrser/mean_streak.rb +95 -0
- data/lib/nrser/refinements/array.rb +6 -0
- data/lib/nrser/refinements/string.rb +6 -0
- data/lib/nrser/rspex/example_group/describe_instance.rb +24 -0
- data/lib/nrser/rspex/example_group/describe_instance_method.rb +20 -0
- data/lib/nrser/rspex/example_group/describe_setup.rb +22 -0
- data/lib/nrser/rspex/example_group/describe_spec_file.rb +127 -0
- data/lib/nrser/rspex/example_group/describe_use_case.rb +18 -0
- data/lib/nrser/rspex/example_group/describe_when.rb +25 -0
- data/lib/nrser/rspex/example_group/describe_x.rb +100 -0
- data/lib/nrser/rspex/example_group.rb +270 -0
- data/lib/nrser/rspex/format.rb +174 -0
- data/lib/nrser/rspex.rb +31 -395
- data/lib/nrser/types/paths.rb +2 -3
- data/lib/nrser/types.rb +5 -1
- data/lib/nrser/version.rb +1 -1
- data/lib/nrser.rb +3 -1
- data/spec/nrser/env/path/insert_spec.rb +65 -0
- data/spec/nrser/env/path_spec.rb +12 -0
- metadata +99 -8
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module NRSER::RSpex::ExampleGroup
|
4
|
+
|
5
|
+
# The core, mostly internal method that all RSpex's description methods lead
|
6
|
+
# back too (or should / will when refactoring is done).
|
7
|
+
#
|
8
|
+
# Keyword options are explicitly broken out in this method, versus the sugary
|
9
|
+
# ones that call it, so `metadata` can be set without restriction (save the
|
10
|
+
# `type` key, which is also it's own keyword here). You can use this method
|
11
|
+
# if you want the RSpex functionality but absolutely have to set some
|
12
|
+
# metadata key that we use for something else.
|
13
|
+
#
|
14
|
+
# @param [Array] *description
|
15
|
+
# Optional list of elements that compose the custom description.
|
16
|
+
#
|
17
|
+
# Will be passed to {NRSER::RSpex::Format.description} to produce the
|
18
|
+
# string value that is in turn passed to {RSpec.describe}.
|
19
|
+
#
|
20
|
+
# @param [Symbol] type:
|
21
|
+
# The RSpex "type" of the example group, which is used to determine the
|
22
|
+
# prefix of the final description and is assigned to the `:type` metadata
|
23
|
+
# key.
|
24
|
+
#
|
25
|
+
# @param [Hash<Symbol, Object>] metadata:
|
26
|
+
# Metadata to add to the new example group.
|
27
|
+
#
|
28
|
+
# In addition to the keys RSpec will reject, we prohibit `:type` *unless*
|
29
|
+
# it is the same as the `type` keyword argument or `nil`.
|
30
|
+
#
|
31
|
+
# In either of these cases, the `type` keyword arg will be used for the new
|
32
|
+
# example group's `:type` metadata value.
|
33
|
+
#
|
34
|
+
# @param [Hash<Symbol, Object>] bindings:
|
35
|
+
#
|
36
|
+
#
|
37
|
+
# @return [return_type]
|
38
|
+
# @todo Document return value.
|
39
|
+
#
|
40
|
+
def describe_x *description,
|
41
|
+
type:,
|
42
|
+
metadata: {},
|
43
|
+
bindings: {},
|
44
|
+
add_binding_desc: true,
|
45
|
+
subject_block: nil,
|
46
|
+
&body
|
47
|
+
|
48
|
+
# Check that `metadata` doesn't have a `:type` value too... although we
|
49
|
+
# allow it if's equal to `type` or `nil` 'cause why not I guess?
|
50
|
+
#
|
51
|
+
if metadata.key?( :type ) &&
|
52
|
+
metadata[:type] != nil &&
|
53
|
+
metadata[:type] != type
|
54
|
+
raise ArgumentError.new binding.erb <<-END
|
55
|
+
`metadata:` keyword argument may not have a `:type` key that conflicts
|
56
|
+
with the `type:` keyword argument.
|
57
|
+
|
58
|
+
Received:
|
59
|
+
`type`:
|
60
|
+
|
61
|
+
<%= type.inspect %>
|
62
|
+
|
63
|
+
`metadata[:type]`:
|
64
|
+
|
65
|
+
<%= metadata[:type].pretty_inspect %>
|
66
|
+
|
67
|
+
END
|
68
|
+
end
|
69
|
+
|
70
|
+
unless bindings.empty? || add_binding_desc == false
|
71
|
+
# bindings_desc = NRSER::RSpex::Opts[bindings].to_desc
|
72
|
+
bindings_desc = ["(", bindings.ai( multiline: false ), ")"]
|
73
|
+
|
74
|
+
if description.empty?
|
75
|
+
description = bindings.ai( multiline: false )
|
76
|
+
else
|
77
|
+
description += ["(", bindings.ai( multiline: false ), ")"]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
formatted = NRSER::RSpex::Format.description *description, type: type
|
82
|
+
|
83
|
+
describe formatted, **metadata, type: type do
|
84
|
+
subject( &subject_block ) if subject_block
|
85
|
+
|
86
|
+
unless bindings.empty?
|
87
|
+
bindings.each { |name, value|
|
88
|
+
let( name ) { unwrap value, context: self }
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
module_exec &body
|
93
|
+
end # description,
|
94
|
+
|
95
|
+
end # #describe_x
|
96
|
+
|
97
|
+
alias_method :describe_x_type, :describe_x
|
98
|
+
|
99
|
+
|
100
|
+
end # module NRSER::RSpex::ExampleGroup
|
@@ -0,0 +1,270 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# encoding: utf-8
|
3
|
+
|
4
|
+
# Instance methods to extend examples groups with. Also included globally so
|
5
|
+
# they're available at the top-level in files.
|
6
|
+
#
|
7
|
+
module NRSER::RSpex::ExampleGroup
|
8
|
+
|
9
|
+
# Create a new {RSpec.describe} section where the subject is set by
|
10
|
+
# calling the parent subject with `args` and evaluate `block` in it.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# describe "hi sayer" do
|
14
|
+
# subject{ ->( name ) { "Hi #{ name }!" } }
|
15
|
+
#
|
16
|
+
# describe_called_with 'Mom' do
|
17
|
+
# it { is_expected.to eq 'Hi Mom!' }
|
18
|
+
# end
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# @param [Array] *args
|
22
|
+
# Arguments to call `subject` with to produce the new subject.
|
23
|
+
#
|
24
|
+
# @param [#call] &block
|
25
|
+
# Block to execute in the context of the example group after refining
|
26
|
+
# the subject.
|
27
|
+
#
|
28
|
+
def describe_called_with *args, &body
|
29
|
+
describe_x_type "called with", List(*args),
|
30
|
+
type: :invocation,
|
31
|
+
subject_block: -> { super().call *args },
|
32
|
+
&body
|
33
|
+
end # #describe_called_with
|
34
|
+
|
35
|
+
# Aliases to other names I was using at first... not preferring their use
|
36
|
+
# at the moment.
|
37
|
+
#
|
38
|
+
# The `when_` one sucks because Atom de-dents the line, and `describe_`
|
39
|
+
# is just clearer what the block is doing for people reading it.
|
40
|
+
alias_method :called_with, :describe_called_with
|
41
|
+
alias_method :when_called_with, :describe_called_with
|
42
|
+
|
43
|
+
|
44
|
+
def describe_message symbol, *args, &body
|
45
|
+
description = \
|
46
|
+
"message #{ [symbol, *args].map( &NRSER::RSpex.method( :short_s ) ).join( ', ' ) }"
|
47
|
+
|
48
|
+
describe description, type: :message do
|
49
|
+
subject { NRSER::Message.new symbol, *args }
|
50
|
+
module_exec &body
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
|
55
|
+
# For use when `subject` is a {NRSER::Message}. Create a new context for
|
56
|
+
# the `receiver` where the subject is the result of sending that message
|
57
|
+
# to the receiver.
|
58
|
+
#
|
59
|
+
# @param [Object] receiver
|
60
|
+
# Object that will receive the message to create the new subject.
|
61
|
+
#
|
62
|
+
# @param [Boolean] publicly:
|
63
|
+
# Send message publicly via {Object#public_send} (default) or privately
|
64
|
+
# via {Object.send}.
|
65
|
+
#
|
66
|
+
# @return
|
67
|
+
# Whatever the `context` call returns.
|
68
|
+
#
|
69
|
+
def describe_sent_to receiver, publicly: true, &block
|
70
|
+
mode = if publicly
|
71
|
+
"publicly"
|
72
|
+
else
|
73
|
+
"privately"
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "sent to #{ receiver } (#{ mode })" do
|
77
|
+
subject { super().send_to unwrap( receiver, context: self ) }
|
78
|
+
module_exec &block
|
79
|
+
end
|
80
|
+
end # #describe_sent_to
|
81
|
+
|
82
|
+
# Aliases to other names I was using at first... not preferring their use
|
83
|
+
# at the moment.
|
84
|
+
#
|
85
|
+
# The `when_` one sucks because Atom de-dents the line, and `describe_`
|
86
|
+
# is just clearer what the block is doing for people reading it.
|
87
|
+
alias_method :sent_to, :describe_sent_to
|
88
|
+
alias_method :when_sent_to, :describe_sent_to
|
89
|
+
|
90
|
+
|
91
|
+
def describe_return_value *args, &body
|
92
|
+
msg = NRSER::Message.from *args
|
93
|
+
|
94
|
+
describe "return value from #{ msg }" do
|
95
|
+
subject { msg.send_to super() }
|
96
|
+
module_exec &body
|
97
|
+
end # "return value from #{ msg }"
|
98
|
+
end
|
99
|
+
|
100
|
+
|
101
|
+
# Describe a "section". Just like {RSpec.describe} except it:
|
102
|
+
#
|
103
|
+
# 1. Expects a string title.
|
104
|
+
#
|
105
|
+
# 2. Prepends a little section squiggle `§` to the title so sections are
|
106
|
+
# easier to pick out visually.
|
107
|
+
#
|
108
|
+
# 3. Adds `type: :section` metadata.
|
109
|
+
#
|
110
|
+
# @param [String] title
|
111
|
+
# String title for the section.
|
112
|
+
#
|
113
|
+
# @param [Hash<Symbol, Object>] **metadata
|
114
|
+
# Additional [RSpec metadata][] for the example group.
|
115
|
+
#
|
116
|
+
# [RSpec metadata]: https://relishapp.com/rspec/rspec-core/docs/metadata/user-defined-metadata
|
117
|
+
#
|
118
|
+
# @return
|
119
|
+
# Whatever {RSpec.describe} returns.
|
120
|
+
#
|
121
|
+
def describe_section title, **metadata, &block
|
122
|
+
describe(
|
123
|
+
"#{ NRSER::RSpex::PREFIXES[:section] } #{ title }",
|
124
|
+
type: :section,
|
125
|
+
**metadata
|
126
|
+
) do
|
127
|
+
module_exec &block
|
128
|
+
end
|
129
|
+
end # #describe_section
|
130
|
+
|
131
|
+
# Old name
|
132
|
+
alias_method :describe_topic, :describe_section
|
133
|
+
|
134
|
+
|
135
|
+
def describe_file path, **metadata, &body
|
136
|
+
title = path
|
137
|
+
|
138
|
+
describe(
|
139
|
+
"#{ NRSER::RSpex::PREFIXES[:file] } #{ title }",
|
140
|
+
type: :file,
|
141
|
+
file: path,
|
142
|
+
**metadata
|
143
|
+
) do
|
144
|
+
module_exec &body
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
def describe_module mod, bind_subject: true, **metadata, &block
|
150
|
+
describe(
|
151
|
+
"#{ NRSER::RSpex::PREFIXES[:module] } #{ mod.name }",
|
152
|
+
type: :module,
|
153
|
+
module: mod,
|
154
|
+
**metadata
|
155
|
+
) do
|
156
|
+
if bind_subject
|
157
|
+
subject { mod }
|
158
|
+
end
|
159
|
+
|
160
|
+
module_exec &block
|
161
|
+
end
|
162
|
+
end # #describe_module
|
163
|
+
|
164
|
+
|
165
|
+
def describe_class klass, bind_subject: true, **metadata, &block
|
166
|
+
description = "#{ NRSER::RSpex::PREFIXES[:class] } #{ klass.name }"
|
167
|
+
|
168
|
+
describe(
|
169
|
+
description,
|
170
|
+
type: :class,
|
171
|
+
class: klass,
|
172
|
+
**metadata
|
173
|
+
) do
|
174
|
+
if bind_subject
|
175
|
+
subject { klass }
|
176
|
+
end
|
177
|
+
|
178
|
+
module_exec &block
|
179
|
+
end
|
180
|
+
end # #describe_class
|
181
|
+
|
182
|
+
|
183
|
+
def described_class
|
184
|
+
metadata[:class] || super()
|
185
|
+
end
|
186
|
+
|
187
|
+
|
188
|
+
def describe_group title, **metadata, &block
|
189
|
+
describe(
|
190
|
+
"#{ NRSER::RSpex::PREFIXES[:group] } #{ title }",
|
191
|
+
type: :group,
|
192
|
+
**metadata
|
193
|
+
) do
|
194
|
+
module_exec &block
|
195
|
+
end
|
196
|
+
end # #describe_class
|
197
|
+
|
198
|
+
|
199
|
+
def describe_method name, **metadata, &block
|
200
|
+
describe(
|
201
|
+
"#{ NRSER::RSpex::PREFIXES[:method] } #{ name }",
|
202
|
+
type: :method,
|
203
|
+
method_name: name,
|
204
|
+
**metadata
|
205
|
+
) do
|
206
|
+
if name.is_a? Symbol
|
207
|
+
subject { super().method name }
|
208
|
+
end
|
209
|
+
|
210
|
+
module_exec &block
|
211
|
+
end
|
212
|
+
end # #describe_method
|
213
|
+
|
214
|
+
|
215
|
+
def describe_attribute symbol, **metadata, &block
|
216
|
+
describe(
|
217
|
+
"#{ NRSER::RSpex::PREFIXES[:attribute] } ##{ symbol }",
|
218
|
+
type: :attribute,
|
219
|
+
**metadata
|
220
|
+
) do
|
221
|
+
subject { super().public_send symbol }
|
222
|
+
module_exec &block
|
223
|
+
end
|
224
|
+
end # #describe_attribute
|
225
|
+
|
226
|
+
# Shorter name
|
227
|
+
alias_method :describe_attr, :describe_attribute
|
228
|
+
|
229
|
+
|
230
|
+
# Define a `context` block with `let` bindings and evaluate the `body`
|
231
|
+
# block in it.
|
232
|
+
#
|
233
|
+
# @param [Hash<Symbol, Object>] **bindings
|
234
|
+
# Map of symbol names to value to bind using `let`.
|
235
|
+
#
|
236
|
+
# @param [#call] &body
|
237
|
+
# Body block to evaluate in the context.
|
238
|
+
#
|
239
|
+
# @return
|
240
|
+
# Whatever `context` returns.
|
241
|
+
#
|
242
|
+
def context_where description = nil, **bindings, &body
|
243
|
+
|
244
|
+
if description.nil?
|
245
|
+
description = bindings.map { |name, value|
|
246
|
+
"#{ name }: #{ NRSER::RSpex.short_s value }"
|
247
|
+
}.join( ", " )
|
248
|
+
end
|
249
|
+
|
250
|
+
context "△ #{ description }", type: :where do
|
251
|
+
bindings.each { |name, value|
|
252
|
+
let( name ) { unwrap value, context: self }
|
253
|
+
}
|
254
|
+
|
255
|
+
module_exec &body
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
end # module NRSER:RSpex::ExampleGroup
|
260
|
+
|
261
|
+
|
262
|
+
# Post-Processing
|
263
|
+
# =======================================================================
|
264
|
+
|
265
|
+
require_relative './example_group/describe_x'
|
266
|
+
require_relative './example_group/describe_spec_file'
|
267
|
+
require_relative './example_group/describe_when'
|
268
|
+
require_relative './example_group/describe_setup'
|
269
|
+
require_relative './example_group/describe_use_case'
|
270
|
+
require_relative './example_group/describe_instance'
|
@@ -0,0 +1,174 @@
|
|
1
|
+
require 'pastel'
|
2
|
+
|
3
|
+
using NRSER
|
4
|
+
|
5
|
+
# Definitions
|
6
|
+
# =======================================================================
|
7
|
+
|
8
|
+
# String formatting utilities.
|
9
|
+
#
|
10
|
+
module NRSER::RSpex::Format
|
11
|
+
|
12
|
+
|
13
|
+
PASTEL = Pastel.new
|
14
|
+
|
15
|
+
def self.transpose_A_z string, lower_a:, upper_a:
|
16
|
+
string
|
17
|
+
.gsub( /[A-Z]/ ) { |char|
|
18
|
+
[upper_a.ord + (char.ord - 'A'.ord)].pack 'U*'
|
19
|
+
}
|
20
|
+
.gsub( /[a-z]/ ) { |char|
|
21
|
+
[lower_a.ord + (char.ord - 'a'.ord)].pack 'U*'
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
# Italicize a string
|
27
|
+
#
|
28
|
+
# @param [type] arg_name
|
29
|
+
# @todo Add name param description.
|
30
|
+
#
|
31
|
+
# @return [return_type]
|
32
|
+
# @todo Document return value.
|
33
|
+
#
|
34
|
+
def self.unicode_italic string
|
35
|
+
transpose_A_z string, lower_a: '𝑎', upper_a: '𝐴'
|
36
|
+
end # .italic
|
37
|
+
|
38
|
+
|
39
|
+
def self.esc_seq_italic string
|
40
|
+
PASTEL.italic string
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
def self.italic string
|
45
|
+
public_send "#{ RSpec.configuration.x_style }_#{ __method__ }", string
|
46
|
+
end
|
47
|
+
|
48
|
+
singleton_class.send :alias_method, :i, :italic
|
49
|
+
|
50
|
+
|
51
|
+
def self.fix_esc_seq commonmark
|
52
|
+
commonmark.gsub( "\e\\[", "\e[" )
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
# @todo Document render_commonmark method.
|
57
|
+
#
|
58
|
+
# @param [type] arg_name
|
59
|
+
# @todo Add name param description.
|
60
|
+
#
|
61
|
+
# @return [return_type]
|
62
|
+
# @todo Document return value.
|
63
|
+
#
|
64
|
+
def self.render_shelldown *render_doc_args
|
65
|
+
doc = CommonMarker.render_doc *render_doc_args
|
66
|
+
|
67
|
+
transformed = transform_node( doc ).only!
|
68
|
+
commonmark = transformed.to_commonmark
|
69
|
+
ansi = fix_esc_seq commonmark
|
70
|
+
ansi
|
71
|
+
end # .render_commonmark
|
72
|
+
|
73
|
+
|
74
|
+
def self.text_node string_content
|
75
|
+
CommonMarker::Node.new( :text ).tap { |node|
|
76
|
+
node.string_content = string_content
|
77
|
+
}
|
78
|
+
end
|
79
|
+
|
80
|
+
|
81
|
+
def self.pastel_node name
|
82
|
+
text_node PASTEL.lookup( name )
|
83
|
+
end
|
84
|
+
|
85
|
+
|
86
|
+
def self.transform_node node
|
87
|
+
case node.type
|
88
|
+
when :emph
|
89
|
+
[
|
90
|
+
pastel_node( :italic ),
|
91
|
+
node.map { |child| transform_node child },
|
92
|
+
pastel_node( :clear ),
|
93
|
+
].flatten
|
94
|
+
when :strong
|
95
|
+
[
|
96
|
+
pastel_node( :bold ),
|
97
|
+
node.map { |child| transform_node child},
|
98
|
+
pastel_node( :clear ),
|
99
|
+
].flatten
|
100
|
+
when :text
|
101
|
+
[node]
|
102
|
+
when :code
|
103
|
+
[
|
104
|
+
pastel_node( :magenta ),
|
105
|
+
text_node( node.string_content ),
|
106
|
+
pastel_node( :clear ),
|
107
|
+
]
|
108
|
+
else
|
109
|
+
new_node = CommonMarker::Node.new node.type
|
110
|
+
|
111
|
+
# new_node.string_content = node.string_content
|
112
|
+
|
113
|
+
node.
|
114
|
+
each { |child|
|
115
|
+
transform_node( child ).each { |new_child|
|
116
|
+
new_node.append_child new_child
|
117
|
+
}
|
118
|
+
}
|
119
|
+
|
120
|
+
[new_node]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
# @todo Document format_type method.
|
126
|
+
#
|
127
|
+
# @param [type] arg_name
|
128
|
+
# @todo Add name param description.
|
129
|
+
#
|
130
|
+
# @return [return_type]
|
131
|
+
# @todo Document return value.
|
132
|
+
#
|
133
|
+
def self.prepend_type type, description
|
134
|
+
return description if type.nil?
|
135
|
+
|
136
|
+
prefixes = RSpec.configuration.x_type_prefixes
|
137
|
+
|
138
|
+
prefix = prefixes[type] ||
|
139
|
+
PASTEL.magenta( i( type.to_s.upcase.gsub('_', ' ') ) )
|
140
|
+
|
141
|
+
"#{ prefix } #{ description }"
|
142
|
+
end # .format_type
|
143
|
+
|
144
|
+
|
145
|
+
# @todo Document format method.
|
146
|
+
#
|
147
|
+
# @param [type] arg_name
|
148
|
+
# @todo Add name param description.
|
149
|
+
#
|
150
|
+
# @return [String]
|
151
|
+
#
|
152
|
+
def self.description *parts, type: nil
|
153
|
+
parts.
|
154
|
+
map { |part|
|
155
|
+
if part.respond_to? :to_desc
|
156
|
+
part.to_desc
|
157
|
+
elsif part.is_a? String
|
158
|
+
part
|
159
|
+
else
|
160
|
+
short_s part
|
161
|
+
end
|
162
|
+
}.
|
163
|
+
join( ' ' ).
|
164
|
+
squish.
|
165
|
+
thru { |description|
|
166
|
+
render_shelldown prepend_type( type, description )
|
167
|
+
}
|
168
|
+
end # .description
|
169
|
+
|
170
|
+
end # module NRSER::RSpex::Format
|
171
|
+
|
172
|
+
|
173
|
+
# Post-Processing
|
174
|
+
# =======================================================================
|