approvals 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.
Files changed (69) hide show
  1. data/.gitignore +7 -0
  2. data/.travis.yml +4 -0
  3. data/Gemfile +4 -0
  4. data/License.txt +22 -0
  5. data/README.md +133 -0
  6. data/Rakefile +1 -0
  7. data/TODO +2 -0
  8. data/approvals.gemspec +23 -0
  9. data/bin/approvals +9 -0
  10. data/lib/approvals.rb +24 -0
  11. data/lib/approvals/approval.rb +97 -0
  12. data/lib/approvals/configuration.rb +24 -0
  13. data/lib/approvals/dsl.rb +7 -0
  14. data/lib/approvals/error.rb +5 -0
  15. data/lib/approvals/extensions/rspec.rb +11 -0
  16. data/lib/approvals/extensions/rspec/dsl.rb +9 -0
  17. data/lib/approvals/extensions/rspec/example.rb +17 -0
  18. data/lib/approvals/extensions/rspec/example_group.rb +15 -0
  19. data/lib/approvals/namers/default_namer.rb +20 -0
  20. data/lib/approvals/namers/rspec_namer.rb +27 -0
  21. data/lib/approvals/reporters.rb +3 -0
  22. data/lib/approvals/reporters/first_working_reporter.rb +21 -0
  23. data/lib/approvals/reporters/image_reporter.rb +16 -0
  24. data/lib/approvals/reporters/image_reporter/html_image_reporter.rb +35 -0
  25. data/lib/approvals/reporters/image_reporter/image_magick_reporter.rb +20 -0
  26. data/lib/approvals/utilities.rb +4 -0
  27. data/lib/approvals/utilities/cli.rb +28 -0
  28. data/lib/approvals/utilities/dotfile.rb +38 -0
  29. data/lib/approvals/utilities/executable.rb +14 -0
  30. data/lib/approvals/utilities/scrubber.rb +43 -0
  31. data/lib/approvals/utilities/system_command.rb +13 -0
  32. data/lib/approvals/writer.rb +26 -0
  33. data/lib/approvals/writers.rb +5 -0
  34. data/lib/approvals/writers/array_writer.rb +15 -0
  35. data/lib/approvals/writers/html_writer.rb +15 -0
  36. data/lib/approvals/writers/json_writer.rb +17 -0
  37. data/lib/approvals/writers/text_writer.rb +22 -0
  38. data/lib/approvals/writers/xml_writer.rb +15 -0
  39. data/spec/approvals_spec.rb +64 -0
  40. data/spec/configuration_spec.rb +27 -0
  41. data/spec/extensions/rspec_approvals_spec.rb +49 -0
  42. data/spec/fixtures/approvals/approvals_verifies_a_complex_object.approved.txt +1 -0
  43. data/spec/fixtures/approvals/approvals_verifies_a_string.approved.txt +1 -0
  44. data/spec/fixtures/approvals/approvals_verifies_an_array.approved.txt +4 -0
  45. data/spec/fixtures/approvals/approvals_verifies_an_executable.approved.txt +1 -0
  46. data/spec/fixtures/approvals/approvals_verifies_html.approved.html +11 -0
  47. data/spec/fixtures/approvals/approvals_verifies_json.approved.json +7 -0
  48. data/spec/fixtures/approvals/approvals_verifies_xml.approved.xml +9 -0
  49. data/spec/fixtures/approvals/verifications_a_string.approved.txt +1 -0
  50. data/spec/fixtures/approvals/verifies_a_complex_object.approved.txt +1 -0
  51. data/spec/fixtures/approvals/verifies_a_string.approved.txt +1 -0
  52. data/spec/fixtures/approvals/verifies_an_array.approved.txt +4 -0
  53. data/spec/fixtures/approvals/verifies_an_executable.approved.txt +1 -0
  54. data/spec/fixtures/approvals/verifies_html.approved.html +11 -0
  55. data/spec/fixtures/approvals/verifies_json.approved.json +7 -0
  56. data/spec/fixtures/approvals/verifies_xml.approved.xml +9 -0
  57. data/spec/fixtures/one.png +0 -0
  58. data/spec/fixtures/two.png +0 -0
  59. data/spec/namers/default_namer_spec.rb +35 -0
  60. data/spec/namers/rspec_namer_spec.rb +31 -0
  61. data/spec/namers_spec.rb +16 -0
  62. data/spec/reporters/first_working_reporter_spec.rb +29 -0
  63. data/spec/reporters/html_image_reporter_spec.rb +22 -0
  64. data/spec/reporters/image_magick_reporter_spec.rb +16 -0
  65. data/spec/utilities/dotfile_spec.rb +22 -0
  66. data/spec/utilities/executable_spec.rb +15 -0
  67. data/spec/utilities/scrubber_spec.rb +24 -0
  68. data/spec/utilities/system_command_spec.rb +13 -0
  69. metadata +147 -0
@@ -0,0 +1,20 @@
1
+ module Approvals
2
+ module Namers
3
+ class DefaultNamer
4
+
5
+ attr_reader :name
6
+ def initialize(name = nil)
7
+ raise ArgumentError.new("DefaultNamer: You must specify a name") if name.nil?
8
+ @name = normalize(name)
9
+ end
10
+
11
+ def normalize(string)
12
+ string.strip.squeeze(" ").gsub(/[\ :-]+/, '_').gsub(/[\W]/, '').downcase
13
+ end
14
+
15
+ def output_dir
16
+ @output_dir ||= Approvals.configuration.approvals_path
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,27 @@
1
+ module Approvals
2
+ module Namers
3
+ class RSpecNamer
4
+
5
+ attr_reader :name
6
+ def initialize(example)
7
+ @name = normalize example.full_description
8
+ end
9
+
10
+ def normalize(string)
11
+ string.strip.squeeze(" ").gsub(/[\ :-]+/, '_').gsub(/[\W]/, '').downcase
12
+ end
13
+
14
+ def output_dir
15
+ unless @output_dir
16
+ begin
17
+ @output_dir = ::RSpec.configuration.approvals_path
18
+ rescue NoMethodError => e
19
+ end
20
+ @output_dir ||= 'spec/fixtures/approvals/'
21
+ end
22
+ @output_dir
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,3 @@
1
+ require 'singleton'
2
+ require 'approvals/reporters/first_working_reporter'
3
+ require 'approvals/reporters/image_reporter'
@@ -0,0 +1,21 @@
1
+ module Approvals
2
+ module Reporters
3
+ class FirstWorkingReporter
4
+
5
+ attr_accessor :reporters
6
+ def initialize(*reporters)
7
+ self.reporters = reporters
8
+ end
9
+
10
+ def working_in_this_environment?
11
+ reporters.any?(&:working_in_this_environment?)
12
+ end
13
+
14
+ def report(received, approved)
15
+ reporter = reporters.find(&:working_in_this_environment?)
16
+ reporter.report(received, approved) unless reporter.nil?
17
+ end
18
+
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,16 @@
1
+ require 'approvals/reporters/image_reporter/image_magick_reporter'
2
+ require 'approvals/reporters/image_reporter/html_image_reporter'
3
+
4
+ module Approvals
5
+ module Reporters
6
+
7
+ class ImageReporter < FirstWorkingReporter
8
+ include Singleton
9
+
10
+ def initialize
11
+ super(ImageMagickReporter.instance, HtmlImageReporter.instance)
12
+ end
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,35 @@
1
+ module Approvals
2
+ module Reporters
3
+ class HtmlImageReporter
4
+ include Singleton
5
+
6
+ def working_in_this_environment?
7
+ true
8
+ end
9
+
10
+ def report(received, approved)
11
+ display html(received, approved)
12
+ end
13
+
14
+ def html(received, approved)
15
+ template(File.expand_path(received), File.expand_path(approved))
16
+ end
17
+
18
+ def display(page)
19
+ filename = "#{Approvals.tmp_path}tmp-#{rand(Time.now.to_i)}.html"
20
+ File.open(filename, 'w') do |file|
21
+ file.write page
22
+ end
23
+ system("open #{filename}")
24
+ end
25
+
26
+ private
27
+ def template(received, approved)
28
+ <<-HTML.gsub(/^\ {8}/, '').chomp
29
+ <html><head><title>Approval</title></head><body><center><table style="text-align: center;" border="1"><tr><td><img src="file://#{received}"></td><td><img src="file://#{approved}"></td></tr><tr><td>received</td><td>approved</td></tr></table></center></body></html>
30
+ HTML
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,20 @@
1
+ module Approvals
2
+ module Reporters
3
+ class ImageMagickReporter
4
+ include Singleton
5
+
6
+ def working_in_this_environment?
7
+ SystemCommand.exists? "compare"
8
+ end
9
+
10
+ def create_command_line(received, approved)
11
+ "compare #{received} #{approved} -compose Src x:"
12
+ end
13
+
14
+ def report(received, approved)
15
+ system(create_command_line(received, approved))
16
+ end
17
+
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,4 @@
1
+ require 'approvals/utilities/system_command'
2
+ require 'approvals/utilities/scrubber'
3
+ require 'approvals/utilities/dotfile'
4
+ require 'approvals/utilities/executable'
@@ -0,0 +1,28 @@
1
+ module Approvals
2
+ class CLI < Thor
3
+
4
+ desc "verify", "Go through all failing approvals with a diff tool"
5
+ method_option :diff, :type => :string, :default => 'vimdiff', :aliases => '-d', :desc => 'The difftool to use'
6
+ method_option :ask, :type => :boolean, :default => false, :aliases => "-a", :desc => 'Offer to approve the received file for you.'
7
+ def verify
8
+
9
+ approvals = File.read('.approvals').split("\n")
10
+
11
+ rejected = []
12
+ approvals.each do |approval|
13
+ system("#{options[:diff]} #{approval}")
14
+
15
+ if options[:ask] && yes?("Approve?")
16
+ system("mv #{approval}")
17
+ else
18
+ rejected << approval
19
+ end
20
+ end
21
+
22
+ File.open('.approvals', 'w') do |f|
23
+ f.write rejected.join("\n")
24
+ end
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,38 @@
1
+ module Approvals
2
+
3
+ class Dotfile
4
+ class << self
5
+
6
+ def reset
7
+ File.delete(path) if File.exists?(path)
8
+ touch
9
+ end
10
+
11
+ def path
12
+ '.approvals'
13
+ end
14
+
15
+ def touch
16
+ FileUtils.touch(path)
17
+ end
18
+
19
+ def append(text)
20
+ unless includes?(text)
21
+ write text
22
+ end
23
+ end
24
+
25
+ def includes?(text)
26
+ system("cat #{path} | grep -q \"^#{text}$\"")
27
+ end
28
+
29
+ def write(text)
30
+ File.open(path, 'a+') do |f|
31
+ f.write "#{text}\n"
32
+ end
33
+ end
34
+
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,14 @@
1
+ module Approvals
2
+ class Executable
3
+
4
+ attr_accessor :command, :on_failure
5
+ def initialize(command, &block)
6
+ self.command = command
7
+ self.on_failure = block
8
+ end
9
+
10
+ def to_s
11
+ command
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,43 @@
1
+ module Approvals
2
+ class Scrubber
3
+ def initialize(string, hash = nil)
4
+ @hash = hash
5
+ @string = scrub(string)
6
+ end
7
+
8
+ def hash
9
+ @hash ||= {
10
+ 'current_dir' => File.expand_path('.')
11
+ }
12
+ end
13
+
14
+ def scrub(string)
15
+ hash.each do |key, value|
16
+ string = string.gsub(value, wrap(key))
17
+ end
18
+ string
19
+ end
20
+
21
+ def unscrub(string = @string)
22
+ hash.each do |key, value|
23
+ string = string.gsub(wrap(key), value)
24
+ end
25
+ string
26
+ end
27
+
28
+ def wrap(string)
29
+ "{{#{string}}}"
30
+ end
31
+
32
+ def to_s
33
+ @string
34
+ end
35
+
36
+ #def to_executable(&block)
37
+ # Approvals::Executable.new(@string) do |scrubbed|
38
+ # block.call(unscrub(scrubbed))
39
+ # end
40
+ #end
41
+
42
+ end
43
+ end
@@ -0,0 +1,13 @@
1
+ module Approvals
2
+
3
+ module SystemCommand
4
+
5
+ class << self
6
+ def exists?(executable)
7
+ `which #{executable}` != ""
8
+ end
9
+ end
10
+
11
+ end
12
+
13
+ end
@@ -0,0 +1,26 @@
1
+ require 'approvals/writers/text_writer'
2
+ require 'approvals/writers/array_writer'
3
+ require 'approvals/writers/html_writer'
4
+ require 'approvals/writers/xml_writer'
5
+ require 'approvals/writers/json_writer'
6
+
7
+ module Approvals
8
+ module Writer
9
+ extend Writers
10
+
11
+ class << self
12
+ def for(format)
13
+ case format
14
+ when :json then JsonWriter.instance
15
+ when :xml then XmlWriter.instance
16
+ when :html then HtmlWriter.instance
17
+ when :hash then HashWriter.instance
18
+ when :array then ArrayWriter.instance
19
+ else
20
+ TextWriter.instance
21
+ end
22
+ end
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,5 @@
1
+ require 'approvals/writers/text_writer'
2
+ require 'approvals/writers/array_writer'
3
+ require 'approvals/writers/html_writer'
4
+ require 'approvals/writers/xml_writer'
5
+ require 'approvals/writers/json_writer'
@@ -0,0 +1,15 @@
1
+ module Approvals
2
+ module Writers
3
+ class ArrayWriter < TextWriter
4
+
5
+ def format(data)
6
+ s = ""
7
+ data.each_with_index do |value, i|
8
+ s << "[#{i.inspect}] #{value.inspect}\n"
9
+ end
10
+ s
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Approvals
2
+ module Writers
3
+ class HtmlWriter < TextWriter
4
+
5
+ def extension
6
+ 'html'
7
+ end
8
+
9
+ def format(data)
10
+ Nokogiri::XML(data.to_s.strip,&:noblanks).to_xhtml(:indent => 2, :encoding => 'UTF-8')
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ require 'json'
2
+
3
+ module Approvals
4
+ module Writers
5
+ class JsonWriter < TextWriter
6
+
7
+ def extension
8
+ 'json'
9
+ end
10
+
11
+ def format(data)
12
+ JSON.pretty_generate(JSON.parse(data))
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ module Approvals
2
+ module Writers
3
+ class TextWriter
4
+ include Singleton
5
+
6
+ def extension
7
+ 'txt'
8
+ end
9
+
10
+ def write(data, path)
11
+ File.open(path, 'w') do |f|
12
+ f.write format(data)
13
+ end
14
+ end
15
+
16
+ def format(data)
17
+ data.inspect
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ module Approvals
2
+ module Writers
3
+ class XmlWriter < TextWriter
4
+
5
+ def extension
6
+ 'xml'
7
+ end
8
+
9
+ def format(data)
10
+ Nokogiri::XML(data.to_s.strip,&:noblanks).to_xml(:indent => 2, :encoding => 'UTF-8')
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,64 @@
1
+ require 'approvals'
2
+ require 'approvals/namers/rspec_namer'
3
+
4
+ describe Approvals do
5
+
6
+ let(:namer) { Approvals::Namers::RSpecNamer.new(self.example) }
7
+
8
+ it "fails" do
9
+ ->{ Approvals.verify "this one doesn't exist", :namer => namer }.should raise_error Approvals::ApprovalError
10
+ end
11
+
12
+ it "verifies a string" do
13
+ string = "We have, I fear, confused power with greatness."
14
+ Approvals.verify string, :namer => namer
15
+ end
16
+
17
+ it "verifies an array" do
18
+ array = [
19
+ "abc",
20
+ 123,
21
+ :zomg_fooooood,
22
+ %w(cheese burger ribs steak bacon)
23
+ ]
24
+ Approvals.verify array, :namer => namer
25
+ end
26
+
27
+ it "verifies a complex object" do
28
+ hello = Object.new
29
+ def hello.to_s
30
+ "Hello, World!"
31
+ end
32
+
33
+ def hello.inspect
34
+ "#<The World Says: Hello!>"
35
+ end
36
+
37
+ Approvals.verify hello, :namer => namer
38
+ end
39
+
40
+ it "verifies html" do
41
+ html = <<-HTML
42
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "DTD/xhtml1-strict.dtd"><html><head><title>Approval</title></head><body><h1>An Approval</h1><p>It has a paragraph</p></body></html>
43
+ HTML
44
+ Approvals.verify html, :format => :html, :namer => namer
45
+ end
46
+
47
+ it "verifies xml" do
48
+ xml = "<xml char=\"kiddo\"><node><content name='beatrice' /></node><node aliases='5'><content /></node></xml>"
49
+ Approvals.verify xml, :format => :xml, :namer => namer
50
+ end
51
+
52
+ it "verifies json" do
53
+ json = '{"pet":{"species":"turtle","color":"green","name":"Anthony"}}'
54
+ Approvals.verify json, :format => :json, :namer => namer
55
+ end
56
+
57
+ it "verifies an executable" do
58
+ executable = Approvals::Executable.new('SELECT 1') do |command|
59
+ puts "your slip is showing (#{command})"
60
+ end
61
+
62
+ Approvals.verify executable, :namer => namer
63
+ end
64
+ end