speculate_about 0.4.2 → 0.6.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: ddf1c9fa5d8bb2d18f142e4fcc18aad6ca71d0694829b7bd0f7d291b0e3c97d2
4
+ data.tar.gz: '06689091cee69a4f3df817efc5ba8be2ed754d85fe98ed5cb9d27c28b58be506'
5
5
  SHA512:
6
- metadata.gz: 222b7eff1615cae7c4e3af5fbaffae7812e9e1144de7884b5a3b7d798e7647f9ea259dddf632e5262977d6a07da1234f22457c84eb656c225ba7745b1a317f49
7
- data.tar.gz: 70c8e9c20f41acc002bfdda9ae3fa6f63561538730989c63e25e0460a65785b21d5835c9f16efc4f41ccaf22a51b179a27bc77c42a7585afa0474d2e95a9dcb8
6
+ metadata.gz: c382d674930d2e61a8494de4331a4b4f3afef2cc6b77193fbb09af4610f3c8944f65edb09ff5ff550dfea5f56114692c1d93f37f04a88d30e91d014deba7b7f0
7
+ data.tar.gz: b29b76a402e49063919eeded1724c7ea2d87c557fba1ea6f4e526d9e9a322da746669d460d3effbc4146090f57559a4c77523464e9cc44f324172c0363e783f1
data/bin/speculate ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative '../lib/speculations/cli'
3
+
4
+ Speculations::CLI.run(ARGV)
@@ -1,34 +1,15 @@
1
1
  require 'rspec'
2
-
3
- require 'speculations/parser'
4
-
2
+ require_relative 'speculations/parser'
5
3
  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
4
 
5
+ def speculate_about(infile)
6
+ raise ArgumentError, "#{infile} not found" unless File.readable? infile
7
+ ast = Speculations::Parser.new.parse_from_file(infile)
8
+ code = ast.to_code.join("\n")
9
+ ENV["SPECULATE_ABOUT_DEBUG"] ? _show(code, infile) : instance_eval(code, infile)
10
+ end
15
11
 
16
12
  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
13
  def _show(code, path)
33
14
  message = "Generated code for #{path}"
34
15
  _underline(message)
@@ -38,6 +19,7 @@ module SpeculateAbout
38
19
  puts message
39
20
  puts message.gsub(/./, ul)
40
21
  end
22
+
41
23
  end
42
24
 
43
25
  RSpec.configure do |conf|
@@ -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,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,109 +1,120 @@
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
+ attr_reader :filename, :level, :lnb, :title, :parent, :root, :tree_level
8
+
9
+ def new_context(title:, lnb:, level: )
10
+ new_child = self.class.new(title: title, lnb: lnb, parent: self, level: level)
11
+ _realign_levels(new_child)
12
+ end
13
+
14
+ def new_example(lnb:, title:)
15
+ examples << Example.new(lnb: lnb, parent: self, title: title)
16
+ examples.last
17
+ end
18
+
19
+ def new_include(lnb:)
20
+ includes << Include.new(lnb: lnb, parent: self)
21
+ includes.last
22
+ end
23
+
24
+ def parent_of_level needed_min_level
25
+ # I would love to write
26
+ # self.enum_by(:parent).find{ |ctxt| ctxt.level <= needed_min_level }
27
+ current = self
28
+ while current.level > needed_min_level
29
+ current = current.parent
30
+ end
31
+ current
32
+ end
33
+
34
+ def children
35
+ @__children__ ||= []
36
+ end
37
+
38
+ def examples
39
+ @__examples__ ||= []
40
+ end
41
+
42
+ def includes
43
+ @__includes__ ||= []
44
+ end
45
+
46
+ def map_lines(*lines, indent: 0)
47
+ prefix = " " * (tree_level + indent).succ
48
+ lines.flatten.map{ |line| "#{prefix}#{line.strip}" }
49
+ end
50
+
51
+ def to_code
52
+ [
53
+ _header,
54
+ includes.map(&:to_code),
55
+ examples.map(&:to_code),
56
+ children.map(&:to_code),
57
+ _footer
58
+ ].flatten.compact
59
+ end
60
+
61
+ def with_new_parent new_parent
62
+ @parent = new_parent
63
+ @tree_level += 1
64
+ self
65
+ end
66
+
67
+
68
+ private
69
+
70
+ def initialize(lnb: 0, title: nil, filename: nil, parent: nil, level: 0)
71
+ @filename = filename
72
+ @level = level
73
+ @lnb = lnb
74
+ @title = title
75
+ @parent = parent
76
+ if parent
77
+ _init_from_parent
78
+ else
79
+ @root = self
80
+ @tree_level = 0
81
+ end
82
+ end
83
+
84
+ def _header
85
+ if parent
86
+ map_lines(%{# #{filename}:#{lnb}}, %{context "#{title}" do}, indent: -1)
87
+ else
88
+ []
89
+ end
90
+ end
91
+
92
+ def _init_from_parent
93
+ @root = parent.root
94
+ @filename = parent.filename
95
+ @tree_level = parent.tree_level.succ
96
+ end
97
+
98
+ def _footer
99
+ if parent
100
+ map_lines("end", indent: -1)
101
+ else
102
+ []
103
+ end
104
+ end
105
+
106
+ def _realign_levels new_parent
107
+ if children.empty? || children.first.level == new_parent.level
108
+ children << new_parent
109
+ return new_parent
110
+ end
111
+ children.each do |child|
112
+ new_parent.children << child.with_new_parent(new_parent)
113
+ end
114
+ @__children__ = [new_parent]
115
+ new_parent
116
+ end
117
+
118
+ end
51
119
  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
120
  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 Bef 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
- [:bef, 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,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
@@ -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,3 +1,3 @@
1
- module SpeculateAbout
2
- VERSION = "0.4.2"
1
+ module Speculations
2
+ VERSION = "0.6.0"
3
3
  end
data/lib/speculations.rb CHANGED
@@ -1,2 +1,44 @@
1
- module Speculations
1
+ require 'fileutils'
2
+ require_relative 'speculations/parser'
3
+ module Speculations extend self
4
+
5
+ DISCLAIMER = <<-EOD
6
+ # DO NOT EDIT!!!
7
+ # This file was generated from FILENAME with the speculate_about gem, if you modify this file
8
+ # one of two bad things will happen
9
+ # - your documentation specs are not correct
10
+ # - your modifications will be overwritten by the speculate rake task
11
+ # YOU HAVE BEEN WARNED
12
+ EOD
13
+
14
+ def compile(infile, outfile=nil)
15
+ raise ArgumentError, "#{infile} not found" unless File.readable? infile
16
+ outfile ||= _speculation_path(infile)
17
+ if _out_of_date?(outfile, infile)
18
+ ast = Speculations::Parser.new.parse_from_file(infile)
19
+ code = _decorated_ast_code ast, infile
20
+ File.write(outfile, code.join("\n"))
21
+ end
22
+ outfile
23
+ end
24
+
25
+ private
26
+
27
+ def _decorated_ast_code ast, filename
28
+ [DISCLAIMER.gsub("FILENAME", filename.inspect).split("\n"), %{RSpec.describe #{filename.inspect} do}] +
29
+ ast.to_code + ["end"]
30
+ end
31
+
32
+ def _out_of_date?(outf, inf)
33
+ return true unless File.exists? outf
34
+ return File.lstat(outf).mtime <= File.lstat(inf).mtime
35
+ end
36
+
37
+ def _speculation_path(file)
38
+ dir = File.dirname(file)
39
+ dest_dir = File.join("spec", "speculations", dir)
40
+ FileUtils.mkdir_p(dest_dir) unless File.directory?(dest_dir)
41
+ rspec = File.basename(file, ".md")
42
+ File.join(dest_dir, "#{rspec}_spec.rb")
43
+ end
2
44
  end
metadata CHANGED
@@ -1,48 +1,36 @@
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.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Dober
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-20 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'
11
+ date: 2022-01-21 00:00:00.000000000 Z
12
+ dependencies: []
27
13
  description: Allows Markdown or other text files to be used as literal specs, à la
28
14
  Elixr doctest, but from any file.
29
15
  email: robert.dober@gmail.com
30
- executables: []
16
+ executables:
17
+ - speculate
31
18
  extensions: []
32
19
  extra_rdoc_files: []
33
20
  files:
21
+ - bin/speculate
34
22
  - lib/speculate_about.rb
35
23
  - lib/speculations.rb
24
+ - lib/speculations/cli.rb
36
25
  - lib/speculations/parser.rb
37
26
  - lib/speculations/parser/context.rb
38
27
  - lib/speculations/parser/context/example.rb
39
28
  - lib/speculations/parser/context/include.rb
40
- - lib/speculations/parser/context/setup.rb
41
29
  - 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
30
+ - lib/speculations/parser/state/candidate.rb
31
+ - lib/speculations/parser/state/in.rb
45
32
  - lib/speculations/parser/state/out.rb
33
+ - lib/speculations/parser/state/triggers.rb
46
34
  - lib/speculations/version.rb
47
35
  homepage: https://github.com/robertdober/speculate
48
36
  licenses:
@@ -56,14 +44,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
56
44
  requirements:
57
45
  - - ">="
58
46
  - !ruby/object:Gem::Version
59
- version: 2.7.0
47
+ version: 3.1.0
60
48
  required_rubygems_version: !ruby/object:Gem::Requirement
61
49
  requirements:
62
50
  - - ">="
63
51
  - !ruby/object:Gem::Version
64
52
  version: '0'
65
53
  requirements: []
66
- rubygems_version: 3.1.4
54
+ rubygems_version: 3.3.3
67
55
  signing_key:
68
56
  specification_version: 4
69
57
  summary: Extract RSpecs from Markdown
@@ -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 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
@@ -1,17 +0,0 @@
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