nrser 0.1.1 β†’ 0.1.2

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