dokaz 0.0.3 → 0.0.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/Changelog.md +5 -0
- data/bin/dokaz +8 -4
- data/dokaz.gemspec +5 -1
- data/examples/README.md +2 -1
- data/lib/dokaz/formatters.rb +23 -8
- data/lib/dokaz/parser.rb +72 -4
- data/lib/dokaz.rb +14 -7
- metadata +56 -15
checksums.yaml
CHANGED
@@ -1,15 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
NTkxNjdlMDk0Mjk5NDZiYmU0ODBlMDE2YjUzYTJjMzliM2YyN2UyZg==
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 523736088a32b27b2e16d362c0ebe5cb704eff2153cd6b07a98565b58fd7dc01
|
4
|
+
data.tar.gz: c86ec189e60c7eaa6f485ef6b5ee807e5bc245300cff3d759bfb1b9a270b37fd
|
7
5
|
SHA512:
|
8
|
-
metadata.gz:
|
9
|
-
|
10
|
-
YWEyNDk0OTZkOGVmYjg5ZDcwNWIxYTc5OGM2ODMzNzRmNTljZTY3Y2VkOGM1
|
11
|
-
ZmEzZWM5Zjg1NjQ2ZmU2MWZhZTQwYWZjMjE1MDFiNmYzOGY5OGU=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
Y2JmMGY4ODljN2QxZjE0ODA4ZDYwN2Y4Mjg0MmM1M2EwYWNkYjM1YTBjYzIw
|
14
|
-
YTU2MTYwMjllOTc1NzgyMzUyODQxZmIyN2Y1NWQyMWNlM2E3ZWFmZmFlNThk
|
15
|
-
OTcyMjVjMjE5ZmI3ZWUwYTZhZGM5MjE4MTEwYTM0MTA0OGU1ZGY=
|
6
|
+
metadata.gz: 31c454210e0d3f8dc5ed652975fe24408c76c2004e743acea40b15b9914b102f759d7e569ce3f0b219e876a7ec07e7773e8ecc79f36abb18e69ca7c0aa925111
|
7
|
+
data.tar.gz: b798d663c0acf6c223a90f638c28dec10b52fb2b0b4112bf95a43371cb1bff2b81cf30ea50d2069d01b0665ddb70646660faa3c36466877ebeca08693aa48e15
|
data/Changelog.md
ADDED
data/bin/dokaz
CHANGED
@@ -5,13 +5,10 @@ require 'slop'
|
|
5
5
|
$:.unshift 'lib'
|
6
6
|
require 'dokaz'
|
7
7
|
|
8
|
-
if File.
|
8
|
+
if File.exist?('.dokaz')
|
9
9
|
ARGV.push(*File.read('.dokaz').split(/\s+/m))
|
10
10
|
end
|
11
11
|
|
12
|
-
patterns = []
|
13
|
-
patterns << ARGV.shift until ARGV.empty? || ARGV.first =~ /^-/
|
14
|
-
|
15
12
|
HELP = %Q{
|
16
13
|
Usage: dokaz [patterns] [options]
|
17
14
|
|
@@ -26,12 +23,19 @@ Options:
|
|
26
23
|
--help Shows this message
|
27
24
|
}
|
28
25
|
|
26
|
+
patterns = []
|
27
|
+
patterns << ARGV.shift until ARGV.empty? || ARGV.first =~ /^-/
|
28
|
+
|
29
29
|
options = Slop.parse do
|
30
30
|
on :r, :require=, "Additional files to require, comma-separated"
|
31
31
|
on :f, :format=, "Output format (spec or show[case])"
|
32
32
|
on :help
|
33
33
|
end
|
34
34
|
|
35
|
+
if patterns.empty?
|
36
|
+
patterns.concat(Dir['README.{md,markdown}'])
|
37
|
+
end
|
38
|
+
|
35
39
|
if patterns.empty? || options.help?
|
36
40
|
puts HELP
|
37
41
|
exit
|
data/dokaz.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = 'dokaz'
|
3
|
-
s.version = '0.0.
|
3
|
+
s.version = '0.0.5'
|
4
4
|
s.authors = ['Victor Shepelev']
|
5
5
|
s.email = 'zverok.offline@gmail.com'
|
6
6
|
s.homepage = 'https://github.com/zverok/dokaz'
|
@@ -24,7 +24,11 @@ Gem::Specification.new do |s|
|
|
24
24
|
|
25
25
|
s.add_dependency 'ansi'
|
26
26
|
s.add_dependency 'slop', '~> 3'
|
27
|
+
s.add_dependency 'rouge', '~> 4'
|
27
28
|
|
28
29
|
s.add_development_dependency 'rspec', '~> 3'
|
29
30
|
s.add_development_dependency 'rspec-its', '~> 1'
|
31
|
+
|
32
|
+
s.add_development_dependency 'rake'
|
33
|
+
s.add_development_dependency 'rubygems-tasks'
|
30
34
|
end
|
data/examples/README.md
CHANGED
@@ -51,13 +51,14 @@ puts a.sum(10)
|
|
51
51
|
This code throws an error:
|
52
52
|
|
53
53
|
```ruby
|
54
|
+
# It's a comment, but not seen while evaluating
|
54
55
|
100 / 0
|
55
56
|
```
|
56
57
|
|
57
58
|
This code requires additional require:
|
58
59
|
|
59
60
|
```ruby
|
60
|
-
BigDecimal.new(
|
61
|
+
BigDecimal.new('0')
|
61
62
|
```
|
62
63
|
|
63
64
|
Try to run it with option `--require cgi`.
|
data/lib/dokaz/formatters.rb
CHANGED
@@ -1,8 +1,14 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'ansi/code'
|
3
|
+
require 'rouge'
|
3
4
|
|
4
5
|
class Dokaz
|
5
6
|
class Formatter
|
7
|
+
def initialize
|
8
|
+
@rouge = Rouge::Formatters::Terminal256.new(Rouge::Themes::Molokai.new)
|
9
|
+
@lexer = Rouge::Lexers::Ruby.new
|
10
|
+
end
|
11
|
+
|
6
12
|
def start_block(block)
|
7
13
|
end
|
8
14
|
|
@@ -35,6 +41,14 @@ class Dokaz
|
|
35
41
|
color(:dark, str)
|
36
42
|
end
|
37
43
|
|
44
|
+
def code(str)
|
45
|
+
if STDOUT.tty?
|
46
|
+
@rouge.format(@lexer.lex(str))
|
47
|
+
else
|
48
|
+
code
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
38
52
|
def color(code, str)
|
39
53
|
if STDOUT.tty?
|
40
54
|
ANSI.send(code){str}
|
@@ -46,6 +60,7 @@ class Dokaz
|
|
46
60
|
|
47
61
|
class SpecFormatter < Formatter
|
48
62
|
def initialize
|
63
|
+
super
|
49
64
|
@ok = 0
|
50
65
|
@errors = []
|
51
66
|
end
|
@@ -65,14 +80,14 @@ class Dokaz
|
|
65
80
|
puts
|
66
81
|
puts
|
67
82
|
|
68
|
-
@errors.each do |
|
69
|
-
puts code
|
83
|
+
@errors.each do |src, e|
|
84
|
+
puts code(src)
|
70
85
|
puts( error("#{e.message} (#{e.class})\n " + filter_backtrace(e.backtrace).join("\n ") + "\n"))
|
71
86
|
end
|
72
87
|
|
73
88
|
puts
|
74
89
|
@errors.each do |code, e|
|
75
|
-
ln = e.backtrace.
|
90
|
+
ln = filter_backtrace(e.backtrace).last.to_s.sub(/:in .*$/, '')
|
76
91
|
puts error("dokaz #{ln}") + comment(" # #{e.message} (#{e.class})")
|
77
92
|
end
|
78
93
|
|
@@ -97,16 +112,16 @@ class Dokaz
|
|
97
112
|
puts "#{block.caption}\n#{'=' * block.caption.size}\n\n"
|
98
113
|
end
|
99
114
|
|
100
|
-
def output(
|
101
|
-
puts code
|
115
|
+
def output(src, res, out)
|
116
|
+
puts code(src)
|
102
117
|
unless out.empty?
|
103
|
-
puts comment("# Prints: \n# " + out.split("\n").join("\n#
|
118
|
+
puts comment("# Prints: \n# " + out.split("\n").join("\n# "))
|
104
119
|
end
|
105
120
|
puts comment("# => " + res.inspect.split("\n").join("\n# "))
|
106
121
|
end
|
107
122
|
|
108
|
-
def output_err(
|
109
|
-
puts code
|
123
|
+
def output_err(src, e)
|
124
|
+
puts code(src)
|
110
125
|
puts comment("# Throws: #{e.message} (#{e.class})")
|
111
126
|
end
|
112
127
|
|
data/lib/dokaz/parser.rb
CHANGED
@@ -1,11 +1,48 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
require 'strscan'
|
3
|
+
require 'stringio'
|
3
4
|
|
4
5
|
class Dokaz
|
6
|
+
class Statement
|
7
|
+
attr_reader :code, :line, :block
|
8
|
+
attr_reader :pre_comment
|
9
|
+
attr_accessor :comment
|
10
|
+
|
11
|
+
def initialize(code, line, block)
|
12
|
+
@raw_code, @line, @block = code, line, block
|
13
|
+
@comment = ''
|
14
|
+
|
15
|
+
parse!
|
16
|
+
end
|
17
|
+
|
18
|
+
def empty?
|
19
|
+
code.empty?
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def parse!
|
25
|
+
pre_lines = []
|
26
|
+
lines = @raw_code.split("\n")
|
27
|
+
while !lines.empty? && (lines.first =~ /^\s*\#/ || lines.first.strip.empty?)
|
28
|
+
pre_lines << lines.shift
|
29
|
+
@line += 1
|
30
|
+
end
|
31
|
+
@code = lines.join("\n")
|
32
|
+
@pre_comment = pre_lines.reject{|ln| ln.strip.empty?}.join("\n")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
5
36
|
class Block
|
37
|
+
FakeIRBContext = Struct.new(:local_variables)
|
38
|
+
|
6
39
|
attr_reader :code, :line, :file
|
40
|
+
attr_reader :statements
|
41
|
+
|
7
42
|
def initialize(code, line, file)
|
8
43
|
@code, @line, @file = code, line, file
|
44
|
+
|
45
|
+
parse!
|
9
46
|
end
|
10
47
|
|
11
48
|
def end_line
|
@@ -19,6 +56,35 @@ class Dokaz
|
|
19
56
|
def inside?(ln)
|
20
57
|
(line..end_line).cover?(ln)
|
21
58
|
end
|
59
|
+
|
60
|
+
private
|
61
|
+
|
62
|
+
def parse!
|
63
|
+
@statements = []
|
64
|
+
lex = RubyLex.new
|
65
|
+
|
66
|
+
# We do unholy things by reusing IRB's internal helper classes.
|
67
|
+
# In 3.2 (ver.1.6), IRB changed them to be more complicated. I only had
|
68
|
+
# time to quick-hack it around with fake copies of object IRB use.
|
69
|
+
# It might fail anytime, but works on test examples :shrug:
|
70
|
+
if IRB::VERSION >= '1.6'
|
71
|
+
ctx = FakeIRBContext.new(local_variables: [])
|
72
|
+
lex.set_input(SpecIO.new(code), context: ctx)
|
73
|
+
lex.each_top_level_statement(ctx) do |st, line_no|
|
74
|
+
@statements << Statement.new(st, line + line_no, self)
|
75
|
+
end
|
76
|
+
else
|
77
|
+
lex.set_input(SpecIO.new(code))
|
78
|
+
lex.each_top_level_statement do |st, line_no|
|
79
|
+
@statements << Statement.new(st, line + line_no, self)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
@statements.each_cons(2){|s1, s2|
|
84
|
+
s1.comment = s2.pre_comment
|
85
|
+
}
|
86
|
+
@statements.reject!(&:empty?)
|
87
|
+
end
|
22
88
|
end
|
23
89
|
|
24
90
|
class Parser
|
@@ -27,9 +93,11 @@ class Dokaz
|
|
27
93
|
end
|
28
94
|
|
29
95
|
def parse
|
30
|
-
|
96
|
+
blocks = []
|
31
97
|
ln = 0
|
98
|
+
|
32
99
|
scanner = StringScanner.new(File.read(@path))
|
100
|
+
|
33
101
|
loop do
|
34
102
|
text = scanner.scan_until(/\n```ruby\n/)
|
35
103
|
break unless text
|
@@ -37,14 +105,14 @@ class Dokaz
|
|
37
105
|
ln += text.count("\n")
|
38
106
|
code = scanner.scan_until(/\n```\n/)
|
39
107
|
if !code
|
40
|
-
|
108
|
+
blocks << Block.new(scanner.rest, ln, @path)
|
41
109
|
break
|
42
110
|
end
|
43
|
-
|
111
|
+
blocks << Block.new(code.sub(/```\Z/, ''), ln, @path)
|
44
112
|
ln += code.count("\n")
|
45
113
|
end
|
46
114
|
|
47
|
-
|
115
|
+
blocks
|
48
116
|
end
|
49
117
|
end
|
50
118
|
end
|
data/lib/dokaz.rb
CHANGED
@@ -11,6 +11,13 @@ class Dokaz
|
|
11
11
|
'show' => ShowcaseFormatter,
|
12
12
|
'showcase' => ShowcaseFormatter
|
13
13
|
}
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def before(&block)
|
17
|
+
return @before || ->(){} unless block
|
18
|
+
@before = block
|
19
|
+
end
|
20
|
+
end
|
14
21
|
|
15
22
|
def initialize(patterns, options = {})
|
16
23
|
@blocks = select_blocks(patterns)
|
@@ -23,6 +30,8 @@ class Dokaz
|
|
23
30
|
end
|
24
31
|
|
25
32
|
def run
|
33
|
+
self.class.before.call
|
34
|
+
|
26
35
|
puts "Running #{@blocks.count} blocks from #{@blocks.map(&:file).uniq.count} files\n\n"
|
27
36
|
|
28
37
|
@blocks.each do |b|
|
@@ -36,17 +45,15 @@ class Dokaz
|
|
36
45
|
private
|
37
46
|
|
38
47
|
def process_block(block)
|
39
|
-
|
40
|
-
lex.set_input(SpecIO.new(block.code))
|
41
|
-
lex.each_top_level_statement do |line, line_no|
|
48
|
+
block.statements.each do |st|
|
42
49
|
begin
|
43
50
|
res = nil
|
44
51
|
out = capture_output{
|
45
|
-
res = eval(
|
52
|
+
res = eval(st.code, TOPLEVEL_BINDING, block.file, st.line)
|
46
53
|
}
|
47
|
-
@formatter.output(
|
48
|
-
rescue => e
|
49
|
-
@formatter.output_err(
|
54
|
+
@formatter.output(st.code, res, out)
|
55
|
+
rescue Exception => e
|
56
|
+
@formatter.output_err(st.code, e)
|
50
57
|
break
|
51
58
|
end
|
52
59
|
end
|
metadata
CHANGED
@@ -1,71 +1,113 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dokaz
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Victor Shepelev
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-02-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ansi
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: slop
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - ~>
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '3'
|
34
34
|
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - ~>
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rouge
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '4'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '4'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: rspec
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
44
58
|
requirements:
|
45
|
-
- - ~>
|
59
|
+
- - "~>"
|
46
60
|
- !ruby/object:Gem::Version
|
47
61
|
version: '3'
|
48
62
|
type: :development
|
49
63
|
prerelease: false
|
50
64
|
version_requirements: !ruby/object:Gem::Requirement
|
51
65
|
requirements:
|
52
|
-
- - ~>
|
66
|
+
- - "~>"
|
53
67
|
- !ruby/object:Gem::Version
|
54
68
|
version: '3'
|
55
69
|
- !ruby/object:Gem::Dependency
|
56
70
|
name: rspec-its
|
57
71
|
requirement: !ruby/object:Gem::Requirement
|
58
72
|
requirements:
|
59
|
-
- - ~>
|
73
|
+
- - "~>"
|
60
74
|
- !ruby/object:Gem::Version
|
61
75
|
version: '1'
|
62
76
|
type: :development
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
|
-
- - ~>
|
80
|
+
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
82
|
version: '1'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: rubygems-tasks
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ">="
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
69
111
|
description:
|
70
112
|
email: zverok.offline@gmail.com
|
71
113
|
executables:
|
@@ -73,6 +115,7 @@ executables:
|
|
73
115
|
extensions: []
|
74
116
|
extra_rdoc_files: []
|
75
117
|
files:
|
118
|
+
- Changelog.md
|
76
119
|
- LICENSE.txt
|
77
120
|
- README.md
|
78
121
|
- bin/dokaz
|
@@ -94,19 +137,17 @@ require_paths:
|
|
94
137
|
- lib
|
95
138
|
required_ruby_version: !ruby/object:Gem::Requirement
|
96
139
|
requirements:
|
97
|
-
- -
|
140
|
+
- - ">="
|
98
141
|
- !ruby/object:Gem::Version
|
99
142
|
version: '0'
|
100
143
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
144
|
requirements:
|
102
|
-
- -
|
145
|
+
- - ">="
|
103
146
|
- !ruby/object:Gem::Version
|
104
147
|
version: '0'
|
105
148
|
requirements: []
|
106
|
-
|
107
|
-
rubygems_version: 2.4.6
|
149
|
+
rubygems_version: 3.1.6
|
108
150
|
signing_key:
|
109
151
|
specification_version: 4
|
110
152
|
summary: Use your documentation as a specification
|
111
153
|
test_files: []
|
112
|
-
has_rdoc:
|