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.
@@ -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
@@ -0,0 +1,2 @@
1
+ module Speculations
2
+ end
@@ -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: []