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.
@@ -116,8 +116,10 @@ module NRSER
116
116
  Pathname.glob( path ).find { |match_path|
117
117
  match_path.public_send test
118
118
  }
119
+ elsif path.public_send( test )
120
+ path
119
121
  else
120
- path.public_send test
122
+ nil
121
123
  end
122
124
 
123
125
  unless found_path.nil?
@@ -121,10 +121,18 @@ module NRSER
121
121
  end # .truncate
122
122
 
123
123
 
124
- # Cut the middle out of a string and stick an ellipsis in there instead.
124
+ # Cut the middle out of a sliceable object with length and stick an ellipsis
125
+ # in there instead.
125
126
  #
126
- # @param [String] string
127
- # Source string.
127
+ # Categorized with {String} functions 'cause that's where it started, and
128
+ # that's probably how it will primarily continue to be used, but tested to
129
+ # work on {Array} and should for other classes that satisfy the same
130
+ # slice and interface.
131
+ #
132
+ # @param [V & #length & #slice & #<< & #+] source
133
+ # Source object. In practice, {String} and {Array} work. In theory,
134
+ # anything that responds to `#length`, `#slice`, `#<<` and `#+` with the
135
+ # same semantics will work.
128
136
  #
129
137
  # @param [Fixnum] max
130
138
  # Max length to allow for the output string.
@@ -134,18 +142,27 @@ module NRSER
134
142
  # removed. Defaults to the unicode ellipsis since I'm targeting the CLI
135
143
  # at the moment and it saves precious characters.
136
144
  #
137
- # @return [String]
138
- # String of at most `max` length with the middle chopped out if needed
139
- # to do so.
140
- def self.ellipsis string, max, omission: UNICODE_ELLIPSIS
141
- return string unless string.length > max
145
+ # @return [V]
146
+ # Object of the same type as `source` of at most `max` length with the
147
+ # middle chopped out if needed to do so.\*
148
+ #
149
+ # \* Really, it has to do with how all the used methods are implemented,
150
+ # but we hope that conforming classes will return instances of their own
151
+ # class like {String} and {Array} do.
152
+ #
153
+ def self.ellipsis source, max, omission: UNICODE_ELLIPSIS
154
+ return source unless source.length > max
142
155
 
143
156
  trim_to = max - omission.length
157
+ middle = trim_to / 2
158
+ remainder = trim_to % 2
144
159
 
145
- start = string[0, (trim_to / 2) + (trim_to % 2)]
146
- finish = string[-( (trim_to / 2) - (trim_to % 2) )..-1]
160
+ start = source.slice( 0, middle + remainder )
161
+ start << omission
147
162
 
148
- start + omission + finish
163
+ finish = source.slice( -( middle - remainder )..-1 )
164
+
165
+ start + finish
149
166
  end # .ellipsis
150
167
 
151
168
 
@@ -52,10 +52,16 @@ module NRSER
52
52
  end
53
53
 
54
54
 
55
- def self.dedent text, ignore_whitespace_lines: true
55
+ def self.dedent text,
56
+ ignore_whitespace_lines: true,
57
+ return_lines: false
56
58
  return text if text.empty?
57
59
 
58
- all_lines = text.lines
60
+ all_lines = if text.is_a?( Array )
61
+ text
62
+ else
63
+ text.lines
64
+ end
59
65
 
60
66
  indent_significant_lines = if ignore_whitespace_lines
61
67
  all_lines.reject { |line| whitespace? line }
@@ -67,7 +73,7 @@ module NRSER
67
73
 
68
74
  return text if indent.empty?
69
75
 
70
- all_lines.map { |line|
76
+ dedented_lines = all_lines.map { |line|
71
77
  if line.start_with? indent
72
78
  line[indent.length..-1]
73
79
  elsif line.end_with? "\n"
@@ -75,7 +81,13 @@ module NRSER
75
81
  else
76
82
  ""
77
83
  end
78
- }.join
84
+ }
85
+
86
+ if return_lines
87
+ dedented_lines
88
+ else
89
+ dedented_lines.join
90
+ end
79
91
  end # .dedent
80
92
 
81
93
  # I like dedent better, but other libs seems to call it deindent
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Requirements
4
+ # =======================================================================
5
+
6
+ # Stdlib
7
+ # -----------------------------------------------------------------------
8
+
9
+ # Deps
10
+ # -----------------------------------------------------------------------
11
+
12
+ # Project / Package
13
+ # -----------------------------------------------------------------------
14
+
15
+
16
+ # Refinements
17
+ # =======================================================================
18
+
19
+
20
+ # Declarations
21
+ # =======================================================================
22
+
23
+
24
+ # Definitions
25
+ # =======================================================================
26
+
27
+
28
+ # @todo document NRSER::MeanStreak::Document class.
29
+ class NRSER::MeanStreak::Document
30
+
31
+ # Constants
32
+ # ======================================================================
33
+
34
+
35
+ # Class Methods
36
+ # ======================================================================
37
+
38
+ # @todo Document from method.
39
+ #
40
+ # @param [type] arg_name
41
+ # @todo Add name param description.
42
+ #
43
+ # @return [NRSER::MeanStreak::Document]
44
+ # @todo Document return value.
45
+ #
46
+ def self.parse source,
47
+ mean_streak:,
48
+ cm_options: :DEFAULT,
49
+ cm_extensions: []
50
+ new mean_streak: mean_streak,
51
+ source: source,
52
+ doc: CommonMarker.render_doc( source, options, extensions )
53
+ end # .parse
54
+
55
+ singleton_class.send :alias_method, :from_string, :parse
56
+ singleton_class.send :alias_method, :from_s, :parse
57
+
58
+
59
+
60
+ # Attributes
61
+ # ======================================================================
62
+
63
+ # The {NRSER::MeanStreak} instance associated with this document, which
64
+ # contains the rendering configuration.
65
+ #
66
+ # @return [NRSER::MeanStreak]
67
+ #
68
+ attr_reader :mean_streak
69
+
70
+
71
+ # The source string.
72
+ #
73
+ # @return [String]
74
+ #
75
+ attr_reader :source
76
+
77
+
78
+ # The root {CommonMarker::Node} (with {CommonMarker::Node#type}=`:document`).
79
+ #
80
+ # @return [CommonMarker::Node]
81
+ #
82
+ attr_reader :doc
83
+
84
+
85
+ # Constructor
86
+ # ======================================================================
87
+
88
+ # Instantiate a new `NRSER::MeanStreak::Document`.
89
+ #
90
+ # @param mean_streak
91
+ # See {NRSER::MeanStreak::Document#mean_streak}
92
+ #
93
+ def initialize mean_streak:, source:, doc:
94
+ @mean_streak = mean_streak
95
+ @source = source.dup.freeze
96
+ @doc = doc
97
+ end
98
+
99
+
100
+ # Instance Methods
101
+ # ======================================================================
102
+
103
+ # The lines in {#source} as a {Hamster::Vector} of frozen strings.
104
+ #
105
+ # @return [Hamster::Vector<String>]
106
+ #
107
+ def source_lines
108
+ @source_lines = Hamster::Vector.new source.lines.map( &:freeze )
109
+ end
110
+
111
+
112
+ # Get the substring of the source that a node came from (via its
113
+ # `#sourcepos`).
114
+ #
115
+ # @return [String]
116
+ #
117
+ def source_for_node node
118
+ pos = node.sourcepos
119
+
120
+ if pos[:start_line] == pos[:end_line]
121
+ source_lines[pos[:start_line] - 1][
122
+ (pos[:start_column] - 1)...pos[:end_column]
123
+ ]
124
+ else
125
+ lines = source_lines[(pos[:start_line] - 1)...pos[:end_line]]
126
+
127
+ # Trim the start off the first line, unless the start column is 1
128
+ unless pos[:start_column] == 1
129
+ lines[0] = lines[0][(pos[:start_column] - 1)..-1]
130
+ end
131
+
132
+ # Trim the end off the first line, unless the end column is the last
133
+ # line's length
134
+ unless pos[:end_column] == lines[-1].length
135
+ lines[-1] = lines[-1][0...pos[:end_column]]
136
+ end
137
+
138
+ lines.join
139
+ end
140
+ end
141
+
142
+
143
+ def render_node node, output = ''
144
+
145
+ end
146
+
147
+ end # class NRSER::MeanStreak::Document
148
+
149
+
150
+ # Post-Processing
151
+ # =======================================================================
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Requirements
4
+ # =======================================================================
5
+
6
+ # Stdlib
7
+ # -----------------------------------------------------------------------
8
+
9
+ # Deps
10
+ # -----------------------------------------------------------------------
11
+ require 'commonmarker'
12
+
13
+ # Project / Package
14
+ # -----------------------------------------------------------------------
15
+
16
+ # Refinements
17
+ # =======================================================================
18
+
19
+
20
+ # Declarations
21
+ # =======================================================================
22
+
23
+
24
+ # Definitions
25
+ # =======================================================================
26
+
27
+
28
+ # Tag up terminals with color and style. Uses {CommonMarker} for the parsing
29
+ # (Markdown / CommonMark / GFM syntax).
30
+ #
31
+ # {NRSER::MeanStreak} instances hold configuration and provide functionality
32
+ # through instance methods, making it easy to use multiple configurations or
33
+ # subclass {NRSER::MeanStreak} to further customize functionality.
34
+ #
35
+ # An instance with default configuration is available via the {.default}
36
+ # class method, and additional class methods are provided that proxy to the
37
+ # default's instance methods, providing convenient use of the default config.
38
+ #
39
+ class NRSER::MeanStreak
40
+
41
+ # Class Methods
42
+ # ======================================================================
43
+
44
+ # Get the default instance, which has the default configuration and is
45
+ # used by the class methods.
46
+ #
47
+ # @return [NRSER::MeanStreak]
48
+ #
49
+ def self.default
50
+ # TODO cache?
51
+ new
52
+ end # .default
53
+
54
+
55
+ # Public: Parses a Markdown string into a `document` node.
56
+ #
57
+ # string - {String} to be parsed
58
+ # option - A {Symbol} or {Array of Symbol}s indicating the parse options
59
+ # extensions - An {Array of Symbol}s indicating the extensions to use
60
+ #
61
+ # @return [CommonMarker::Node]
62
+ # The `document` node.
63
+ #
64
+ def self.parse text, options = :DEFAULT, extensions = []
65
+
66
+ end
67
+
68
+
69
+ # Instance Methods
70
+ # ============================================================================
71
+
72
+
73
+ # @todo Document parse method.
74
+ #
75
+ # @param [type] arg_name
76
+ # @todo Add name param description.
77
+ #
78
+ # @return [return_type]
79
+ # @todo Document return value.
80
+ #
81
+ def parse source, **options
82
+ NRSER::MeanStreak::Document.parse \
83
+ source,
84
+ **options,
85
+ mean_streak: self
86
+ end # #parse
87
+
88
+
89
+ end # class NRSER::ShellDown
90
+
91
+
92
+ # Post-Processing
93
+ # =======================================================================
94
+
95
+ require_relative './mean_streak/document'
@@ -18,6 +18,12 @@ module NRSER
18
18
  end
19
19
 
20
20
 
21
+ # Calls {NRSER.ellipsis} on `self`.
22
+ def ellipsis *args
23
+ NRSER.ellipsis self, *args
24
+ end
25
+
26
+
21
27
  # `to_*` Converters
22
28
  # =====================================================================
23
29
 
@@ -50,5 +50,11 @@ module NRSER
50
50
  NRSER.whitespace? self
51
51
  end
52
52
 
53
+
54
+ # Calls {NRSER.ellipsis} on `self`.
55
+ def ellipsis *args
56
+ NRSER.ellipsis self, *args
57
+ end
58
+
53
59
  end # refine String
54
60
  end # NRSER
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NRSER::RSpex::ExampleGroup
4
+
5
+ # @todo Document describe_instance method.
6
+ #
7
+ # @param [type] arg_name
8
+ # @todo Add name param description.
9
+ #
10
+ # @return [return_type]
11
+ # @todo Document return value.
12
+ #
13
+ def describe_instance *constructor_args, &body
14
+ describe_x_type ".new(", Args(*constructor_args), ")",
15
+ type: :instance,
16
+ metadata: {
17
+ constructor_args: constructor_args,
18
+ },
19
+ # subject_block: -> { super().new *described_args },
20
+ subject_block: -> { super().new *described_constructor_args },
21
+ &body
22
+ end # #describe_instance
23
+
24
+ end # module NRSER::RSpex::ExampleGroup
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NRSER::RSpex::ExampleGroup
4
+
5
+ def describe_instance_method name, **metadata, &block
6
+ describe(
7
+ "#{ NRSER::RSpex::PREFIXES[:method] } #{ name }",
8
+ type: :method,
9
+ method_name: name,
10
+ **metadata
11
+ ) do
12
+ if name.is_a? Symbol
13
+ subject { super().method name }
14
+ end
15
+
16
+ module_exec &block
17
+ end
18
+ end # #describe_method
19
+
20
+ end # module NRSER::RSpex::ExampleGroup
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NRSER::RSpex::ExampleGroup
4
+
5
+ # Setup describes what's going to be *done* in all child examples.
6
+ #
7
+ # It's where you setup your `subject`, usually depending on `let`
8
+ # bindings that are provided in the children.
9
+ #
10
+ # @return [void]
11
+ #
12
+ def describe_setup *description, **metadata, &body
13
+ describe_x \
14
+ *description,
15
+ type: :setup,
16
+ metadata: metadata,
17
+ &body
18
+ end # #describe_setup
19
+
20
+ alias_method :setup, :describe_setup
21
+
22
+ end # module NRSER::RSpex::ExampleGroup
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NRSER::RSpex::ExampleGroup
4
+
5
+ # **EXPERIMENTAL**
6
+ #
7
+ # Example group helper for use at the top level of each spec file to
8
+ # set a bunch of stuff up and build a helpful description.
9
+ #
10
+ # @todo
11
+ # This is totally just a one-off right now... would need to be
12
+ # generalized quite a bit...
13
+ #
14
+ # 1. Extraction of module, class, etc from metadata should be flexible
15
+ #
16
+ # 2. Built description would need to be conditional on what metadata
17
+ # was found.
18
+ #
19
+ # @param [String] description:
20
+ # A description of the spec file to add to the RSpec description.
21
+ #
22
+ # @param [String] spec_path:
23
+ # The path to the spec file (just feed it `__FILE__`).
24
+ #
25
+ # Probably possible to extract this somehow without having to provide it?
26
+ #
27
+ # @return [nil]
28
+ #
29
+ def describe_spec_file description: nil,
30
+ spec_path:,
31
+ bind_subject: true,
32
+ **metadata,
33
+ &body
34
+
35
+ if metadata[:module] && metadata[:method]
36
+ meth = metadata[:module].method metadata[:method]
37
+ file, line = meth.source_location
38
+ path = Pathname.new file
39
+ loc = "./#{ path.relative_path_from Pathname.getwd }:#{ line }"
40
+
41
+ spec_rel_path = \
42
+ "./#{ Pathname.new( spec_path ).relative_path_from Pathname.getwd }"
43
+
44
+ desc = [
45
+ "#{ metadata[:module].name }.#{ metadata[:method] }",
46
+ "(#{ loc })",
47
+ description,
48
+ "Spec (#{ spec_rel_path})"
49
+ ].compact.join " "
50
+
51
+ subj = meth
52
+
53
+ elsif metadata[:class]
54
+ klass = metadata[:class]
55
+
56
+ if metadata[:instance_method]
57
+ instance_method = klass.instance_method metadata[:instance_method]
58
+
59
+ file, line = instance_method.source_location
60
+
61
+ name = "#{ klass.name }##{ metadata[:instance_method] }"
62
+ else
63
+ name = klass.name
64
+
65
+ # Get a reasonable file and line for the class
66
+ file, line = klass.
67
+ # Get an array of all instance methods, excluding inherited ones
68
+ # (the `false` arg)
69
+ instance_methods( false ).
70
+ # Add `#initialize` since it isn't in `#instance_methods` for some
71
+ # reason
72
+ <<( :initialize ).
73
+ # Map those to their {UnboundMethod} objects
74
+ map { |sym| klass.instance_method sym }.
75
+ # Toss any `nil` values
76
+ compact.
77
+ # Get the source locations
78
+ map( &:source_location ).
79
+ # Get the first line in the shortest path
80
+ min_by { |(path, line)| [path.length, line] }
81
+
82
+ # Another approach I thought of... (untested)
83
+ #
84
+ # Get the path
85
+ # # Get frequency of the paths
86
+ # count_by { |(path, line)| path }.
87
+ # # Get the one with the most occurrences
88
+ # max_by { |path, count| count }.
89
+ # # Get just the path (not the count)
90
+ # first
91
+ end
92
+
93
+ location = if file
94
+ "(#{ NRSER::RSpex.dot_rel_path file }:#{ line })"
95
+ end
96
+
97
+ desc = [
98
+ "𝑆𝑃𝐸𝐢 𝐹𝐼𝐿𝐸 `#{ NRSER::RSpex.dot_rel_path spec_path }` 𝐹𝑂𝑅",
99
+ name,
100
+ location,
101
+ description,
102
+ ].compact.join " "
103
+
104
+ subj = klass
105
+
106
+ else
107
+ # TODO Make this work!
108
+ raise ArgumentError.new binding.erb <<-END
109
+ Not yet able to handle metadata:
110
+
111
+ <%= metadata.pretty_inspect %>
112
+
113
+ END
114
+ end
115
+
116
+ describe desc, **metadata do
117
+ if bind_subject
118
+ subject { subj }
119
+ end
120
+
121
+ module_exec &body
122
+ end
123
+
124
+ nil
125
+ end # #describe_spec_file
126
+
127
+ end # module NRSER::RSpex::ExampleGroup
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NRSER::RSpex::ExampleGroup
4
+
5
+ # @todo Document describe_use_case method.
6
+ #
7
+ # @return [void]
8
+ #
9
+ def describe_use_case *description, where: {}, **metadata, &body
10
+ describe_x \
11
+ *description,
12
+ type: :use_case,
13
+ bindings: where,
14
+ metadata: metadata,
15
+ &body
16
+ end # #describe_use_case
17
+
18
+ end # module NRSER::RSpex::ExampleGroup
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module NRSER::RSpex::ExampleGroup
4
+
5
+ # Define a example group block with `let` bindings and evaluate the `body`
6
+ # block in it.
7
+ #
8
+ # @param [Hash<Symbol, Object>] **bindings
9
+ # Map of symbol names to value to bind using `let`.
10
+ #
11
+ # @param [#call] &body
12
+ # Body block to evaluate in the context.
13
+ #
14
+ # @return
15
+ # Whatever `context` returns.
16
+ #
17
+ def describe_when *description, **bindings, &body
18
+ describe_x \
19
+ *description,
20
+ type: :when,
21
+ bindings: bindings,
22
+ &body
23
+ end
24
+
25
+ end # module NRSER::RSpex::ExampleGroup