git-approvals 0.2.2 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -18,10 +18,12 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ["lib"]
20
20
 
21
- spec.add_dependency 'awesome_print', '1.1.0'
21
+ spec.add_dependency 'tilt', '~> 1.4.1'
22
22
 
23
23
  # soft dependencies
24
- spec.add_development_dependency 'uglifier', '~> 2.1.1'
24
+ spec.add_development_dependency 'awesome_print', '~> 1.1.0'
25
+ spec.add_development_dependency 'uglifier', '~> 2.1.1'
26
+ spec.add_development_dependency 'sass', '~> 3.2.9'
25
27
 
26
28
  spec.add_development_dependency 'bundler', '~> 1.3'
27
29
  spec.add_development_dependency 'rspec', '~> 2.13.0'
@@ -1,106 +1,46 @@
1
1
  require 'open3'
2
+ require 'pathname'
3
+ require 'tilt'
2
4
 
3
5
  module Git
4
6
  module Approvals
5
- ##
6
- # The base class for approval errors.
7
- class ApprovalError < StandardError; end
8
-
9
- ##
10
- # Raised when an unregistered formatter is requested.
11
- class UnknownFormat < ApprovalError
12
- def initialize( name )
13
- super "There is no registered formatter named '#{name}'."
14
- end
15
- end
16
-
17
- ##
18
- # Raised when a formatter's soft dependencies are missing.
19
- class MissingSoftDependency < ApprovalError
20
- def initialize( name, dependency )
21
- super <<-EOS
22
- The format '#{name}' requires #{dependency}.
23
- To use this formatter, make sure to load the dependency:
24
-
25
- require '#{dependency}'
26
- EOS
27
- end
28
- end
29
7
 
30
8
  ##
31
9
  #
32
- class Approval
33
-
34
- class << self
35
-
36
- ##
37
- # Registers a new formatter block by name. The block
38
- # is expected to return a deterministic string
39
- # representation of an object.
40
- def register_formatter( name, &block )
41
- formatters[ name.to_sym ] = block
42
- end
43
-
44
- ##
45
- # Looks up the formatter named `name` and attempts to
46
- # format `object`. Raises a helpful error message if
47
- # a formatter's soft dependency cannot be loaded.
48
- def format( name, object )
49
- formatters[ name ][ object ]
50
- rescue NoMethodError => e
51
- raise UnknownFormat, name
52
- rescue LoadError => e
53
- raise MissingSoftDependency.new name,
54
- e.message[ /^cannot load such file -- (.*)$/, 1 ]
55
- end
10
+ class Approval < Pathname
56
11
 
57
- protected
12
+ def initialize( path, options={} ) # :nodoc:
13
+ @options = options
58
14
 
59
- ##
60
- # The hash of registered formatters by name.
61
- def formatters
62
- @formatters ||= { }
63
- end
64
- end
15
+ path = Pathname( path )
16
+ path = munge_format( path )
17
+ path = munge_filename( path )
65
18
 
66
- ##
67
- # The `txt` format requires the `awesome_print` gem.
68
- # It is suitable for formatting most native ruby types.
69
- register_formatter :txt do |object|
70
- require 'awesome_print'
71
- object.awesome_inspect :plain => true, :indent => -2
19
+ super path.to_path
72
20
  end
21
+ attr_reader :options
73
22
 
74
23
  ##
75
- # The `json` format requires the `json` library.
76
- # It is suitable for formatting JSON strings.
77
- register_formatter :json do |object|
78
- require 'json'
79
- JSON.pretty_generate JSON.parse( object )
24
+ # Makes sure the given pathname has an extension.
25
+ def munge_format( path )
26
+ ext = [ options[ :format ], path.extname, 'txt' ].detect do |arg|
27
+ !arg.nil? && !arg.empty?
28
+ end
29
+ ext = ext.to_s
30
+ ext = '.' + ext unless ext.start_with?( '.' )
31
+ path.sub_ext ext
80
32
  end
81
33
 
82
34
  ##
83
- # The `js` format requires the `uglifier` gem.
84
- # It is suitable for formatting javascript.
85
- register_formatter :js do |object|
86
- require 'uglifier'
87
- Uglifier.compile object,
88
- :output => {
89
- :beautify => true,
90
- :indent_level => 2,
91
- :comments => :all,
92
- :space_colon => true
93
- }
94
- end
95
-
96
- def initialize( path, options={} ) # :nodoc:
97
- @path, @options = path, options
98
-
99
- # rewrite the extension for the file based on the format
100
- @path.chomp! File.extname( @path )
101
- @path << '.' << format.to_s
35
+ # Makes sure the given pathname has the correct filename.
36
+ def munge_filename( path )
37
+ if filename = options[ :filename ]
38
+ dir, base = path.split
39
+ dir.join( filename + base.extname )
40
+ else
41
+ path
42
+ end
102
43
  end
103
- attr_reader :path, :options
104
44
 
105
45
  ##
106
46
  # Diffs the given string with this approval file. If the
@@ -109,20 +49,20 @@ EOS
109
49
  # called if the diff fails, meaning there are differences.
110
50
  def diff( string, &block )
111
51
  # Make sure the directory of the file exists.
112
- FileUtils.mkdir_p File.dirname( path )
52
+ dirname.mkpath
113
53
 
114
54
  # Write the new string to the file.
115
- File.open path, 'w' do |f|
116
- f << self.class.format( format, string )
55
+ open 'w' do |f|
56
+ f << Tilt.new( to_path ).render( string )
117
57
  end
118
58
 
119
59
  # If the file hasn't been checked in, raise an error.
120
- sh "git ls-files #{path} --error-unmatch" do |err|
121
- raise Errno::ENOENT, path
60
+ sh "git ls-files #{to_path} --error-unmatch" do |err|
61
+ raise Errno::ENOENT, to_path
122
62
  end
123
63
 
124
64
  # If the file has changed, call the block.
125
- sh "git diff --exit-code #{path}" do |err|
65
+ sh "git diff --exit-code #{to_path}" do |err|
126
66
  block.call err
127
67
  end
128
68
  end
@@ -135,13 +75,13 @@ EOS
135
75
  out, cmd = Open3.capture2e cmd
136
76
  yield out if !cmd.success?
137
77
  end
138
-
139
- ##
140
- # The format of this approval. Determines the file extension
141
- # and also the formatter to use when writing the approval file.
142
- def format
143
- options[ :format ] || :txt
144
- end
145
78
  end
79
+
80
+ ##
81
+ # Register all formatters as tilt templates.
82
+ Tilt.register AwesomePrintFormatter, 'txt'
83
+ Tilt.register JSONFormatter, 'json'
84
+ Tilt.register UglifierFormatter, 'js'
85
+ Tilt.register SassFormatter, 'css'
146
86
  end
147
87
  end
@@ -0,0 +1,28 @@
1
+ require 'tilt'
2
+
3
+ module Git
4
+ module Approvals
5
+ class AwesomePrintFormatter < Tilt::Template
6
+
7
+ def self.engine_initialized?
8
+ defined?(::AwesomePrint)
9
+ end
10
+
11
+ def initialize_engine
12
+ require_template_library 'awesome_print'
13
+ end
14
+
15
+ def prepare
16
+ end
17
+
18
+ def evaluate( context, locals, &block )
19
+ # TODO use locals as options to the formatter
20
+ # TODO require awesome_print/inspector to not pollute kernel, etc
21
+ context.awesome_inspect \
22
+ :plain => true,
23
+ :indent => -2
24
+ end
25
+ end
26
+ end
27
+ end
28
+
@@ -0,0 +1,24 @@
1
+ require 'tilt'
2
+
3
+ module Git
4
+ module Approvals
5
+ class JSONFormatter < Tilt::Template
6
+
7
+ def self.engine_initialized?
8
+ defined?(::JSON)
9
+ end
10
+
11
+ def initialize_engine
12
+ require_template_library 'json'
13
+ end
14
+
15
+ def prepare
16
+ end
17
+
18
+ def evaluate( context, locals, &block )
19
+ # TODO use locals as options to the formatter
20
+ ::JSON.pretty_generate( ::JSON.parse( context ) )
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,50 @@
1
+ require 'git-approvals'
2
+
3
+ begin
4
+ gem 'rspec-core', '>= 2.12.0'
5
+ rescue Gem::LoadError => e
6
+ raise 'rspec/approvals requires rspec-core >= 2.12.0. Please upgrade rspec.'
7
+ end
8
+
9
+ module RSpec
10
+ module Approvals
11
+
12
+ ##
13
+ # Verifies that the result of the block is the same as the approved
14
+ # version.
15
+ def verify( options={}, &block )
16
+ approval = Git::Approvals::Approval.new( approval_path, options )
17
+ approval.diff( block.call ) do |err|
18
+ ::RSpec::Expectations.fail_with err
19
+ end
20
+ rescue Errno::ENOENT => e
21
+ ::RSpec::Expectations.fail_with e.message
22
+ EOS
23
+ end
24
+
25
+ ##
26
+ # The path to the approval for this example
27
+ def approval_path
28
+ File.join approval_directory, approval_filename
29
+ end
30
+
31
+ ##
32
+ # The approval filename
33
+ def approval_filename
34
+ parts = [ example, *example.example_group.parent_groups ].map do |ex|
35
+ Git::Approvals::Utils.filenamify ex.description
36
+ end
37
+ File.join parts.to_a.reverse
38
+ end
39
+
40
+ ##
41
+ # The directory containing this spec's approvals
42
+ def approval_directory
43
+ example.file_path.sub /\.rb$/, ''
44
+ end
45
+ end
46
+
47
+ ##
48
+ #
49
+ configure { |config| config.include RSpec::Approvals }
50
+ end
@@ -0,0 +1,29 @@
1
+ require 'tilt'
2
+
3
+ module Git
4
+ module Approvals
5
+ class SassFormatter < Tilt::Template
6
+
7
+ def self.engine_initialized?
8
+ defined?(::Sass::Engine)
9
+ end
10
+
11
+ def initialize_engine
12
+ require_template_library 'sass'
13
+ end
14
+
15
+ def prepare
16
+ end
17
+
18
+ def evaluate( context, locals, &block )
19
+ # TODO use locals as options to the formatter
20
+ ::Sass::Engine.new( context, {
21
+ :syntax => :scss,
22
+ :cache => false,
23
+ :read_cache => false,
24
+ :style => :expanded
25
+ } ).render
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,30 @@
1
+ require 'tilt'
2
+
3
+ module Git
4
+ module Approvals
5
+ class UglifierFormatter < Tilt::Template
6
+
7
+ def self.engine_initialized?
8
+ defined?(::Uglifier)
9
+ end
10
+
11
+ def initialize_engine
12
+ require_template_library 'uglifier'
13
+ end
14
+
15
+ def prepare
16
+ end
17
+
18
+ def evaluate( context, locals, &block )
19
+ # TODO use locals as options to the formatter
20
+ ::Uglifier.compile context,
21
+ :output => {
22
+ :beautify => true,
23
+ :indent_level => 2,
24
+ :comments => :all,
25
+ :space_colon => true
26
+ }
27
+ end
28
+ end
29
+ end
30
+ end
@@ -1,5 +1,5 @@
1
1
  module Git
2
2
  module Approvals
3
- VERSION = '0.2.2'
3
+ VERSION = '0.3.0'
4
4
  end
5
5
  end
data/lib/git/approvals.rb CHANGED
@@ -2,7 +2,11 @@ require 'git/approvals/version'
2
2
 
3
3
  module Git
4
4
  module Approvals
5
- autoload :Approval, 'git/approvals/approval'
6
- autoload :Utils, 'git/approvals/utils'
5
+ autoload :Approval, 'git/approvals/approval'
6
+ autoload :AwesomePrintFormatter, 'git/approvals/awesome_print_formatter.rb'
7
+ autoload :JSONFormatter, 'git/approvals/json_formatter.rb'
8
+ autoload :SassFormatter, 'git/approvals/sass_formatter.rb'
9
+ autoload :UglifierFormatter, 'git/approvals/uglifier_formatter.rb'
10
+ autoload :Utils, 'git/approvals/utils'
7
11
  end
8
12
  end
@@ -1,50 +1 @@
1
- require 'git-approvals'
2
-
3
- begin
4
- gem 'rspec-core', '>= 2.12.0'
5
- rescue Gem::LoadError => e
6
- raise 'rspec/approvals requires rspec-core >= 2.12.0. Please upgrade rspec.'
7
- end
8
-
9
- module RSpec
10
- module Approvals
11
-
12
- ##
13
- # Verifies that the result of the block is the same as the approved
14
- # version.
15
- def verify( options={}, &block )
16
- approval = Git::Approvals::Approval.new( approval_path, options )
17
- approval.diff( block.call ) do |err|
18
- ::RSpec::Expectations.fail_with err
19
- end
20
- rescue Errno::ENOENT => e
21
- ::RSpec::Expectations.fail_with e.message
22
- EOS
23
- end
24
-
25
- ##
26
- # The path to the approval for this example
27
- def approval_path
28
- File.join approval_directory, approval_filename
29
- end
30
-
31
- ##
32
- # The approval filename
33
- def approval_filename
34
- parts = [ example, *example.example_group.parent_groups ].map do |ex|
35
- Git::Approvals::Utils.filenamify ex.description
36
- end
37
- File.join parts.to_a.reverse
38
- end
39
-
40
- ##
41
- # The directory containing this spec's approvals
42
- def approval_directory
43
- example.file_path.sub /\.rb$/, ''
44
- end
45
- end
46
-
47
- ##
48
- #
49
- configure { |config| config.include RSpec::Approvals }
50
- end
1
+ require 'git/approvals/rspec'
@@ -0,0 +1,3 @@
1
+ html, body {
2
+ font-family: "Helvetica Neue";
3
+ }
@@ -2,13 +2,28 @@ require 'spec_helper'
2
2
 
3
3
  describe Git::Approvals::Approval do
4
4
 
5
- describe 'initialize' do
5
+ describe '#initialize' do
6
6
  subject { described_class.new './foo/bar.txt' }
7
7
 
8
- its( :path ){ should == './foo/bar.txt' }
8
+ its( :to_path ){ should == './foo/bar.txt' }
9
9
  its( :options ){ should == { } }
10
10
  end
11
11
 
12
+ describe '#options' do
13
+ describe ':format' do
14
+ it 'replaces the extension' do
15
+ subject = described_class.new 'foo/bar.txt', :format => :json
16
+ subject.to_path.should == 'foo/bar.json'
17
+ end
18
+ end
19
+ describe ':filename' do
20
+ it 'replaces the filename' do
21
+ subject = described_class.new 'foo/bar.txt', :filename => 'baz'
22
+ subject.to_path.should == 'foo/baz.txt'
23
+ end
24
+ end
25
+ end
26
+
12
27
  describe '#diff' do
13
28
  subject { described_class.new './foo/bar.txt' }
14
29
 
@@ -41,17 +56,6 @@ describe Git::Approvals::Approval do
41
56
  end
42
57
  end
43
58
 
44
- describe 'extensions' do
45
- it 'leaves the extension if specified' do
46
- approval = described_class.new './foo/bar.txt'
47
- approval.path.should == './foo/bar.txt'
48
- end
49
- it 'uses the format extension if provided' do
50
- approval = described_class.new './foo/bar.baz', :format => :txt
51
- approval.path.should == './foo/bar.txt'
52
- end
53
- end
54
-
55
59
  describe 'formats' do
56
60
  it 'formats strings' do
57
61
  approval = described_class.new './spec/fixtures/string.txt'
@@ -66,15 +70,22 @@ describe Git::Approvals::Approval do
66
70
  approval.diff( { :foo => 'bar', :quux => 'bar' } ){ |diff| fail diff }
67
71
  end
68
72
  it 'formats json' do
69
- approval = described_class.new './spec/fixtures/hash.json', :format => :json
73
+ approval = described_class.new './spec/fixtures/hash.json'
70
74
  approval.diff( '{"foo":"bar","baz":"quux"}' ){ |diff| fail diff }
71
75
  end
72
76
  it 'formats javascript' do
73
- approval = described_class.new './spec/fixtures/asset.js', :format => :js
77
+ approval = described_class.new './spec/fixtures/asset.js'
74
78
  approval.diff( <<-EOS ){ |diff| fail diff }
75
79
  // Comments are preserved
76
80
  (function(){return {status:"IT WERKS"};})();
77
81
  EOS
78
82
  end
83
+ it 'formats css' do
84
+ approval = described_class.new './spec/fixtures/asset.css'
85
+ approval.diff( <<-EOS ){ |diff| fail diff }
86
+ // Comments are preserved
87
+ html,body{font-family:"Helvetica Neue";}
88
+ EOS
89
+ end
79
90
  end
80
91
  end
@@ -4,13 +4,13 @@ require 'rspec/approvals'
4
4
  describe 'RSpec integration' do
5
5
 
6
6
  it 'provides the correct approval directory' do
7
- approval_directory.should == './spec/rspec/approvals_spec'
7
+ approval_directory.should == './spec/git/approvals/rspec_spec'
8
8
  end
9
9
  it 'provides the correct approval filename' do
10
10
  approval_filename.should == 'rspec_integration/provides_the_correct_approval_filename'
11
11
  end
12
12
  it 'provides the correct approval path' do
13
- approval_path.should == './spec/rspec/approvals_spec/rspec_integration/provides_the_correct_approval_path'
13
+ approval_path.should == './spec/git/approvals/rspec_spec/rspec_integration/provides_the_correct_approval_path'
14
14
  end
15
15
 
16
16
  describe '#verify' do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: git-approvals
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,22 +9,38 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-06-11 00:00:00.000000000 Z
12
+ date: 2013-06-14 00:00:00.000000000 Z
13
13
  dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: tilt
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.4.1
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.4.1
14
30
  - !ruby/object:Gem::Dependency
15
31
  name: awesome_print
16
32
  requirement: !ruby/object:Gem::Requirement
17
33
  none: false
18
34
  requirements:
19
- - - '='
35
+ - - ~>
20
36
  - !ruby/object:Gem::Version
21
37
  version: 1.1.0
22
- type: :runtime
38
+ type: :development
23
39
  prerelease: false
24
40
  version_requirements: !ruby/object:Gem::Requirement
25
41
  none: false
26
42
  requirements:
27
- - - '='
43
+ - - ~>
28
44
  - !ruby/object:Gem::Version
29
45
  version: 1.1.0
30
46
  - !ruby/object:Gem::Dependency
@@ -43,6 +59,22 @@ dependencies:
43
59
  - - ~>
44
60
  - !ruby/object:Gem::Version
45
61
  version: 2.1.1
62
+ - !ruby/object:Gem::Dependency
63
+ name: sass
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: 3.2.9
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: 3.2.9
46
78
  - !ruby/object:Gem::Dependency
47
79
  name: bundler
48
80
  requirement: !ruby/object:Gem::Requirement
@@ -160,19 +192,25 @@ files:
160
192
  - lib/git-approvals.rb
161
193
  - lib/git/approvals.rb
162
194
  - lib/git/approvals/approval.rb
195
+ - lib/git/approvals/awesome_print_formatter.rb
196
+ - lib/git/approvals/json_formatter.rb
197
+ - lib/git/approvals/rspec.rb
198
+ - lib/git/approvals/sass_formatter.rb
199
+ - lib/git/approvals/uglifier_formatter.rb
163
200
  - lib/git/approvals/utils.rb
164
201
  - lib/git/approvals/version.rb
165
202
  - lib/rspec/approvals.rb
166
203
  - spec/fixtures/array.txt
204
+ - spec/fixtures/asset.css
167
205
  - spec/fixtures/asset.js
168
206
  - spec/fixtures/hash.json
169
207
  - spec/fixtures/hash.txt
170
208
  - spec/fixtures/string.txt
171
209
  - spec/git/approvals/approval_spec.rb
210
+ - spec/git/approvals/rspec_spec.rb
211
+ - spec/git/approvals/rspec_spec/rspec_integration/verify/fails_when_changed.txt
212
+ - spec/git/approvals/rspec_spec/rspec_integration/verify/passes_when_unchanged.txt
172
213
  - spec/git/approvals/utils_spec.rb
173
- - spec/rspec/approvals_spec.rb
174
- - spec/rspec/approvals_spec/rspec_integration/verify/fails_when_changed.txt
175
- - spec/rspec/approvals_spec/rspec_integration/verify/passes_when_unchanged.txt
176
214
  - spec/spec_helper.rb
177
215
  homepage: https://github.com/jeremyruppel/git-approvals
178
216
  licenses:
@@ -201,14 +239,15 @@ specification_version: 3
201
239
  summary: Simple git-powered approval tests.
202
240
  test_files:
203
241
  - spec/fixtures/array.txt
242
+ - spec/fixtures/asset.css
204
243
  - spec/fixtures/asset.js
205
244
  - spec/fixtures/hash.json
206
245
  - spec/fixtures/hash.txt
207
246
  - spec/fixtures/string.txt
208
247
  - spec/git/approvals/approval_spec.rb
248
+ - spec/git/approvals/rspec_spec.rb
249
+ - spec/git/approvals/rspec_spec/rspec_integration/verify/fails_when_changed.txt
250
+ - spec/git/approvals/rspec_spec/rspec_integration/verify/passes_when_unchanged.txt
209
251
  - spec/git/approvals/utils_spec.rb
210
- - spec/rspec/approvals_spec.rb
211
- - spec/rspec/approvals_spec/rspec_integration/verify/fails_when_changed.txt
212
- - spec/rspec/approvals_spec/rspec_integration/verify/passes_when_unchanged.txt
213
252
  - spec/spec_helper.rb
214
253
  has_rdoc: