doc_rspec 0.1.2 → 0.2.0
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/{ast → context}/example.rb +5 -3
- data/lib/doc_rspec/context.rb +32 -0
- data/lib/doc_rspec/parser.rb +37 -39
- 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 +36 -57
- metadata +7 -9
- 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: 88e3a986df1d2228b1455a425584ffa80e6ca725961a5e86e2cf14eb7d600b0c
|
4
|
+
data.tar.gz: ccbe8af928c2762e281e776b451d7a14a981d8f28fef39ac2ceb72f447b297de
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9a1a4f71792902e0454f2e857471f0122f06c2cea648401cdee47e7c6e743fbcd52ea689771edd675d3d69a529c77b2a602b130bbb2246dce045a3e51a020df2
|
7
|
+
data.tar.gz: 0f51c159a2cdde070fc6339c4bf7c2fab713047d391b649f887337c24544ee039f5493df0e66d8172d228b49d630767e61d4a9bf470aed31da3518458970d346
|
@@ -1,7 +1,7 @@
|
|
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
|
|
@@ -13,9 +13,11 @@ class DocRSpec
|
|
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(op, lhs, rhs=nil)
|
14
|
+
examples.last.add(op, lhs, rhs)
|
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,19 @@
|
|
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
|
-
|
11
|
+
CONTEXT_DEFINITION = %r{\A \s* \# \s ={1,7} \s (.*)}x
|
12
|
+
|
13
13
|
EQUALS_OP = %r{\s+ => \s+}x
|
14
|
-
EXAMPLE_LINE = %r{\A \s*
|
14
|
+
EXAMPLE_LINE = %r{\A \s* \# \s{4,} (.*)}x
|
15
15
|
|
16
|
-
START_EXAMPLE = %r{\A \s*
|
17
|
-
START_RDOC_COMMENT = %r{\A \s* \#\#}x
|
18
|
-
STOP_EXAMPLE = %r{\A \s* \#+ \s+ ```}x
|
19
|
-
STOP_RDOC_COMMENT = %r{\A \s* [^#]* \z}x
|
16
|
+
START_EXAMPLE = %r{\A \s* \# \s{4,} \# \s example: \s (.*)}x
|
20
17
|
|
21
18
|
attr_reader :ast, :lines, :state
|
22
19
|
|
@@ -24,26 +21,40 @@ class DocRSpec
|
|
24
21
|
lines.each_with_index do |line, lnb|
|
25
22
|
parse_line(line, lnb.succ)
|
26
23
|
end
|
27
|
-
ast
|
24
|
+
ast.reject(&:empty?)
|
28
25
|
end
|
29
26
|
|
30
27
|
private
|
31
28
|
def initialize(lines)
|
32
|
-
@ast = []
|
29
|
+
@ast = [Context.new(0)]
|
33
30
|
@state = :outside
|
34
31
|
@lines = lines
|
35
32
|
end
|
36
33
|
|
37
|
-
def
|
38
|
-
|
34
|
+
def add_example(lnb, match)
|
35
|
+
name = match[1]
|
36
|
+
current_context.add_example(lnb, name:)
|
37
|
+
@state = :inside
|
38
|
+
end
|
39
|
+
|
40
|
+
def current_context = ast.last
|
41
|
+
|
42
|
+
def maybe_new_context(lnb, match)
|
43
|
+
name = match[1]
|
44
|
+
ast.pop if current_context.empty?
|
45
|
+
ast << Context.new(lnb, name:)
|
46
|
+
@state = :outside
|
47
|
+
end
|
48
|
+
|
49
|
+
def parse_example_line(match)
|
50
|
+
code = match[1]
|
39
51
|
|
40
52
|
case code.split(EQUALS_OP)
|
41
53
|
in [lhs, rhs]
|
42
54
|
ast.last.add_example_line(:eq, lhs, rhs)
|
43
55
|
else
|
44
|
-
ast.last.add_example_line(:verb,
|
56
|
+
ast.last.add_example_line(:verb, code) unless code.empty?
|
45
57
|
end
|
46
|
-
|
47
58
|
end
|
48
59
|
|
49
60
|
def parse_line(line, lnb)
|
@@ -51,45 +62,32 @@ class DocRSpec
|
|
51
62
|
case state
|
52
63
|
when :outside
|
53
64
|
parse_line_outside(line, lnb)
|
54
|
-
when :
|
55
|
-
|
56
|
-
when :inside_example
|
57
|
-
parse_line_inside_example(line, lnb)
|
65
|
+
when :inside
|
66
|
+
parse_line_inside(line, lnb)
|
58
67
|
else
|
59
68
|
raise InternalError, "unexpected state :#{state} (in line: #{line} at #{lnb}"
|
60
69
|
end
|
61
70
|
end
|
62
71
|
|
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)
|
72
|
+
def parse_line_inside(line, lnb)
|
78
73
|
case line
|
74
|
+
when CONTEXT_DEFINITION
|
75
|
+
maybe_new_context(lnb, Regexp.last_match)
|
79
76
|
when START_EXAMPLE
|
80
|
-
|
81
|
-
|
77
|
+
add_example(lnb, Regexp.last_match)
|
78
|
+
when EXAMPLE_LINE
|
79
|
+
parse_example_line(Regexp.last_match)
|
82
80
|
end
|
83
81
|
end
|
84
82
|
|
85
83
|
def parse_line_outside(line, lnb)
|
86
84
|
case line
|
87
|
-
when
|
88
|
-
|
89
|
-
|
85
|
+
when CONTEXT_DEFINITION
|
86
|
+
maybe_new_context(lnb, Regexp.last_match)
|
87
|
+
when START_EXAMPLE
|
88
|
+
add_example(lnb, Regexp.last_match)
|
90
89
|
end
|
91
90
|
end
|
92
|
-
|
93
91
|
end
|
94
92
|
end
|
95
93
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
data/lib/doc_rspec/version.rb
CHANGED
data/lib/doc_rspec.rb
CHANGED
@@ -2,87 +2,66 @@
|
|
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
|
+
# = Usage
|
11
11
|
#
|
12
|
-
# Install the gem
|
12
|
+
# Install the gem <tt>gem install doc_rspec</tt> or put +doc_rspec+ in your Gemfile
|
13
13
|
# or gemspec file.
|
14
14
|
#
|
15
|
-
# Then
|
16
|
-
# or put it into your
|
15
|
+
# Then <tt>require doc_rspec</tt> in the file you want to use the +docspec+ macro
|
16
|
+
# or put it into your +spec_helper.rb+ or use any advanced require strategy
|
17
17
|
# you are used to.
|
18
18
|
#
|
19
|
-
#
|
19
|
+
# Inside your +RSpec+ file, at the example group level then call
|
20
20
|
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
# of the `RSpec` example group `doctest` has been called.
|
21
|
+
# # Usage example
|
22
|
+
# docspec '<path_to_file>'
|
24
23
|
#
|
25
|
-
#
|
26
|
-
# between ` ```spec ` and ` ``` ` will be executed in the context
|
27
|
-
# of one example.
|
24
|
+
# Where +path_to_file+ is relative to the +lib+ directory
|
28
25
|
#
|
29
|
-
#
|
26
|
+
# = Abstract
|
30
27
|
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
# ```
|
28
|
+
# Ruby Codeblocks that start with a comment line as follows
|
29
|
+
# # example: example_name
|
34
30
|
#
|
35
|
-
#
|
31
|
+
# are considered an +RSpec+ example of the +RSpec+ example group +doctest+ has been called.
|
36
32
|
#
|
37
|
-
#
|
38
|
-
# 41 => 42
|
39
|
-
# ```
|
33
|
+
# = RSpec Examples
|
40
34
|
#
|
41
|
-
#
|
42
|
-
# group we call `docspec` in.
|
35
|
+
# == Just plain old Ruby Code
|
43
36
|
#
|
44
|
-
#
|
45
|
-
#
|
46
|
-
# ```
|
37
|
+
# # example: just an RSpec example
|
38
|
+
# expect(2*21).to eq(42)
|
47
39
|
#
|
48
|
-
#
|
49
|
-
# if the expression right to `=>` evaluates to a `Regexp`.
|
40
|
+
# = Implementation Notice
|
50
41
|
#
|
51
|
-
#
|
52
|
-
# DocRSpec::Version::VERSION => %r{\d+\.\d+\.\d+}
|
53
|
-
# ```
|
42
|
+
# # example: We have access to the wrapping context
|
54
43
|
#
|
55
|
-
#
|
44
|
+
# this_is_available(22) => 44
|
56
45
|
#
|
57
|
-
# is
|
46
|
+
# Although this gem is named +doc_rspec+ and its goal is certainly to document behavior
|
47
|
+
# of Ruby code inside the +RDoc+ documentation of that code, and _most_ _importantly_
|
48
|
+
# backing the claims made in this documentation up with *actual* +RSpec+ examples,
|
49
|
+
# in this _MVP_ the parser will simply generate code for all lines matching
|
50
|
+
# <tt>/\A \s* \# \s{3,}/x</tt> after a triggering line macthing
|
51
|
+
# <tt>/\A \s* \# \s{3,} \# \s example: \s (.+)/x</tt>
|
52
|
+
# and potentially ended with the aforementioned <tt>/\A \s* \# \s{3,} \# \s end \s of \s example/x</tt>
|
58
53
|
#
|
59
|
-
#
|
60
|
-
# expect(42).to be > 41
|
61
|
-
# ```
|
62
|
-
# ## Naming Examples
|
54
|
+
# This implies two thing
|
63
55
|
#
|
64
|
-
#
|
65
|
-
#
|
56
|
+
# - you can also write +RSpec+ examples inside your code (inside comments) which are not part of rdoc
|
57
|
+
# - you can use (although that would be missleading) an example like syntax in your +RDoc+ comments
|
58
|
+
# by spacing the ruby code block with only two spaces.
|
66
59
|
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
# example from rdoc (doc_rspec.rb:34)
|
70
|
-
# example from rdoc (doc_rspec.rb:41)
|
71
|
-
# example from rdoc (doc_rspec.rb:49)
|
60
|
+
# The context is taken from the last headline
|
61
|
+
# == First Examples
|
72
62
|
#
|
73
|
-
#
|
74
|
-
#
|
75
|
-
# ```spec # fourtytwo is the answer
|
76
|
-
# the_answer = 42
|
77
|
-
# expect(42).to eq(the_answer)
|
78
|
-
# ```
|
79
|
-
#
|
80
|
-
# Now the output looks like:
|
81
|
-
#
|
82
|
-
# DocRSpec
|
83
|
-
# example from rdoc (doc_rspec.rb:21)
|
84
|
-
# ...
|
85
|
-
# fourtytwo is the answer (doc_rspec.rb:65)
|
63
|
+
# # example: equality
|
64
|
+
# expect(41 + 1).to eq(42)
|
86
65
|
#
|
87
66
|
class DocRSpec
|
88
67
|
|
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.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Robert Dober
|
@@ -37,9 +37,7 @@ 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: []
|
@@ -48,13 +46,13 @@ files:
|
|
48
46
|
- LICENSE
|
49
47
|
- README.md
|
50
48
|
- lib/doc_rspec.rb
|
51
|
-
- lib/doc_rspec/ast.rb
|
52
|
-
- lib/doc_rspec/ast/example.rb
|
53
49
|
- lib/doc_rspec/compiler.rb
|
54
|
-
- lib/doc_rspec/
|
50
|
+
- lib/doc_rspec/context.rb
|
51
|
+
- lib/doc_rspec/context/example.rb
|
55
52
|
- lib/doc_rspec/parser.rb
|
53
|
+
- lib/doc_rspec/rspec_example_group.rb
|
56
54
|
- lib/doc_rspec/version.rb
|
57
|
-
homepage: https://codeberg.org/lab419/
|
55
|
+
homepage: https://codeberg.org/lab419/doc_rspec
|
58
56
|
licenses:
|
59
57
|
- AGPL-3.0-or-later
|
60
58
|
metadata: {}
|
@@ -74,5 +72,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
74
72
|
requirements: []
|
75
73
|
rubygems_version: 3.6.7
|
76
74
|
specification_version: 4
|
77
|
-
summary: '["
|
75
|
+
summary: '["Extract RSpec Examples from RDocs formatted with RDoc::Markup (the default)"]'
|
78
76
|
test_files: []
|
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
|