speculate_about 0.1.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 +7 -0
- data/lib/speculate_about.rb +29 -0
- data/lib/speculations.rb +2 -0
- data/lib/speculations/parser.rb +39 -0
- data/lib/speculations/parser/context.rb +80 -0
- data/lib/speculations/parser/context/example.rb +34 -0
- data/lib/speculations/parser/context/include.rb +24 -0
- data/lib/speculations/parser/context/setup.rb +28 -0
- data/lib/speculations/parser/state.rb +34 -0
- data/lib/speculations/parser/state/bef.rb +17 -0
- data/lib/speculations/parser/state/exa.rb +17 -0
- data/lib/speculations/parser/state/inc.rb +17 -0
- data/lib/speculations/parser/state/out.rb +29 -0
- metadata +69 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 79f9d62e9eca9435df9fc7c9c5851be34190128cb5bf839035ae54623aabd623
|
4
|
+
data.tar.gz: 0ec1ab318bbb646c8fae2fbd40d1c5a0b4b809104559f75499d2edc274dc6862
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f88a8f63272fb0dfe276a00b454d527178ec798378270d62d5ea2d266e01bae8e1260a2edb261444eb0ab3c4582651b3c6f66c7abb0f333d93d0abc50979239e
|
7
|
+
data.tar.gz: ca9364804df625b912058277c01f2b20d526de1a2264c27b7f8d0ceba025074288425d3ed8ce0f9f22c4d31088aa1339daf6bc73f166342b4f18e6a8036bbe45
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
|
3
|
+
require 'speculations/parser'
|
4
|
+
|
5
|
+
module SpeculateAbout
|
6
|
+
def speculate_about file
|
7
|
+
path = _find_file(file)
|
8
|
+
code = _compile path
|
9
|
+
ENV["SPECULATE_ABOUT_DEBUG"] ? puts(code) : instance_eval(code)
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def _compile path
|
16
|
+
ast = Speculations::Parser.new.parse_from_file(path)
|
17
|
+
ast.to_code
|
18
|
+
end
|
19
|
+
def _find_file file
|
20
|
+
return file if File.readable? file
|
21
|
+
path = File.join(File.dirname(location), file)
|
22
|
+
return path if File.readable? path
|
23
|
+
raise ArgumentError, "file #{file} neither found in your project dir, neither your spec dir"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
RSpec.configure do |conf|
|
28
|
+
conf.extend SpeculateAbout
|
29
|
+
end
|
data/lib/speculations.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require_relative '../speculations'
|
2
|
+
class Speculations::Parser
|
3
|
+
require_relative './parser/context'
|
4
|
+
require_relative './parser/state'
|
5
|
+
|
6
|
+
attr_reader :filename, :input, :root, :state
|
7
|
+
|
8
|
+
def self.parsers
|
9
|
+
@__parsers__ ||= {
|
10
|
+
bef: State::Bef,
|
11
|
+
exa: State::Exa,
|
12
|
+
inc: State::Inc,
|
13
|
+
out: State::Out
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse_from_file file
|
18
|
+
@filename = file
|
19
|
+
@input = File
|
20
|
+
.new(file)
|
21
|
+
.each_line(chomp: true)
|
22
|
+
.lazy
|
23
|
+
parse!
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
@state = :out
|
30
|
+
end
|
31
|
+
|
32
|
+
def parse!
|
33
|
+
root = node = Context.new(name: "Speculations from #{@filename}", lnb: 0, filename: @filename, parent: nil)
|
34
|
+
input.each_with_index do |line, lnb|
|
35
|
+
@state, node = self.class.parsers.fetch(@state).parse(line, lnb.succ, node)
|
36
|
+
end
|
37
|
+
root
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
class Speculations::Parser::Context
|
2
|
+
require_relative './context/include'
|
3
|
+
require_relative './context/example'
|
4
|
+
require_relative './context/setup'
|
5
|
+
|
6
|
+
attr_reader :filename, :level, :lnb, :name, :parent, :setup
|
7
|
+
|
8
|
+
def add_child(name:, lnb:)
|
9
|
+
raise "Illegal nesting" if parent
|
10
|
+
children << self.class.new(name: name, lnb: lnb, parent: self)
|
11
|
+
children.last
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_example(lnb:)
|
15
|
+
examples << Example.new(lnb: lnb, parent: self)
|
16
|
+
examples.last
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_include(lnb:)
|
20
|
+
includes << Include.new(lnb: lnb, parent: self)
|
21
|
+
includes.last
|
22
|
+
end
|
23
|
+
|
24
|
+
def children
|
25
|
+
@__children__ ||= []
|
26
|
+
end
|
27
|
+
|
28
|
+
def examples
|
29
|
+
@__examples__ ||= []
|
30
|
+
end
|
31
|
+
|
32
|
+
def includes
|
33
|
+
@__includes__ ||= []
|
34
|
+
end
|
35
|
+
|
36
|
+
def map_lines(*lines, indent: 0)
|
37
|
+
prefix = " " * (level + indent)
|
38
|
+
lines.flatten.map{ |line| "#{prefix}#{line.strip}" }.join("\n")
|
39
|
+
end
|
40
|
+
|
41
|
+
def set_setup(lnb:)
|
42
|
+
@setup = Setup.new(lnb: lnb, parent: self)
|
43
|
+
end
|
44
|
+
|
45
|
+
def to_code
|
46
|
+
[
|
47
|
+
_header,
|
48
|
+
includes.map(&:to_code),
|
49
|
+
setup&.to_code,
|
50
|
+
examples.map(&:to_code),
|
51
|
+
children.map(&:to_code),
|
52
|
+
_footer
|
53
|
+
].flatten.compact.join("\n")
|
54
|
+
end
|
55
|
+
|
56
|
+
private
|
57
|
+
|
58
|
+
def initialize(lnb:, name:, filename: nil, parent: nil)
|
59
|
+
_init_from_parent filename, parent
|
60
|
+
@level = parent ? parent.level.succ : 1
|
61
|
+
@lnb = lnb
|
62
|
+
@setup = nil
|
63
|
+
@name = name
|
64
|
+
@parent = parent
|
65
|
+
end
|
66
|
+
|
67
|
+
def _header
|
68
|
+
map_lines(%{context "#{name}" do}, indent: -1)
|
69
|
+
end
|
70
|
+
|
71
|
+
def _init_from_parent filename, parent
|
72
|
+
@filename = parent ? parent.filename : filename
|
73
|
+
raise ArgumentError, "no filename given in root context" unless @filename
|
74
|
+
end
|
75
|
+
|
76
|
+
def _footer
|
77
|
+
map_lines("end", indent: -1)
|
78
|
+
end
|
79
|
+
|
80
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
class Speculations::Parser::Context::Example
|
2
|
+
|
3
|
+
attr_reader :lnb, :name, :parent
|
4
|
+
|
5
|
+
def add_line line
|
6
|
+
lines << line
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
def lines
|
11
|
+
@__lines__ ||= []
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_code
|
15
|
+
parent.map_lines(_example_head) +
|
16
|
+
"\n" +
|
17
|
+
parent.map_lines(lines, indent: 1) +
|
18
|
+
"\n" +
|
19
|
+
parent.map_lines("end")
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def initialize(lnb:, parent:)
|
26
|
+
@lnb = lnb
|
27
|
+
@name = "Example from #{parent.filename}:#{lnb.succ}"
|
28
|
+
@parent = parent
|
29
|
+
end
|
30
|
+
|
31
|
+
def _example_head
|
32
|
+
%{it "#{name}" do}
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Speculations::Parser::Context::Include
|
2
|
+
|
3
|
+
attr_reader :lnb, :parent
|
4
|
+
|
5
|
+
def add_line line
|
6
|
+
lines << line
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
def lines
|
11
|
+
@__lines__ ||= []
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_code
|
15
|
+
parent.map_lines(lines)
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def initialize(parent:, lnb:)
|
21
|
+
@parent = parent
|
22
|
+
@lnb = lnb
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Speculations::Parser::Context::Setup
|
2
|
+
|
3
|
+
attr_reader :lnb, :parent
|
4
|
+
|
5
|
+
def add_line line
|
6
|
+
lines << line
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
def lines
|
11
|
+
@__lines__ ||= []
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_code
|
15
|
+
parent.map_lines("before do") +
|
16
|
+
"\n" +
|
17
|
+
parent.map_lines(lines, indent: 1) +
|
18
|
+
"\n" +
|
19
|
+
parent.map_lines("end")
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def initialize(lnb:, parent:)
|
26
|
+
@parent = parent
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Speculations::Parser::State extend self
|
2
|
+
require_relative './state/bef'
|
3
|
+
require_relative './state/exa'
|
4
|
+
require_relative './state/inc'
|
5
|
+
require_relative './state/out'
|
6
|
+
|
7
|
+
BEFORE_RGX = %r[\A\s{0,3}```.*\s:before]
|
8
|
+
CONTEXT_RGX = %r[\A\s{0,3}\#{1,7}\s+Context\s+(.*)]
|
9
|
+
EOBLOCK_RGX = %r[\A\s{0,3}```\s*\z]
|
10
|
+
EXAMPLE_RGX = %r[\A\s{0,3}```.*\s:example]
|
11
|
+
INCLUDE_RGX = %r[\A\s{0,3}```.*\s:include]
|
12
|
+
|
13
|
+
def before_match line
|
14
|
+
BEFORE_RGX =~ line
|
15
|
+
end
|
16
|
+
|
17
|
+
def context_match line
|
18
|
+
if match = CONTEXT_RGX.match(line)
|
19
|
+
match.captures.first
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def eoblock_match line
|
24
|
+
EOBLOCK_RGX =~ line
|
25
|
+
end
|
26
|
+
|
27
|
+
def example_match line
|
28
|
+
EXAMPLE_RGX =~ line
|
29
|
+
end
|
30
|
+
|
31
|
+
def include_match line
|
32
|
+
INCLUDE_RGX =~ line
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Speculations
|
2
|
+
class Parser
|
3
|
+
module State
|
4
|
+
module Bef extend self
|
5
|
+
|
6
|
+
def parse line, _lnb, node
|
7
|
+
case
|
8
|
+
when State.eoblock_match(line)
|
9
|
+
[:out, node.parent]
|
10
|
+
else
|
11
|
+
[:bef, node.add_line(line)]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Speculations
|
2
|
+
class Parser
|
3
|
+
module State
|
4
|
+
module Exa extend self
|
5
|
+
|
6
|
+
def parse line, _lnb, node
|
7
|
+
case
|
8
|
+
when State.eoblock_match(line)
|
9
|
+
[:out, node.parent]
|
10
|
+
else
|
11
|
+
[:exa, node.add_line(line)]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Speculations
|
2
|
+
class Parser
|
3
|
+
module State
|
4
|
+
module Inc extend self
|
5
|
+
|
6
|
+
def parse line, _lnb, node
|
7
|
+
case
|
8
|
+
when State.eoblock_match(line)
|
9
|
+
[:out, node.parent]
|
10
|
+
else
|
11
|
+
[:inc, node.add_line(line)]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Speculations
|
2
|
+
class Parser
|
3
|
+
module State
|
4
|
+
module Out extend self
|
5
|
+
|
6
|
+
def parse line, lnb, node
|
7
|
+
case
|
8
|
+
when name = State.context_match(line)
|
9
|
+
node = node.parent if node.parent
|
10
|
+
new_node = node.add_child(name: name, lnb: lnb)
|
11
|
+
[:out, new_node]
|
12
|
+
when State.before_match(line)
|
13
|
+
node = node.set_setup(lnb: lnb)
|
14
|
+
[:bef, node]
|
15
|
+
when State.example_match(line)
|
16
|
+
node = node.add_example(lnb: lnb)
|
17
|
+
[:exa, node]
|
18
|
+
when State.include_match(line)
|
19
|
+
node = node.add_include(lnb: lnb)
|
20
|
+
[:inc, node]
|
21
|
+
else
|
22
|
+
[:out, node]
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: speculate_about
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Robert Dober
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-02-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.9'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.9'
|
27
|
+
description: Allows Markdown or other text files to be used as literal specs, a la
|
28
|
+
Elixr doctest, but from any file.
|
29
|
+
email: robert.dober@gmail.com
|
30
|
+
executables: []
|
31
|
+
extensions: []
|
32
|
+
extra_rdoc_files: []
|
33
|
+
files:
|
34
|
+
- lib/speculate_about.rb
|
35
|
+
- lib/speculations.rb
|
36
|
+
- lib/speculations/parser.rb
|
37
|
+
- lib/speculations/parser/context.rb
|
38
|
+
- lib/speculations/parser/context/example.rb
|
39
|
+
- lib/speculations/parser/context/include.rb
|
40
|
+
- lib/speculations/parser/context/setup.rb
|
41
|
+
- lib/speculations/parser/state.rb
|
42
|
+
- lib/speculations/parser/state/bef.rb
|
43
|
+
- lib/speculations/parser/state/exa.rb
|
44
|
+
- lib/speculations/parser/state/inc.rb
|
45
|
+
- lib/speculations/parser/state/out.rb
|
46
|
+
homepage: https://github.com/robertdober/speculate
|
47
|
+
licenses:
|
48
|
+
- Apache-2.0
|
49
|
+
metadata: {}
|
50
|
+
post_install_message:
|
51
|
+
rdoc_options: []
|
52
|
+
require_paths:
|
53
|
+
- lib
|
54
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
55
|
+
requirements:
|
56
|
+
- - ">="
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: 2.7.0
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
requirements:
|
61
|
+
- - ">="
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
requirements: []
|
65
|
+
rubygems_version: 3.1.2
|
66
|
+
signing_key:
|
67
|
+
specification_version: 4
|
68
|
+
summary: Extract RSpecs from Markdown
|
69
|
+
test_files: []
|