speculate_about 0.4.2 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1d08ccbaea3017274bdc86160387aeed915eba6aac5e5119cca48c2e8f4a53a3
4
- data.tar.gz: dd53be1c870c773d263a59b03204b469c593e0244df48ea19aaac1be87a45b5d
3
+ metadata.gz: b7194ffffa00a4292176dafa4120ea24ddf47e8aa6b6ed04d8be2dfaeefcfb8b
4
+ data.tar.gz: bb6b85091a5c9c59465f7df1f2369f33aa6e3090c249ee4cdc9184a9ef1580e3
5
5
  SHA512:
6
- metadata.gz: 222b7eff1615cae7c4e3af5fbaffae7812e9e1144de7884b5a3b7d798e7647f9ea259dddf632e5262977d6a07da1234f22457c84eb656c225ba7745b1a317f49
7
- data.tar.gz: 70c8e9c20f41acc002bfdda9ae3fa6f63561538730989c63e25e0460a65785b21d5835c9f16efc4f41ccaf22a51b179a27bc77c42a7585afa0474d2e95a9dcb8
6
+ metadata.gz: 2e88641b31f6adf08b14730b619fbc31828f4d04b8058946cb1b9b71d7cecf05695d9d1b59849184b31220d88d7fd0d821e0b318f24cc68d803ed303cb792b97
7
+ data.tar.gz: 7de92ee9f101df89939eea6b8c0b42513d88222bbef60fa51fe2a9413add66e50b5f1da0ca7ff46d7ca02811b31be7ceb925c45509a3ad8c5dabb82fcc28f5e0
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/speculations/cli'
3
+
4
+ Speculations::CLI.run(ARGV)
@@ -1,2 +1,29 @@
1
- module Speculations
1
+ require 'fileutils'
2
+ require_relative 'speculations/parser'
3
+ module Speculations extend self
4
+
5
+ def compile(infile, outfile=nil)
6
+ raise ArgumentError, "#{infile} not found" unless File.readable? infile
7
+ outfile ||= _speculation_path(infile)
8
+ if _out_of_date?(outfile, infile)
9
+ ast = Speculations::Parser.new.parse_from_file(infile)
10
+ File.write(outfile, ast.to_code.join("\n"))
11
+ end
12
+ outfile
13
+ end
14
+
15
+ private
16
+
17
+ def _out_of_date?(outf, inf)
18
+ return true unless File.exists? outf
19
+ return File.lstat(outf).mtime <= File.lstat(inf).mtime
20
+ end
21
+
22
+ def _speculation_path(file)
23
+ dir = File.dirname(file)
24
+ dest_dir = File.join("spec", "speculations", dir)
25
+ FileUtils.mkdir_p(dest_dir) unless File.directory?(dest_dir)
26
+ rspec = File.basename(file, ".md")
27
+ File.join(dest_dir, "#{rspec}_spec.rb")
28
+ end
2
29
  end
@@ -0,0 +1,49 @@
1
+
2
+ module Speculations
3
+ module CLI extend self
4
+ def run args
5
+ loop do
6
+ case args.first
7
+ when "-h", "--help"
8
+ _usage
9
+ when "-v", "--version"
10
+ _version
11
+ else
12
+ return _compile_and_maybe_run args
13
+ end
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def _compile_and_maybe_run args
20
+ require_relative "../speculations"
21
+ args = Dir.glob(["*.md", "speculations/**/*.md"]) if args.empty?
22
+ args.each do |input_file|
23
+ Speculations.compile(input_file)
24
+ end
25
+ end
26
+
27
+ def _usage
28
+ puts <<-EOF
29
+ usage:
30
+ #{$0} [options] [filenames]
31
+
32
+ options:
33
+ -h | --help display this exit with -1
34
+ -v | --version display version of the speculate_about gem exit with -2
35
+
36
+ filenames (default to all markdown files in the project directory and its speculations subdirectories)
37
+
38
+ recreate outdated speculations in `spec/speculations/`
39
+ EOF
40
+ exit -1
41
+ end
42
+
43
+ def _version
44
+ require_relative 'version'
45
+ puts VERSION
46
+ exit -2
47
+ end
48
+ end
49
+ end
@@ -1,41 +1,39 @@
1
- require_relative '../speculations'
2
- class Speculations::Parser
3
- require_relative './parser/context'
4
- require_relative './parser/state'
1
+ module Speculations
2
+ class Parser
3
+ require_relative './parser/context'
4
+ require_relative './parser/state'
5
5
 
6
- attr_reader :alternate_syntax, :filename, :input, :orig_filename, :root, :state
7
6
 
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
7
+ def self.parsers
8
+ @__parsers__ ||= {
9
+ candidate: State::Candidate,
10
+ in: State::In,
11
+ out: State::Out
12
+ }
13
+ end
16
14
 
17
- def parse_from_file file, orig_filename = nil, alternate_syntax: false
18
- @alternate_syntax = alternate_syntax
19
- @filename = file
20
- @orig_filename = orig_filename || file
21
- @input = File
22
- .new(file)
23
- .each_line(chomp: true)
24
- .lazy
25
- parse!
26
- end
15
+ def parse_from_file file
16
+ @filename = file
17
+ @input = File
18
+ .new(file)
19
+ .each_line(chomp: true)
20
+ .lazy
21
+ parse!
22
+ end
27
23
 
28
- private
24
+ private
29
25
 
30
- def initialize
31
- @state = :out
32
- end
26
+ def initialize
27
+ @state = :out
28
+ end
33
29
 
34
- def parse!
35
- root = node = Context.new(alternate_syntax: alternate_syntax, name: "Speculations from #{@filename}", lnb: 0, filename: @filename, orig_filename: orig_filename, parent: nil)
36
- input.each_with_index do |line, lnb|
37
- @state, node = self.class.parsers.fetch(@state).parse(line, lnb.succ, node)
30
+ def parse!
31
+ root = node = Context.new(filename: @filename)
32
+ ctxt = nil
33
+ @input.each_with_index do |line, lnb|
34
+ @state, node, ctxt = self.class.parsers.fetch(@state).parse(line, lnb.succ, node, ctxt)
35
+ end
36
+ root
38
37
  end
39
- root
40
38
  end
41
39
  end
@@ -1,109 +1,129 @@
1
- class Speculations::Parser::Context
2
- require_relative './context/include'
3
- require_relative './context/example'
4
- require_relative './context/setup'
5
-
6
- attr_reader :alternate_syntax, :filename, :level, :lnb, :name, :orig_filename, :parent, :potential_name, :setup
7
-
8
- def add_child(name:, lnb:)
9
- raise "Illegal nesting" if parent
10
- children << self.class.new(alternate_syntax: alternate_syntax, name: name, lnb: lnb, parent: self)
11
- children.last
12
- end
13
-
14
- def add_example(lnb:, line:)
15
- examples << Example.new(lnb: lnb, parent: self, line: line, name: potential_name)
16
- reset_context
17
- examples.last
18
- end
19
-
20
- def add_include(lnb:)
21
- includes << Include.new(lnb: lnb, parent: self)
22
- @given = false
23
- includes.last
24
- end
25
-
26
- def children
27
- @__children__ ||= []
28
- end
29
-
30
- def examples
31
- @__examples__ ||= []
32
- end
33
-
34
- def given?
35
- @given
36
- end
37
-
38
- def includes
39
- @__includes__ ||= []
40
- end
41
-
42
- def map_lines(*lines, indent: 0)
43
- prefix = " " * (level + indent)
44
- lines.flatten.map{ |line| "#{prefix}#{line.strip}" }.join("\n")
45
- end
46
-
47
- def reset_context
48
- @potential_name = nil
49
- @given = false
50
- self
1
+ module Speculations
2
+ class Parser
3
+ class Context
4
+ require_relative './context/include'
5
+ require_relative './context/example'
6
+
7
+ DISCLAIMER = <<-EOD
8
+ # DO NOT EDIT!!!
9
+ # This file was generated from FILENAME with the speculate_about gem, if you modify this file
10
+ # one of two bad things will happen
11
+ # - your documentation specs are not correct
12
+ # - your modifications will be overwritten by the speculate rake task
13
+ # YOU HAVE BEEN WARNED
14
+ EOD
15
+
16
+ attr_reader :filename, :level, :lnb, :title, :parent, :root, :tree_level
17
+
18
+ def new_context(title:, lnb:, level: )
19
+ new_child = self.class.new(title: title, lnb: lnb, parent: self, level: level)
20
+ _realign_levels(new_child)
21
+ end
22
+
23
+ def new_example(lnb:, title:)
24
+ examples << Example.new(lnb: lnb, parent: self, title: title)
25
+ examples.last
26
+ end
27
+
28
+ def new_include(lnb:)
29
+ includes << Include.new(lnb: lnb, parent: self)
30
+ includes.last
31
+ end
32
+
33
+ def parent_of_level needed_min_level
34
+ # I would love to write
35
+ # self.enum_by(:parent).find{ |ctxt| ctxt.level <= needed_min_level }
36
+ current = self
37
+ while current.level > needed_min_level
38
+ current = current.parent
39
+ end
40
+ current
41
+ end
42
+
43
+ def children
44
+ @__children__ ||= []
45
+ end
46
+
47
+ def examples
48
+ @__examples__ ||= []
49
+ end
50
+
51
+ def includes
52
+ @__includes__ ||= []
53
+ end
54
+
55
+ def map_lines(*lines, indent: 0)
56
+ prefix = " " * (tree_level + indent).succ
57
+ lines.flatten.map{ |line| "#{prefix}#{line.strip}" }
58
+ end
59
+
60
+ def to_code
61
+ [
62
+ _header,
63
+ includes.map(&:to_code),
64
+ examples.map(&:to_code),
65
+ children.map(&:to_code),
66
+ _footer
67
+ ].flatten.compact
68
+ end
69
+
70
+ def with_new_parent new_parent
71
+ @parent = new_parent
72
+ @tree_level += 1
73
+ self
74
+ end
75
+
76
+
77
+ private
78
+
79
+ def initialize(lnb: 0, title: nil, filename: nil, parent: nil, level: 0)
80
+ @filename = filename
81
+ @level = level
82
+ @lnb = lnb
83
+ @title = title
84
+ @parent = parent
85
+ if parent
86
+ _init_from_parent
87
+ else
88
+ @root = self
89
+ @tree_level = 0
90
+ end
91
+ end
92
+
93
+ def _header
94
+ if parent
95
+ map_lines(%{# #{filename}:#{lnb}}, %{context "#{title}" do}, indent: -1)
96
+ else
97
+ _root_header
98
+ end
99
+ end
100
+
101
+ def _root_header
102
+ map_lines(DISCLAIMER.gsub("FILENAME", filename.inspect).split("\n"), %{RSpec.describe #{filename.inspect} do}, indent: -1)
103
+ end
104
+
105
+ def _init_from_parent
106
+ @root = parent.root
107
+ @filename = parent.filename
108
+ @tree_level = parent.tree_level.succ
109
+ end
110
+
111
+ def _footer
112
+ map_lines("end", indent: -1)
113
+ end
114
+
115
+ def _realign_levels new_parent
116
+ if children.empty? || children.first.level == new_parent.level
117
+ children << new_parent
118
+ return new_parent
119
+ end
120
+ children.each do |child|
121
+ new_parent.children << child.with_new_parent(new_parent)
122
+ end
123
+ @__children__ = [new_parent]
124
+ new_parent
125
+ end
126
+
127
+ end
51
128
  end
52
-
53
- def set_given given
54
- @given = given
55
- self
56
- end
57
-
58
- def set_name(potential_name)
59
- @potential_name = potential_name
60
- self
61
- end
62
-
63
- def set_setup(lnb:)
64
- @setup = Setup.new(lnb: lnb, parent: self)
65
- end
66
-
67
- def to_code
68
- [
69
- _header,
70
- includes.map(&:to_code),
71
- setup&.to_code,
72
- examples.map(&:to_code),
73
- children.map(&:to_code),
74
- _footer
75
- ].flatten.compact.join("\n")
76
- end
77
-
78
- private
79
-
80
- def initialize(alternate_syntax: false, lnb:, name:, filename: nil, orig_filename: nil, parent: nil)
81
- _init_from_parent filename, orig_filename, parent
82
- @alternate_syntax = alternate_syntax
83
- @given = false
84
- @level = parent ? parent.level.succ : 1
85
- @lnb = lnb
86
- @setup = nil
87
- @name = name
88
- @parent = parent
89
- end
90
-
91
- def _header
92
- map_lines(%{context "#{name}" do}, indent: -1)
93
- end
94
-
95
- def _init_from_parent filename, orig_filename, parent
96
- _set_filename filename, orig_filename, parent
97
- @orig_filename = parent ? parent.orig_filename : orig_filename
98
- end
99
-
100
- def _set_filename filename, orig_filename, parent
101
- @filename = parent ? parent.filename : filename
102
- raise ArgumentError, "no filename given in root context" unless @filename
103
- end
104
-
105
- def _footer
106
- map_lines("end", indent: -1)
107
- end
108
-
109
129
  end
@@ -1,8 +1,7 @@
1
1
  class Speculations::Parser::Context::Example
2
2
 
3
- attr_reader :lnb, :name, :parent
3
+ attr_reader :lnb, :title, :parent
4
4
 
5
- NAMED_EXAMPLE = %r{\A[`~]{3,}ruby\s+:example\s+(.*)}
6
5
 
7
6
  def add_line line
8
7
  lines << line
@@ -15,34 +14,24 @@ class Speculations::Parser::Context::Example
15
14
 
16
15
  def to_code
17
16
  parent.map_lines(_example_head) +
18
- "\n" +
19
17
  parent.map_lines(lines, indent: 1) +
20
- "\n" +
21
18
  parent.map_lines("end")
22
19
  end
23
20
 
24
21
 
25
22
  private
26
23
 
27
- def initialize(lnb:, line:, parent:, name: nil)
24
+ def initialize(lnb:, parent:, title:)
28
25
  @lnb = lnb
29
26
  @parent = parent
30
- @name = _compute_name(lnb: lnb, line: line, parent: parent, name: name)
27
+ @title = _compute_title(title)
31
28
  end
32
29
 
33
- def _compute_name(lnb:, parent:, line:, name:)
34
- _, name1 = NAMED_EXAMPLE.match(line).to_a
35
-
36
- if name1&.empty? == false # SIC
37
- "#{name1} (#{parent.orig_filename}:#{lnb.succ})"
38
- elsif name
39
- "#{name} (#{parent.orig_filename}:#{lnb.succ})"
40
- else
41
- "Example from #{parent.orig_filename}:#{lnb.succ}"
42
- end
30
+ def _compute_title(title)
31
+ "#{title} (#{parent.root.filename}:#{lnb})"
43
32
  end
44
33
 
45
34
  def _example_head
46
- %{it "#{name}" do}
35
+ %{it "#{title}" do}
47
36
  end
48
37
  end
@@ -12,7 +12,7 @@ class Speculations::Parser::Context::Include
12
12
  end
13
13
 
14
14
  def to_code
15
- parent.map_lines(lines)
15
+ parent.map_lines("# #{parent.filename}:#{lnb}", lines)
16
16
  end
17
17
 
18
18
  private
@@ -1,59 +1,8 @@
1
1
  module Speculations::Parser::State extend self
2
- require_relative './state/bef'
3
- require_relative './state/exa'
4
- require_relative './state/inc'
2
+ require_relative './state/candidate'
3
+ require_relative './state/in'
5
4
  require_relative './state/out'
5
+ require_relative './state/triggers'
6
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
- GIVEN_RGX = %r[\A\s{0,3}(?:Given|When)\b]
12
- INCLUDE_RGX = %r[\A\s{0,3}```.*\s:include]
13
- NAME_RGX = %r[\A\s{0,3}Example:?\s+(.*)]i
14
- RUBY_RGX = %r[\A\s{0,3}```ruby]
15
- THEN_RGX = %r[\A\s{0,3}(?:Then|But|And)\s+(.*)]
16
- WS_RGX = %r[\A\s*\z]
17
-
18
- def before_match line
19
- BEFORE_RGX =~ line
20
- end
21
-
22
- def context_match line
23
- if match = CONTEXT_RGX.match(line)
24
- match.captures.first
25
- end
26
- end
27
-
28
- def eoblock_match line
29
- EOBLOCK_RGX =~ line
30
- end
31
-
32
- def example_match line
33
- EXAMPLE_RGX =~ line
34
- end
35
-
36
- def given_match line
37
- GIVEN_RGX =~ line
38
- end
39
-
40
- def include_match line
41
- INCLUDE_RGX =~ line
42
- end
43
-
44
- def potential_name line
45
- NAME_RGX.match(line)
46
- end
47
-
48
- def ruby_match line
49
- RUBY_RGX =~ line
50
- end
51
-
52
- def then_match line
53
- THEN_RGX.match(line)
54
- end
55
-
56
- def ws_match line
57
- WS_RGX =~ line
58
- end
7
+ extend Triggers
59
8
  end
@@ -0,0 +1,32 @@
1
+ module Speculations
2
+ class Parser
3
+ module State
4
+ module Candidate extend self
5
+ def parse line, lnb, node, ctxt
6
+ case
7
+ when State.blank_line(line)
8
+ [:candidate, node, ctxt]
9
+ when match = State.context_match(line)
10
+ level = match[1].size
11
+ new_parent = node.parent_of_level(level.pred)
12
+ node = new_parent.new_context(title: match[2], lnb: lnb, level: level)
13
+ [:out, node]
14
+ when match = State.maybe_example(line)
15
+ [:candidate, node, match[:title]]
16
+ when match = State.maybe_include(line)
17
+ [:candidate, node, :inc]
18
+ when match = State.ruby_code_block(line)
19
+ if ctxt == :inc
20
+ node = node.new_include(lnb: lnb)
21
+ else
22
+ node = node.new_example(title: ctxt, lnb: lnb)
23
+ end
24
+ [:in, node]
25
+ else
26
+ [:out, node]
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,14 +1,14 @@
1
1
  module Speculations
2
2
  class Parser
3
3
  module State
4
- module Inc extend self
4
+ module In extend self
5
5
 
6
- def parse line, _lnb, node
6
+ def parse line, _lnb, node, ctxt
7
7
  case
8
8
  when State.eoblock_match(line)
9
9
  [:out, node.parent]
10
10
  else
11
- [:inc, node.add_line(line)]
11
+ [:in, node.add_line(line)]
12
12
  end
13
13
  end
14
14
  end
@@ -3,71 +3,26 @@ module Speculations
3
3
  module State
4
4
  module Out extend self
5
5
 
6
- def parse line, lnb, node
7
- if node.alternate_syntax
8
- _parse_alternate line, lnb, node
6
+ def parse line, lnb, node, _ctxt
7
+ case
8
+ when match = State.context_match(line)
9
+ make_new_context(lnb: lnb, node: node, match: match)
10
+ when match = State.maybe_example(line)
11
+ [:candidate, node, match[:title]]
12
+ when match = State.maybe_include(line)
13
+ [:candidate, node, :inc]
9
14
  else
10
- _parse_classical line, lnb, node
15
+ [:out, node]
11
16
  end
12
17
  end
13
18
 
14
19
  private
15
20
 
16
- def _parse_alternate line, lnb, node
17
- case
18
- when name = State.context_match(line)
19
- node = node.parent if node.parent
20
- new_node = node.add_child(name: name, lnb: lnb)
21
- [:out, new_node.reset_context]
22
- when State.ws_match(line)
23
- [:out, node]
24
- when name = State.potential_name(line)
25
- node.set_name(name[1])
26
- node.set_given(false)
27
- [:out, node]
28
- when name = State.then_match(line)
29
- node.set_name(name[1])
30
- node.set_given(false)
31
- [:out, node]
32
- when State.given_match(line)
33
- node.set_name(nil)
34
- [:out, node.set_given(true)]
35
- when State.ruby_match(line)
36
- if node.potential_name
37
- node = node.add_example(lnb: lnb, line: line)
38
- [:exa, node]
39
- elsif node.given?
40
- node = node.add_include(lnb: lnb)
41
- [:inc, node]
42
- else
43
- [:out, node]
44
- end
45
- else
46
- [:out, node.reset_context]
47
- end
48
- end
49
-
50
- def _parse_classical line, lnb, node
51
- case
52
- when name = State.context_match(line)
53
- node = node.parent if node.parent
54
- new_node = node.add_child(name: name, lnb: lnb)
55
- [:out, new_node]
56
- when State.before_match(line)
57
- node = node.set_setup(lnb: lnb)
58
- [:bef, node]
59
- when State.example_match(line)
60
- node = node.add_example(lnb: lnb, line: line)
61
- [:exa, node]
62
- when State.include_match(line)
63
- node = node.add_include(lnb: lnb)
64
- [:inc, node]
65
- when name = State.potential_name(line)
66
- node.set_name(name[1])
67
- [:out, node]
68
- else
69
- [:out, node]
70
- end
21
+ def make_new_context(lnb:, match:, node:)
22
+ level = match[:level].size
23
+ new_parent = node.parent_of_level(level.pred)
24
+ node = new_parent.new_context(title: match[:title], lnb: lnb, level: level)
25
+ [:out, node]
71
26
  end
72
27
 
73
28
  end
@@ -0,0 +1,35 @@
1
+ module Speculations::Parser::State::Triggers
2
+
3
+ BLANK_RGX = %r{\A\s*\z}
4
+ CONTEXT_RGX = %r[\A\s{0,3}(?<level>\#{1,7})\s+Context:?\s+(?<title>.*)]
5
+ EOBLOCK_RGX = %r[\A\s{0,3}```\s*\z]
6
+ GIVEN_RGX = %r[\A\s{0,3}(?:Given|When)\b]
7
+ EXAMPLE_RGX = %r[\A\s{0,3}Example:?\s+(<?title>.*)]
8
+ RUBY_RGX = %r[\A\s{0,3}```ruby]
9
+ THEN_RGX = %r[\A\s{0,3}(?:Then|But|And|Also)\b\s*(?<title>.*)]
10
+
11
+ def blank_line line
12
+ BLANK_RGX.match(line)
13
+ end
14
+
15
+ def context_match line
16
+ CONTEXT_RGX.match(line)
17
+ end
18
+
19
+ def eoblock_match line
20
+ EOBLOCK_RGX.match(line)
21
+ end
22
+
23
+ def maybe_example line
24
+ EXAMPLE_RGX.match(line) ||
25
+ THEN_RGX.match(line)
26
+ end
27
+
28
+ def maybe_include line
29
+ GIVEN_RGX.match(line)
30
+ end
31
+
32
+ def ruby_code_block line
33
+ RUBY_RGX.match(line)
34
+ end
35
+ end
@@ -1,3 +1,3 @@
1
- module SpeculateAbout
2
- VERSION = "0.4.2"
1
+ module Speculations
2
+ VERSION = "0.5.0"
3
3
  end
metadata CHANGED
@@ -1,54 +1,69 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: speculate_about
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.2
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Dober
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-20 00:00:00.000000000 Z
11
+ date: 2021-01-01 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '13.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '13.0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rspec
15
29
  requirement: !ruby/object:Gem::Requirement
16
30
  requirements:
17
31
  - - "~>"
18
32
  - !ruby/object:Gem::Version
19
- version: '3.9'
33
+ version: '3.10'
20
34
  type: :runtime
21
35
  prerelease: false
22
36
  version_requirements: !ruby/object:Gem::Requirement
23
37
  requirements:
24
38
  - - "~>"
25
39
  - !ruby/object:Gem::Version
26
- version: '3.9'
40
+ version: '3.10'
27
41
  description: Allows Markdown or other text files to be used as literal specs, à la
28
42
  Elixr doctest, but from any file.
29
43
  email: robert.dober@gmail.com
30
- executables: []
44
+ executables:
45
+ - speculate
31
46
  extensions: []
32
47
  extra_rdoc_files: []
33
48
  files:
34
- - lib/speculate_about.rb
49
+ - bin/speculate
35
50
  - lib/speculations.rb
51
+ - lib/speculations/cli.rb
36
52
  - lib/speculations/parser.rb
37
53
  - lib/speculations/parser/context.rb
38
54
  - lib/speculations/parser/context/example.rb
39
55
  - lib/speculations/parser/context/include.rb
40
- - lib/speculations/parser/context/setup.rb
41
56
  - 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
57
+ - lib/speculations/parser/state/candidate.rb
58
+ - lib/speculations/parser/state/in.rb
45
59
  - lib/speculations/parser/state/out.rb
60
+ - lib/speculations/parser/state/triggers.rb
46
61
  - lib/speculations/version.rb
47
62
  homepage: https://github.com/robertdober/speculate
48
63
  licenses:
49
64
  - Apache-2.0
50
65
  metadata: {}
51
- post_install_message:
66
+ post_install_message:
52
67
  rdoc_options: []
53
68
  require_paths:
54
69
  - lib
@@ -63,8 +78,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
63
78
  - !ruby/object:Gem::Version
64
79
  version: '0'
65
80
  requirements: []
66
- rubygems_version: 3.1.4
67
- signing_key:
81
+ rubygems_version: 3.2.3
82
+ signing_key:
68
83
  specification_version: 4
69
84
  summary: Extract RSpecs from Markdown
70
85
  test_files: []
@@ -1,45 +0,0 @@
1
- require 'rspec'
2
-
3
- require 'speculations/parser'
4
-
5
- module SpeculateAbout
6
- def speculate_about file, alternate_syntax: false
7
- paths = _find_files(file, File.dirname( caller.first ))
8
- raise ArgumentError, "no files found for pattern #{file}" if paths.empty?
9
- paths.each do |path|
10
- code = _compile path, _readable(path), alternate_syntax: alternate_syntax
11
- ENV["SPECULATE_ABOUT_DEBUG"] ? _show(code, path) : instance_eval(code, path)
12
- end
13
- end
14
-
15
-
16
- private
17
-
18
- def _compile path, file, alternate_syntax: false
19
- ast = Speculations::Parser.new.parse_from_file(path, file, alternate_syntax: alternate_syntax)
20
- ast.to_code
21
- end
22
- def _readable(path)
23
- d = File.dirname(path)
24
- File.join(File.basename(d), File.basename(path))
25
- end
26
- def _find_files file, local_path
27
- [Dir.pwd, local_path]
28
- .flat_map do |dir|
29
- Dir.glob(File.join(dir, file))
30
- end
31
- end
32
- def _show(code, path)
33
- message = "Generated code for #{path}"
34
- _underline(message)
35
- puts code
36
- end
37
- def _underline(message, ul: "=")
38
- puts message
39
- puts message.gsub(/./, ul)
40
- end
41
- end
42
-
43
- RSpec.configure do |conf|
44
- conf.extend SpeculateAbout
45
- end
@@ -1,28 +0,0 @@
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
@@ -1,17 +0,0 @@
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
@@ -1,17 +0,0 @@
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