dokaz 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ M2I3NzJkY2RlNjVjNjVmYzI1YmRiNmU0MmU0MjZiZmJlZDI3YzdkYw==
5
+ data.tar.gz: !binary |-
6
+ ODljNjkzMjE2MjI1NTlkZmQ2Yzk5ZGJmMzJmYzE3NDU1MTBkMDljYw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ NzFkMWI0NDMxZDlhYjYwYzg4MjZmOWFmYjEzNTNiNGI0OTQyMzM4ZTYxMzg2
10
+ ZmQzZTFiZGU2YTZjNzhmYjIzOTQyYjllNjQyNDYxYTdhYmMyNzAzMWNiODUw
11
+ N2Q3NmNhZDgxZGM3YjQ3MDkzMTM0MTNjY2VkZmQ4YmMzNmQzODM=
12
+ data.tar.gz: !binary |-
13
+ ZTU4ZjRmMzAyNjA2MTlkNzI3NTYyMGY5MWJkYTdjMjMwZWMzYjJhNDk5OTRk
14
+ ZWY3MDI0NTQxYmY3NzZjMTA2N2MyNjFlODU4MTAyMDhmYjQwZmQxZjhkMjJj
15
+ YmQ2OTYyNmJhY2VmYmEyYjVlMzY3OTRkMGVkMzVhMGE5NWUwYjk=
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014-15 Victor 'Zverok' Shepelev
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,82 @@
1
+ # Dokaz
2
+
3
+ Dokaz (До́каз) means "proof" in Ukrainian.
4
+
5
+ **Dokaz** gem allows you to check all code inside your documentation, and
6
+ see what it outputs.
7
+
8
+ It just finds all pieces of <pre>```ruby</pre>-marked code in your .md
9
+ files, tries to run it and shows you the consequences.
10
+
11
+ ## Install
12
+
13
+ Just as usual -- by adding `gem 'dokaz'` to your Gemfile then running
14
+ `bundle install`.
15
+
16
+ Or `[sudo] gem install dokaz`
17
+
18
+ ## Usage
19
+
20
+ ```
21
+ dokaz --help
22
+
23
+ Usage: dokaz [patterns] [options]
24
+
25
+ Patterns:
26
+ File.md one file
27
+ File.md:15 only block around line 15
28
+ folder/*.md all files in folder
29
+
30
+ Options:
31
+ -r, --require Additional files to require, comma-separated
32
+ -f, --format Output format ("spec" or "show[case]")
33
+ --help Shows this message
34
+ ```
35
+
36
+ Dokaz respects `.dokaz` file in local folder, which should be just its
37
+ options, each on newline.
38
+
39
+ ## Output formats
40
+
41
+ * "spec" -- simple "does it work?" check:
42
+
43
+ <img src="https://raw.github.com/zverok/dokaz/master/screenshots/spec.png" alt="--format spec"/>
44
+
45
+ * "showcase" or just "show" -- pretty copy-pasteable output of what
46
+ sample code returns and prints:
47
+
48
+ <img src="https://raw.github.com/zverok/dokaz/master/screenshots/showcase.png" alt="--format showcase"/>
49
+
50
+ You can just insert it back into your docs.
51
+
52
+ ## Using it for your project's README
53
+
54
+ * Add to project's Gemfile (preferably to `:dev` group) `gem 'dokaz'`
55
+ * Create file for example `spec/dokaz_helpers.rb`, containing all
56
+ initialization code to include your project files
57
+ * Create file `.dokaz`, containing:
58
+ ```
59
+ README.md
60
+ --require ./spec/dokaz_helpers.rb
61
+ ```
62
+ * Run `bundle exec dokaz`
63
+
64
+ ## Using it for your project's GitHub wiki
65
+
66
+ * Do the same as above
67
+ * Clone your project's wiki to some directory (outside your project's directory!):
68
+ `git clone git:github.com/myusername/myproject.wiki.git`
69
+ * At your project's directory, run `bundle exec dokaz /path/to/cloned/wiki/*.md`
70
+
71
+ ## Known issues
72
+
73
+ * ridiculously simple, naïve and not tested; though, works for me;
74
+ * all code from all blocks is evaluated in same context (so, if block
75
+ from one documentation file defines some class or variable, it is
76
+ visible to all other blocks). Typically, it is reasonable behaviour,
77
+ yet can produce unwanted effects when your docs demonstrate some
78
+ metaprogramming or do some serious side-effects.
79
+
80
+ ## License
81
+
82
+ MIT
data/bin/dokaz ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+ # encoding: utf-8
3
+ require 'slop'
4
+
5
+ $:.unshift 'lib'
6
+ require 'dokaz'
7
+
8
+ if File.exists?('.dokaz')
9
+ ARGV.push(*File.read('.dokaz').split("\n"))
10
+ end
11
+
12
+ patterns = []
13
+ patterns << ARGV.shift until ARGV.empty? || ARGV.first =~ /^-/
14
+
15
+ HELP = %Q{
16
+ Usage: dokaz [patterns] [options]
17
+
18
+ Patterns:
19
+ File.md one file
20
+ File.md:15 only block around line 15
21
+ folder/*.md all files in folder
22
+
23
+ Options:
24
+ -r, --require Additional files to require, comma-separated
25
+ -f, --format Output format ("spec" or "show[case]")
26
+ --help Shows this message
27
+ }
28
+
29
+ options = Slop.parse do
30
+ on :r, :require=, "Additional files to require, comma-separated"
31
+ on :f, :format=, "Output format (spec or show[case])"
32
+ on :help
33
+ end
34
+
35
+ if patterns.empty? || options.help?
36
+ puts HELP
37
+ exit
38
+ end
39
+
40
+ Dokaz.new(patterns, options.to_hash).run
data/dokaz.gemspec ADDED
@@ -0,0 +1,30 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'dokaz'
3
+ s.version = '0.0.1'
4
+ s.authors = ['Victor Shepelev']
5
+ s.email = 'zverok.offline@gmail.com'
6
+ s.homepage = 'https://github.com/zverok/dokaz'
7
+
8
+ s.summary = 'Use your documentation as a specification'
9
+ s.licenses = ['MIT']
10
+
11
+ s.files = `git ls-files`.split($RS).reject do |file|
12
+ file =~ /^(?:
13
+ spec\/.*
14
+ |Gemfile
15
+ |Rakefile
16
+ |\.rspec
17
+ |\.gitignore
18
+ |\.rubocop.yml
19
+ |\.travis.yml
20
+ )$/x
21
+ end
22
+ s.require_paths = ["lib"]
23
+ s.executables << 'dokaz'
24
+
25
+ s.add_dependency 'ansi'
26
+ s.add_dependency 'slop', '~> 3'
27
+
28
+ s.add_development_dependency 'rspec', '~> 3'
29
+ s.add_development_dependency 'rspec-its', '~> 1'
30
+ end
@@ -0,0 +1,63 @@
1
+ This file shows you how DocSpec work.
2
+
3
+ Try this
4
+ * `docspec examples/example.md`
5
+ * `docspec examples/example.md --format showcase`
6
+
7
+ ## Example 1
8
+
9
+ So, lets begin with simple ruby code:
10
+
11
+ ```ruby
12
+ 1 + 18
13
+ ```
14
+
15
+ In default (check) mode, this first gives just green dot (it works!).
16
+ In showcase mode you'll see what this code outputs; which allows you to
17
+ paste it into your docs.
18
+
19
+ ## Example 2
20
+
21
+ This is more complex code, try it:
22
+
23
+ ```ruby
24
+ class A
25
+ def initialize(i)
26
+ @i = i
27
+ end
28
+
29
+ def sum(other)
30
+ @i + other
31
+ end
32
+ end
33
+
34
+ a = A.new(5)
35
+ a.sum(3)
36
+ ```
37
+
38
+ The code is evaluated in continuous manner, so, the `A` class and `a`
39
+ variable are already defined:
40
+
41
+ ```ruby
42
+ a.sum(8)
43
+ ```
44
+
45
+ This code not only returns values, but also outputs something to STDOUT:
46
+
47
+ ```ruby
48
+ puts a.sum(10)
49
+ ```
50
+
51
+ This code throws an error:
52
+
53
+ ```ruby
54
+ 100 / 0
55
+ ```
56
+
57
+ This code requires additional require:
58
+
59
+ ```ruby
60
+ BigDecimal.new("0")
61
+ ```
62
+
63
+ Try to run it with option `--require cgi`.
data/lib/dokaz.rb ADDED
@@ -0,0 +1,70 @@
1
+ # encoding: utf-8
2
+ require 'irb'
3
+
4
+ require_relative 'dokaz/parser'
5
+ require_relative 'dokaz/io_helpers'
6
+ require_relative 'dokaz/formatters'
7
+
8
+ class Dokaz
9
+ FORMATTERS = {
10
+ 'spec' => SpecFormatter,
11
+ 'show' => ShowcaseFormatter,
12
+ 'showcase' => ShowcaseFormatter
13
+ }
14
+
15
+ def initialize(patterns, options = {})
16
+ @blocks = select_blocks(patterns)
17
+
18
+ if options[:require]
19
+ options[:require].split(',').each{|f| require f}
20
+ end
21
+
22
+ @formatter = FORMATTERS[options[:format] || 'spec'].new
23
+ end
24
+
25
+ def run
26
+ puts "Running #{@blocks.count} blocks from #{@blocks.map(&:file).uniq.count} files\n\n"
27
+
28
+ @blocks.each do |b|
29
+ @formatter.start_block(b)
30
+ process_block(b)
31
+ @formatter.finish_block(b)
32
+ end
33
+ @formatter.finalize
34
+ end
35
+
36
+ private
37
+
38
+ def process_block(block)
39
+ lex = RubyLex.new
40
+ lex.set_input(SpecIO.new(block.code))
41
+ lex.each_top_level_statement do |line, line_no|
42
+ begin
43
+ res = nil
44
+ out = capture_output{
45
+ res = eval(line, TOPLEVEL_BINDING, block.file, block.line + line_no)
46
+ }
47
+ @formatter.output(line, res, out)
48
+ rescue => e
49
+ @formatter.output_err(line, e)
50
+ break
51
+ end
52
+ end
53
+ end
54
+
55
+ def capture_output(&block)
56
+ Out.new.capture(&block)
57
+ end
58
+
59
+ def select_blocks(patterns)
60
+ patterns.map{|pat|
61
+ path, ln = pat.split(':', 2)
62
+ files = Dir[path]
63
+ blocks = files.map{|f|
64
+ Parser.new(f).parse
65
+ }.flatten.select{|b|
66
+ ln ? b.inside?(ln.to_i) : true
67
+ }
68
+ }.flatten
69
+ end
70
+ end
@@ -0,0 +1,102 @@
1
+ # encoding: utf-8
2
+ require 'ansi/code'
3
+
4
+ class Dokaz
5
+ class Formatter
6
+ def start_block(block)
7
+ end
8
+
9
+ def finish_block(block)
10
+ end
11
+
12
+ def finalize
13
+ end
14
+
15
+ def output(code, res, out)
16
+ end
17
+
18
+ def output_err(code, e)
19
+ end
20
+
21
+ protected
22
+ def filter_backtrace(trace)
23
+ trace.take_while{|ln| ln !~ /lib\/dokaz/}
24
+ end
25
+ end
26
+
27
+ class SpecFormatter < Formatter
28
+ def initialize
29
+ @ok = 0
30
+ @errors = []
31
+ end
32
+
33
+ def start_block(block)
34
+ @err = false
35
+ end
36
+
37
+ def finish_block(block)
38
+ unless @err
39
+ @ok += 1
40
+ print ANSI.green{'.'}
41
+ end
42
+ end
43
+
44
+ def finalize
45
+ puts
46
+ puts
47
+
48
+ @errors.each do |code, e|
49
+ puts code
50
+ puts( ANSI.red{"#{e.message} (#{e.class})\n " + filter_backtrace(e.backtrace).join("\n ") + "\n"})
51
+ end
52
+
53
+ puts
54
+ @errors.each do |code, e|
55
+ ln = e.backtrace.first.sub(/:in .*$/, '')
56
+ puts ANSI.red{"dokaz #{ln}"} + ANSI.cyan{" # #{e.message} (#{e.class})"}
57
+ end
58
+
59
+ puts
60
+ puts [
61
+ "#{@ok + @errors.count} total",
62
+ !@ok.zero? && ANSI.green{"#{@ok} ok"},
63
+ !@errors.empty? && ANSI.red{"#{@errors.count} errors"}
64
+ ].select{|l| l}.join(', ')
65
+
66
+ end
67
+
68
+ def output_err(code, e)
69
+ @errors << [code, e]
70
+ print ANSI.red{'F'}
71
+ @err = true
72
+ end
73
+ end
74
+
75
+ class ShowcaseFormatter < Formatter
76
+ def start_block(block)
77
+ puts "#{block.caption}\n#{'=' * block.caption.size}\n\n"
78
+ end
79
+
80
+ def output(code, res, out)
81
+ puts code
82
+ unless out.empty?
83
+ puts comment("# Prints: \n# " + out.split("\n").join("\n# "))
84
+ end
85
+ puts comment("# => " + res.inspect.split("\n").join("\n# "))
86
+ end
87
+
88
+ def output_err(code, e)
89
+ puts code
90
+ puts comment("# Throws: #{e.message} (#{e.class})")
91
+ end
92
+
93
+ def finish_block(block)
94
+ puts "\n\n"
95
+ end
96
+
97
+ private
98
+ def comment(str)
99
+ ANSI.dark{str}
100
+ end
101
+ end
102
+ end
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+ class Dokaz
3
+ class SpecIO < StringIO
4
+ def encoding
5
+ string.encoding
6
+ end
7
+ end
8
+
9
+ class Out < StringIO
10
+ def capture(&block)
11
+ orig = $stdout
12
+ $stdout = self
13
+ yield
14
+ string.to_s
15
+ ensure
16
+ $stdout = orig
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,50 @@
1
+ # encoding: utf-8
2
+ require 'strscan'
3
+
4
+ class Dokaz
5
+ class Block
6
+ attr_reader :code, :line, :file
7
+ def initialize(code, line, file)
8
+ @code, @line, @file = code, line, file
9
+ end
10
+
11
+ def end_line
12
+ line + code.count("\n")
13
+ end
14
+
15
+ def caption
16
+ "#{file}:#{line}"
17
+ end
18
+
19
+ def inside?(ln)
20
+ (line..end_line).cover?(ln)
21
+ end
22
+ end
23
+
24
+ class Parser
25
+ def initialize(path)
26
+ @path = path
27
+ end
28
+
29
+ def parse
30
+ res = []
31
+ ln = 0
32
+ scanner = StringScanner.new(File.read(@path))
33
+ loop do
34
+ text = scanner.scan_until(/\n```ruby\n/)
35
+ break unless text
36
+
37
+ ln += text.count("\n")
38
+ code = scanner.scan_until(/\n```\n/)
39
+ if !code
40
+ res << Block.new(scanner.rest, ln, @path)
41
+ break
42
+ end
43
+ res << Block.new(code.sub(/```\Z/, ''), ln, @path)
44
+ ln += code.count("\n")
45
+ end
46
+
47
+ res
48
+ end
49
+ end
50
+ end
Binary file
Binary file
metadata ADDED
@@ -0,0 +1,112 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dokaz
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Victor Shepelev
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: ansi
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: slop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '3'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-its
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: '1'
69
+ description:
70
+ email: zverok.offline@gmail.com
71
+ executables:
72
+ - dokaz
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - LICENSE.txt
77
+ - README.md
78
+ - bin/dokaz
79
+ - dokaz.gemspec
80
+ - examples/README.md
81
+ - lib/dokaz.rb
82
+ - lib/dokaz/formatters.rb
83
+ - lib/dokaz/io_helpers.rb
84
+ - lib/dokaz/parser.rb
85
+ - screenshots/showcase.png
86
+ - screenshots/spec.png
87
+ homepage: https://github.com/zverok/dokaz
88
+ licenses:
89
+ - MIT
90
+ metadata: {}
91
+ post_install_message:
92
+ rdoc_options: []
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 2.4.6
108
+ signing_key:
109
+ specification_version: 4
110
+ summary: Use your documentation as a specification
111
+ test_files: []
112
+ has_rdoc: