speculate_about 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|