doc_rspec 0.1.5 → 0.2.1
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/doc_rspec/compiler.rb +3 -5
- data/lib/doc_rspec/{ast → context}/example.rb +7 -5
- data/lib/doc_rspec/context.rb +32 -0
- data/lib/doc_rspec/parser.rb +53 -42
- data/lib/doc_rspec/{example_group.rb → rspec_example_group.rb} +1 -1
- data/lib/doc_rspec/version.rb +1 -1
- data/lib/doc_rspec.rb +56 -55
- metadata +8 -11
- data/.rdoc_options +0 -27
- data/lib/doc_rspec/ast.rb +0 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2fb556df1f9e8f00edb81eb6fc6db4c0d86d7864dbaedda0a9e559625df7fe98
|
4
|
+
data.tar.gz: 69296b360896f5a2258884bc59dc26b115648b107242f8f877eecca507b3b371
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 641237f09a349ea510b93a562d3548e8398409af64c58f03be21499ce42eee8053f0b2fa4d596befdb6ac035747ba761f3e6e494de5c317966554756c74921cc
|
7
|
+
data.tar.gz: b7100cb9cf8d8b016c4466409fd4a608d569f022e62e20a30bacf90031e65f7047fad2d7d3e758504db4fb64d732a072c4cbb1a8fef853a40e8588a4a17b0230
|
data/lib/doc_rspec/compiler.rb
CHANGED
@@ -30,15 +30,13 @@ class DocRSpec
|
|
30
30
|
def compile_example(example_spec)
|
31
31
|
# See comment above for an explanation why we capture
|
32
32
|
# this method as a function into a closure
|
33
|
-
compile_example_line = method(:compile_example_line)
|
34
33
|
example_group.it example_spec.it_name(path) do
|
35
34
|
example = self
|
36
|
-
|
35
|
+
code = example_spec
|
37
36
|
.lines
|
38
|
-
.
|
39
|
-
.compact
|
37
|
+
.join("\n")
|
40
38
|
|
41
|
-
eval(
|
39
|
+
eval(code) unless code.empty?
|
42
40
|
end
|
43
41
|
end
|
44
42
|
|
@@ -1,21 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
class DocRSpec
|
4
|
-
class
|
4
|
+
class Context
|
5
5
|
class Example
|
6
6
|
attr_reader :lines, :lnb, :name
|
7
7
|
|
8
|
-
def add(
|
9
|
-
lines <<
|
8
|
+
def add(code)
|
9
|
+
lines << code
|
10
10
|
end
|
11
11
|
|
12
12
|
def it_name(path)
|
13
13
|
"#{name || "example from rdoc"} (#{path}:#{lnb})"
|
14
14
|
end
|
15
15
|
|
16
|
+
def empty? = lines.empty?
|
17
|
+
|
16
18
|
private
|
17
|
-
def initialize(lnb,
|
18
|
-
@name =
|
19
|
+
def initialize(lnb, name:)
|
20
|
+
@name = name
|
19
21
|
@lnb = lnb
|
20
22
|
@lines = []
|
21
23
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'context/example'
|
4
|
+
class DocRSpec
|
5
|
+
class Context
|
6
|
+
attr_reader :examples, :lnb, :name
|
7
|
+
|
8
|
+
def add_example(lnb, name:)
|
9
|
+
examples.pop if examples.last&.empty?
|
10
|
+
examples << Example.new(lnb, name:)
|
11
|
+
end
|
12
|
+
|
13
|
+
def add_example_line(code)
|
14
|
+
examples.last.add(code)
|
15
|
+
end
|
16
|
+
|
17
|
+
def context_name(path)
|
18
|
+
"#{name || "docspec"} (#{path}:#{lnb})"
|
19
|
+
end
|
20
|
+
|
21
|
+
def empty? = examples.empty?
|
22
|
+
|
23
|
+
private
|
24
|
+
def initialize(lnb, name: nil)
|
25
|
+
@lnb = lnb
|
26
|
+
@examples = []
|
27
|
+
@name = name
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
# SPDX-License-Identifier: AGPL-3.0-or-later
|
data/lib/doc_rspec/parser.rb
CHANGED
@@ -1,22 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'context'
|
4
4
|
|
5
5
|
class DocRSpec
|
6
6
|
class Parser
|
7
7
|
class InternalError < RuntimeError; end
|
8
8
|
class SyntaxError < RuntimeError; end
|
9
9
|
|
10
|
-
COMMENT = %r{\A \s* \# \s*}x
|
11
10
|
|
12
|
-
|
13
|
-
EQUALS_OP = %r{\s+ => \s+}x
|
14
|
-
EXAMPLE_LINE = %r{\A \s* \#+ \s+ (.*)}x
|
11
|
+
CONTEXT_DEFINITION = %r{\A \s* \# \s ={1,7} \s (.*)}x
|
15
12
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
13
|
+
EXAMPLE_LINE = %r{\A \s* \# \s{4,} (.*)}x
|
14
|
+
|
15
|
+
SHORT_EQUALS = %r{\s eq\! \s}x
|
16
|
+
SHORT_MATCHES = %r{\s \= \~ \s}x
|
17
|
+
SHORT_PREDICATE = %r{\s is\! \s}x
|
18
|
+
START_EXAMPLE = %r{\A \s* \# \s{4,} \# \s example: \s (.*)}x
|
20
19
|
|
21
20
|
attr_reader :ast, :lines, :state
|
22
21
|
|
@@ -24,26 +23,51 @@ class DocRSpec
|
|
24
23
|
lines.each_with_index do |line, lnb|
|
25
24
|
parse_line(line, lnb.succ)
|
26
25
|
end
|
27
|
-
ast
|
26
|
+
ast.reject(&:empty?)
|
28
27
|
end
|
29
28
|
|
30
29
|
private
|
31
30
|
def initialize(lines)
|
32
|
-
@ast = []
|
31
|
+
@ast = [Context.new(0)]
|
33
32
|
@state = :outside
|
34
33
|
@lines = lines
|
35
34
|
end
|
36
35
|
|
37
|
-
def
|
38
|
-
|
36
|
+
def add_example(lnb, match)
|
37
|
+
name = match[1]
|
38
|
+
current_context.add_example(lnb, name:)
|
39
|
+
@state = :inside
|
40
|
+
end
|
41
|
+
|
42
|
+
def current_context = ast.last
|
43
|
+
|
44
|
+
def maybe_new_context(lnb, match)
|
45
|
+
name = match[1]
|
46
|
+
ast.pop if current_context.empty?
|
47
|
+
ast << Context.new(lnb, name:)
|
48
|
+
@state = :outside
|
49
|
+
end
|
50
|
+
|
51
|
+
def parse_example_line(match)
|
52
|
+
code = match[1]
|
53
|
+
return if code.empty?
|
39
54
|
|
40
|
-
case code.split(
|
55
|
+
case code.split(SHORT_EQUALS)
|
41
56
|
in [lhs, rhs]
|
42
|
-
ast.last.add_example_line(
|
57
|
+
ast.last.add_example_line("expect(#{lhs}).to eq(#{rhs})")
|
43
58
|
else
|
44
|
-
|
59
|
+
case code.split(SHORT_MATCHES)
|
60
|
+
in [lhs, rhs]
|
61
|
+
ast.last.add_example_line("expect(#{lhs}).to match(#{rhs})")
|
62
|
+
else
|
63
|
+
case code.split(SHORT_PREDICATE)
|
64
|
+
in [lhs, rhs]
|
65
|
+
ast.last.add_example_line("expect(#{lhs}).to be_#{rhs}")
|
66
|
+
else
|
67
|
+
ast.last.add_example_line(code)
|
68
|
+
end
|
69
|
+
end
|
45
70
|
end
|
46
|
-
|
47
71
|
end
|
48
72
|
|
49
73
|
def parse_line(line, lnb)
|
@@ -51,45 +75,32 @@ class DocRSpec
|
|
51
75
|
case state
|
52
76
|
when :outside
|
53
77
|
parse_line_outside(line, lnb)
|
54
|
-
when :
|
55
|
-
|
56
|
-
when :inside_example
|
57
|
-
parse_line_inside_example(line, lnb)
|
78
|
+
when :inside
|
79
|
+
parse_line_inside(line, lnb)
|
58
80
|
else
|
59
81
|
raise InternalError, "unexpected state :#{state} (in line: #{line} at #{lnb}"
|
60
82
|
end
|
61
83
|
end
|
62
84
|
|
63
|
-
def
|
64
|
-
case line
|
65
|
-
when EMPTY_LINE
|
66
|
-
nil
|
67
|
-
when STOP_EXAMPLE
|
68
|
-
ast.last.close_example { raise SyntaxError, "Empty docspec example in line #{ast.last.examples.last.lnb}" }
|
69
|
-
@state = :inside_rdoc
|
70
|
-
when STOP_RDOC_COMMENT
|
71
|
-
raise SyntaxError, "Missing end of ```spec (starting at #{ast.last.examples.last.lnb}) at end of rdoc comment at #{lnb}"
|
72
|
-
else
|
73
|
-
parse_example_line(line, lnb)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
def parse_line_inside_rdoc(line, lnb)
|
85
|
+
def parse_line_inside(line, lnb)
|
78
86
|
case line
|
87
|
+
when CONTEXT_DEFINITION
|
88
|
+
maybe_new_context(lnb, Regexp.last_match)
|
79
89
|
when START_EXAMPLE
|
80
|
-
|
81
|
-
|
90
|
+
add_example(lnb, Regexp.last_match)
|
91
|
+
when EXAMPLE_LINE
|
92
|
+
parse_example_line(Regexp.last_match)
|
82
93
|
end
|
83
94
|
end
|
84
95
|
|
85
96
|
def parse_line_outside(line, lnb)
|
86
97
|
case line
|
87
|
-
when
|
88
|
-
|
89
|
-
|
98
|
+
when CONTEXT_DEFINITION
|
99
|
+
maybe_new_context(lnb, Regexp.last_match)
|
100
|
+
when START_EXAMPLE
|
101
|
+
add_example(lnb, Regexp.last_match)
|
90
102
|
end
|
91
103
|
end
|
92
|
-
|
93
104
|
end
|
94
105
|
end
|
95
106
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
data/lib/doc_rspec/version.rb
CHANGED
data/lib/doc_rspec.rb
CHANGED
@@ -2,87 +2,88 @@
|
|
2
2
|
|
3
3
|
require 'rspec'
|
4
4
|
require_relative 'doc_rspec/compiler'
|
5
|
-
require_relative 'doc_rspec/
|
5
|
+
require_relative 'doc_rspec/rspec_example_group'
|
6
6
|
require_relative 'doc_rspec/parser'
|
7
|
-
RSpec.configure { it.extend DocRSpec::
|
7
|
+
RSpec.configure { it.extend DocRSpec::RSpecExampleGroup }
|
8
8
|
|
9
9
|
##
|
10
|
-
#
|
10
|
+
# = Code
|
11
11
|
#
|
12
|
-
#
|
12
|
+
# can be found [at Codeberg](https://codeberg.org/lab419/doc_rspec)
|
13
|
+
#
|
14
|
+
# = Usage
|
15
|
+
#
|
16
|
+
# Install the gem <tt>gem install doc_rspec</tt> or put +doc_rspec+ in your Gemfile
|
13
17
|
# or gemspec file.
|
14
18
|
#
|
15
|
-
# Then
|
16
|
-
# or put it into your
|
19
|
+
# Then <tt>require doc_rspec</tt> in the file you want to use the +docspec+ macro
|
20
|
+
# or put it into your +spec_helper.rb+ or use any advanced require strategy
|
17
21
|
# you are used to.
|
18
22
|
#
|
19
|
-
#
|
23
|
+
# Inside your +RSpec+ file, at the example group level then call
|
24
|
+
#
|
25
|
+
# docspec '<path_to_file>'
|
26
|
+
#
|
27
|
+
# Where +path_to_file+ is relative to the +lib+ directory
|
28
|
+
#
|
29
|
+
# = Abstract
|
30
|
+
#
|
31
|
+
# Ruby Codeblocks that start with a comment line as follows
|
32
|
+
# # example: example_name
|
33
|
+
#
|
34
|
+
# are considered an +RSpec+ example of the +RSpec+ example group +doctest+ has been called.
|
35
|
+
#
|
36
|
+
# = RSpec Examples
|
37
|
+
#
|
38
|
+
# == Just plain old Ruby Code
|
39
|
+
#
|
40
|
+
# # example: just an RSpec example
|
41
|
+
# expect(2*21).to eq(42)
|
42
|
+
#
|
43
|
+
# = Implementation Notice
|
44
|
+
#
|
45
|
+
# # example: We have access to the wrapping context
|
20
46
|
#
|
21
|
-
#
|
22
|
-
# between ` ```spec ` and ` ``` ` will be executed in the context
|
23
|
-
# of the `RSpec` example group `doctest` has been called.
|
47
|
+
# this_is_available(22) eq! 44
|
24
48
|
#
|
25
|
-
#
|
26
|
-
# between ` ```spec ` and ` ``` ` will be executed in the context
|
27
|
-
# of one example.
|
49
|
+
# This implies two thing
|
28
50
|
#
|
29
|
-
#
|
51
|
+
# - you can also write +RSpec+ examples inside your code (inside comments) which are not part of rdoc
|
52
|
+
# - you can use (although that would be missleading) an example like syntax in your +RDoc+ comments
|
53
|
+
# by spacing the ruby code block with only two spaces.
|
30
54
|
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
# ```
|
55
|
+
# The context is taken from the last headline
|
56
|
+
# == First Examples
|
34
57
|
#
|
35
|
-
#
|
58
|
+
# # example: equality
|
59
|
+
# expect(41 + 1).to eq(42)
|
36
60
|
#
|
37
|
-
#
|
38
|
-
# 41 => 42
|
39
|
-
# ```
|
61
|
+
# == Shortcuts
|
40
62
|
#
|
41
|
-
#
|
42
|
-
# group we call `docspec` in.
|
63
|
+
# In addition to _standard_ +RSpec+ code, one can use 3 shorthand forms that are compiled as follows
|
43
64
|
#
|
44
|
-
#
|
45
|
-
# this_is_available(21) => 42
|
46
|
-
# ```
|
65
|
+
# === +eq!+
|
47
66
|
#
|
48
|
-
#
|
49
|
-
# if the expression right to `=>` evaluates to a `Regexp`.
|
67
|
+
# # example: eq! shorthand for eq
|
50
68
|
#
|
51
|
-
#
|
52
|
-
# DocRSpec::Version::VERSION => %r{\d+\.\d+\.\d+}
|
53
|
-
# ```
|
69
|
+
# answer = 42
|
54
70
|
#
|
55
|
-
#
|
71
|
+
# answer eq! 42
|
56
72
|
#
|
57
|
-
#
|
73
|
+
# this was compiled to <tt>expect(answer).to eq(42)</tt>
|
58
74
|
#
|
59
|
-
#
|
60
|
-
# expect(42).to be > 41
|
61
|
-
# ```
|
62
|
-
# ## Naming Examples
|
75
|
+
# === +=~+ for match
|
63
76
|
#
|
64
|
-
#
|
65
|
-
# `formatter=:doc` looks like this:
|
77
|
+
# # example: =~ shorthand for match
|
66
78
|
#
|
67
|
-
#
|
68
|
-
# example from rdoc (doc_rspec.rb:21)
|
69
|
-
# example from rdoc (doc_rspec.rb:34)
|
70
|
-
# example from rdoc (doc_rspec.rb:41)
|
71
|
-
# example from rdoc (doc_rspec.rb:49)
|
79
|
+
# "alpha" =~ /\Aa.+a\z/
|
72
80
|
#
|
73
|
-
#
|
81
|
+
# === +is!+ for be_
|
74
82
|
#
|
75
|
-
#
|
76
|
-
# the_answer = 42
|
77
|
-
# expect(42).to eq(the_answer)
|
78
|
-
# ```
|
79
|
-
#
|
80
|
-
# Now the output looks like:
|
83
|
+
# # example: =~ shorthand for be
|
81
84
|
#
|
82
|
-
#
|
83
|
-
#
|
84
|
-
# ...
|
85
|
-
# fourtytwo is the answer (doc_rspec.rb:65)
|
85
|
+
# require 'ostruct'
|
86
|
+
# OpenStruct.new(ok?: true) is! ok
|
86
87
|
#
|
87
88
|
class DocRSpec
|
88
89
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: doc_rspec
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1
|
4
|
+
version: 0.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Dober
|
@@ -37,25 +37,22 @@ dependencies:
|
|
37
37
|
- - "~>"
|
38
38
|
- !ruby/object:Gem::Version
|
39
39
|
version: 3.13.0
|
40
|
-
description:
|
41
|
-
|
42
|
-
'
|
40
|
+
description: Extract RSpec Examples from RDocs formatted with RDoc::Markup (the default)
|
43
41
|
email: robert.dober@gmail.com
|
44
42
|
executables: []
|
45
43
|
extensions: []
|
46
44
|
extra_rdoc_files: []
|
47
45
|
files:
|
48
|
-
- ".rdoc_options"
|
49
46
|
- LICENSE
|
50
47
|
- README.md
|
51
48
|
- lib/doc_rspec.rb
|
52
|
-
- lib/doc_rspec/ast.rb
|
53
|
-
- lib/doc_rspec/ast/example.rb
|
54
49
|
- lib/doc_rspec/compiler.rb
|
55
|
-
- lib/doc_rspec/
|
50
|
+
- lib/doc_rspec/context.rb
|
51
|
+
- lib/doc_rspec/context/example.rb
|
56
52
|
- lib/doc_rspec/parser.rb
|
53
|
+
- lib/doc_rspec/rspec_example_group.rb
|
57
54
|
- lib/doc_rspec/version.rb
|
58
|
-
homepage: https://codeberg.org/lab419/
|
55
|
+
homepage: https://codeberg.org/lab419/doc_rspec
|
59
56
|
licenses:
|
60
57
|
- AGPL-3.0-or-later
|
61
58
|
metadata: {}
|
@@ -73,7 +70,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
73
70
|
- !ruby/object:Gem::Version
|
74
71
|
version: '0'
|
75
72
|
requirements: []
|
76
|
-
rubygems_version: 3.6.
|
73
|
+
rubygems_version: 3.6.8
|
77
74
|
specification_version: 4
|
78
|
-
summary: '["
|
75
|
+
summary: '["Extract RSpec Examples from RDocs formatted with RDoc::Markup (the default)"]'
|
79
76
|
test_files: []
|
data/.rdoc_options
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
---
|
2
|
-
encoding: UTF-8
|
3
|
-
static_path: []
|
4
|
-
rdoc_include: []
|
5
|
-
page_dir:
|
6
|
-
apply_default_exclude: true
|
7
|
-
autolink_excluded_words: []
|
8
|
-
charset: UTF-8
|
9
|
-
class_module_path_prefix:
|
10
|
-
embed_mixins: false
|
11
|
-
exclude: []
|
12
|
-
file_path_prefix:
|
13
|
-
hyperlink_all: false
|
14
|
-
line_numbers: false
|
15
|
-
locale_dir: locale
|
16
|
-
locale_name:
|
17
|
-
main_page:
|
18
|
-
markup: markdown
|
19
|
-
output_decoration: true
|
20
|
-
show_hash: false
|
21
|
-
skip_tests: true
|
22
|
-
tab_width: 8
|
23
|
-
template_stylesheets: []
|
24
|
-
title:
|
25
|
-
visibility: :protected
|
26
|
-
warn_missing_rdoc_ref: true
|
27
|
-
webcvs:
|
data/lib/doc_rspec/ast.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative 'ast/example'
|
4
|
-
class DocRSpec
|
5
|
-
class Ast
|
6
|
-
attr_reader :examples, :lnb
|
7
|
-
|
8
|
-
def add_example(lnb, match)
|
9
|
-
examples << Example.new(lnb, match)
|
10
|
-
end
|
11
|
-
|
12
|
-
def add_example_line(op, lhs, rhs=nil)
|
13
|
-
examples.last.add(op, lhs, rhs)
|
14
|
-
end
|
15
|
-
|
16
|
-
def close_example(&error_handler)
|
17
|
-
return unless examples.last.lines.empty?
|
18
|
-
|
19
|
-
error_handler.()
|
20
|
-
end
|
21
|
-
|
22
|
-
def context_name(path)
|
23
|
-
"docspec #{path}:#{lnb}"
|
24
|
-
end
|
25
|
-
|
26
|
-
private
|
27
|
-
def initialize(lnb)
|
28
|
-
@lnb = lnb
|
29
|
-
@examples = []
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
33
|
-
end
|
34
|
-
# SPDX-License-Identifier: AGPL-3.0-or-later
|