guard-rspec 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,19 +1,19 @@
1
- Guard::RSpec ![travis-ci](http://travis-ci.org/guard/guard-rspec.png)
2
- =============
1
+ # Guard::RSpec [![Build Status](https://secure.travis-ci.org/guard/guard-rspec.png?branch=master)](http://travis-ci.org/guard/guard-rspec)
3
2
 
4
3
  RSpec guard allows to automatically & intelligently launch specs when files are modified.
5
4
 
6
- * Compatible with RSpec 1.x & RSpec 2.x (>= 2.4 needed for the notification feature)
7
- * Tested against Ruby 1.8.7, 1.9.2, REE, JRuby & Rubinius.
5
+ * Compatible with RSpec 1.x & RSpec 2.x (>= 2.4 needed for the notification feature).
6
+ * Tested against Ruby 1.8.7, 1.9.2, 1.9.3, REE and the latest versions of JRuby & Rubinius.
8
7
 
9
- Install
10
- -------
8
+ ## Install
11
9
 
12
10
  Please be sure to have [Guard](https://github.com/guard/guard) installed before continue.
13
11
 
14
12
  Install the gem:
15
13
 
16
- $ gem install guard-rspec
14
+ ```
15
+ $ gem install guard-rspec
16
+ ```
17
17
 
18
18
  Add it to your Gemfile (inside development group):
19
19
 
@@ -25,15 +25,15 @@ end
25
25
 
26
26
  Add guard definition to your Guardfile by running this command:
27
27
 
28
- $ guard init rspec
28
+ ```
29
+ $ guard init rspec
30
+ ```
29
31
 
30
- Usage
31
- -----
32
+ ## Usage
32
33
 
33
34
  Please read [Guard usage doc](https://github.com/guard/guard#readme)
34
35
 
35
- Guardfile
36
- ---------
36
+ ## Guardfile
37
37
 
38
38
  RSpec guard can be really adapted to all kind of projects.
39
39
 
@@ -64,8 +64,7 @@ end
64
64
 
65
65
  Please read [Guard doc](https://github.com/guard/guard#readme) for more information about the Guardfile DSL.
66
66
 
67
- Options
68
- -------
67
+ ## Options
69
68
 
70
69
  By default, Guard::RSpec automatically detect your RSpec version (with the `spec_helper.rb` syntax or with Bundler) but you can force the version with the `:version` option:
71
70
 
@@ -99,7 +98,7 @@ end
99
98
  ```
100
99
 
101
100
 
102
- Former `:color`, `:drb`, `:fail_fast` and `:formatter` options are thus deprecated and have no effect anymore.
101
+ Former `:color`, `:drb`, `:fail_fast` and `:formatter` options are deprecated and have no effect anymore.
103
102
 
104
103
  ### List of available options:
105
104
 
@@ -117,6 +116,14 @@ Former `:color`, `:drb`, `:fail_fast` and `:formatter` options are thus deprecat
117
116
  :spec_paths => ["spec"] # specify an array of paths that contain spec files
118
117
  ```
119
118
 
119
+ ### DRb mode
120
+
121
+ When you specify `--drb` within `:cli`, guard-rspec will circumvent the `rspec` command line tool by
122
+ directly communicating with the RSpec DRb server. This avoids the extra overhead incurred by your
123
+ shell, bundler and loading RSpec's environment just to send a DRb message. It shaves off a
124
+ second or two before the specs start to run; they should run almost immediately.
125
+
126
+
120
127
  Notification
121
128
  ------------
122
129
 
data/lib/guard/rspec.rb CHANGED
@@ -6,23 +6,20 @@ module Guard
6
6
  autoload :Runner, 'guard/rspec/runner'
7
7
  autoload :Inspector, 'guard/rspec/inspector'
8
8
 
9
- def initialize(watchers=[], options={})
9
+ def initialize(watchers = [], options = {})
10
10
  super
11
11
  @options = {
12
12
  :all_after_pass => true,
13
13
  :all_on_start => true,
14
14
  :keep_failed => true,
15
- :spec_paths => ["spec"]
16
- }.update(options)
15
+ :spec_paths => ["spec"],
16
+ :run_all => {}
17
+ }.merge(options)
17
18
  @last_failed = false
18
19
  @failed_paths = []
19
20
 
20
- @runner = Runner.new
21
- @inspector = Inspector.new
22
-
23
- @runner.set_rspec_version(options)
24
- @inspector.excluded = @options[:exclude]
25
- @inspector.spec_paths = @options[:spec_paths]
21
+ @inspector = Inspector.new(@options)
22
+ @runner = Runner.new(@options)
26
23
  end
27
24
 
28
25
  # Call once when guard starts
@@ -32,10 +29,9 @@ module Guard
32
29
  end
33
30
 
34
31
  def run_all
35
- passed = @runner.run(options[:spec_paths], options.merge(options[:run_all] || {}).merge(:message => "Running all specs"))
32
+ passed = @runner.run(@options[:spec_paths], @options[:run_all].merge(:message => 'Running all specs'))
36
33
 
37
- @last_failed = !passed
38
- if passed
34
+ unless @last_failed = !passed
39
35
  @failed_paths = []
40
36
  else
41
37
  throw :task_has_failed
@@ -49,22 +45,33 @@ module Guard
49
45
  def run_on_change(paths)
50
46
  paths += @failed_paths if @options[:keep_failed]
51
47
  paths = @inspector.clean(paths)
52
- passed = @runner.run(paths, options)
53
48
 
54
- if passed
55
- # clean failed paths memory
56
- @failed_paths -= paths if @options[:keep_failed]
57
- # run all the specs if the changed specs failed, like autotest
58
- run_all if @last_failed && @options[:all_after_pass]
49
+ if passed = @runner.run(paths)
50
+ remove_failed(paths)
51
+
52
+ # run all the specs if the run before this one failed
53
+ if @last_failed && @options[:all_after_pass]
54
+ @last_failed = false
55
+ run_all
56
+ end
59
57
  else
60
- # remember failed paths for the next change
61
- @failed_paths += paths if @options[:keep_failed]
62
- # track whether the changed specs failed for the next change
63
58
  @last_failed = true
59
+ add_failed(paths)
60
+
64
61
  throw :task_has_failed
65
62
  end
66
63
  end
67
64
 
65
+ private
66
+
67
+ def remove_failed(paths)
68
+ @failed_paths -= paths if @options[:keep_failed]
69
+ end
70
+
71
+ def add_failed(paths)
72
+ @failed_paths += paths if @options[:keep_failed]
73
+ end
74
+
68
75
  end
69
76
  end
70
77
 
@@ -37,7 +37,7 @@ module Guard::RSpec::Formatter
37
37
 
38
38
  private
39
39
 
40
- def round_float(float, decimals=4)
40
+ def round_float(float, decimals = 4)
41
41
  if Float.instance_method(:round).arity == 0 # Ruby 1.8
42
42
  factor = 10**decimals
43
43
  (float*factor).round / factor.to_f
@@ -1,20 +1,26 @@
1
1
  module Guard
2
2
  class RSpec
3
3
  class Inspector
4
+
5
+ def initialize(options = {})
6
+ self.excluded = options[:exclude]
7
+ self.spec_paths = options[:spec_paths]
8
+ end
9
+
4
10
  def excluded
5
11
  @excluded || []
6
12
  end
7
13
 
8
- def excluded=(glob)
9
- @excluded = Dir[glob.to_s]
14
+ def excluded=(pattern)
15
+ @excluded = Dir[pattern.to_s]
10
16
  end
11
17
 
12
18
  def spec_paths
13
19
  @spec_paths || []
14
20
  end
15
21
 
16
- def spec_paths=(path_array)
17
- @spec_paths = Array(path_array)
22
+ def spec_paths=(paths)
23
+ @spec_paths = Array(paths)
18
24
  end
19
25
 
20
26
  def clean(paths)
@@ -42,15 +48,14 @@ module Guard
42
48
 
43
49
  def spec_folder?(path)
44
50
  path.match(%r{^(#{spec_paths.join("|")})[^\.]*$})
45
- # path.match(%r{^spec[^\.]*$})
46
51
  end
47
52
 
48
53
  def spec_files
49
- @spec_files ||= spec_paths.collect { |path| Dir[File.join(path, "**", "*_spec.rb")] }.flatten
54
+ @spec_files ||= spec_paths.collect { |path| Dir[File.join(path, "**{,/*/**}", "*_spec.rb")] }.flatten
50
55
  end
51
56
 
52
57
  def feature_files
53
- @feature_files ||= spec_paths.collect { |path| Dir[File.join(path, "**", "*.feature")] }.flatten
58
+ @feature_files ||= spec_paths.collect { |path| Dir[File.join(path, "**{,/*/**}", "*.feature")] }.flatten
54
59
  end
55
60
 
56
61
  def clear_spec_files_list_after
@@ -3,104 +3,202 @@ module Guard
3
3
  class Runner
4
4
  attr_reader :rspec_version
5
5
 
6
- def run(paths, options={})
6
+ FAILURE_EXIT_CODE = 2
7
+
8
+ def initialize(options = {})
9
+ @options = {
10
+ :bundler => true,
11
+ :binstubs => false,
12
+ :rvm => nil,
13
+ :cli => nil,
14
+ :notification => true
15
+ }.merge(options)
16
+
17
+ deprecations_warnings
18
+ end
19
+
20
+ def run(paths, options = {})
7
21
  return false if paths.empty?
22
+
8
23
  message = options[:message] || "Running: #{paths.join(' ')}"
9
24
  UI.info(message, :reset => true)
10
- system(rspec_command(paths, options))
11
25
 
12
- if options[:notification] != false && !drb?(options) && failure_exit_code_supported?(options) && $? && !$?.success? && $?.exitstatus != failure_exit_code
13
- Notifier.notify("Failed", :title => "RSpec results", :image => :failed, :priority => 2)
26
+ options = @options.merge(options)
27
+
28
+ if drb_used?
29
+ run_via_drb(paths, options)
30
+ else
31
+ run_via_shell(paths, options)
14
32
  end
33
+ end
34
+
35
+ def rspec_version
36
+ @rspec_version ||= @options[:version] || determine_rspec_version
37
+ end
15
38
 
16
- $?.success?
39
+ def rspec_executable
40
+ @rspec_executable ||= begin
41
+ exec = rspec_class.downcase
42
+ binstubs? ? "bin/#{exec}" : exec
43
+ end
17
44
  end
18
45
 
19
- def set_rspec_version(options={})
20
- @rspec_version = options[:version] || determine_rspec_version
46
+ def failure_exit_code_supported?
47
+ @failure_exit_code_supported ||= begin
48
+ cmd_parts = []
49
+ cmd_parts << "bundle exec" if bundler?
50
+ cmd_parts << rspec_executable
51
+ cmd_parts << "--help"
52
+ `#{cmd_parts.join(' ')}`.include? "--failure-exit-code"
53
+ end
54
+ end
55
+
56
+ def rspec_class
57
+ @rspec_class ||= case rspec_version
58
+ when 1
59
+ "Spec"
60
+ when 2
61
+ "RSpec"
62
+ end
21
63
  end
22
64
 
23
65
  private
24
66
 
25
- def rspec_command(paths, options={})
26
- warn_deprectation(options)
67
+ def rspec_arguments(paths, options)
68
+ arg_parts = []
69
+ arg_parts << options[:cli]
70
+ arg_parts << "-f progress" if !options[:cli] || options[:cli].split(/[\s=]/).none? { |w| %w[-f --format].include?(w) }
71
+ if @options[:notification]
72
+ arg_parts << "-r #{File.dirname(__FILE__)}/formatters/notification_#{rspec_class.downcase}.rb"
73
+ arg_parts << "-f Guard::RSpec::Formatter::Notification#{rspec_class}#{rspec_version == 1 ? ":" : " --out "}/dev/null"
74
+ end
75
+ arg_parts << "--failure-exit-code #{FAILURE_EXIT_CODE}" if failure_exit_code_supported?
76
+ arg_parts << paths.join(' ')
77
+
78
+ arg_parts.compact.join(' ')
79
+ end
27
80
 
81
+ def rspec_command(paths, options)
28
82
  cmd_parts = []
29
- cmd_parts << "rvm #{options[:rvm].join(',')} exec" if options[:rvm].is_a?(Array)
30
- cmd_parts << "bundle exec" if (bundler? && options[:binstubs] == true && options[:bundler] != false) || (bundler? && options[:bundler] != false)
31
- cmd_parts << rspec_exec(options)
32
- cmd_parts << options[:cli] if options[:cli]
33
- cmd_parts << "-f progress" if options[:cli].nil? || !options[:cli].split(/[\s=]/).any? { |w| %w[-f --format].include?(w) }
34
- cmd_parts << "-r #{File.dirname(__FILE__)}/formatters/notification_#{rspec_class.downcase}.rb -f Guard::RSpec::Formatter::Notification#{rspec_class}#{rspec_version == 1 ? ":" : " --out "}/dev/null" if options[:notification] != false
35
- cmd_parts << "--failure-exit-code #{failure_exit_code}" if failure_exit_code_supported?(options)
36
- cmd_parts << paths.join(' ')
83
+ cmd_parts << "rvm #{@options[:rvm].join(',')} exec" if @options[:rvm].respond_to?(:join)
84
+ cmd_parts << "bundle exec" if bundler?
85
+ cmd_parts << rspec_executable
86
+ cmd_parts << rspec_arguments(paths, options)
37
87
 
38
- cmd_parts.join(' ')
88
+ cmd_parts.compact.join(' ')
39
89
  end
40
90
 
41
- def drb?(options)
42
- !options[:cli].nil? && options[:cli].include?('--drb')
91
+ def run_via_shell(paths, options)
92
+ success = system(rspec_command(paths, options))
93
+
94
+ if @options[:notification] && !drb_used? && !success && rspec_command_exited_with_an_exception?
95
+ Notifier.notify("Failed", :title => "RSpec results", :image => :failed, :priority => 2)
96
+ end
97
+
98
+ success
43
99
  end
44
100
 
45
- def bundler?
46
- @bundler ||= File.exist?("#{Dir.pwd}/Gemfile")
101
+ def rspec_command_exited_with_an_exception?
102
+ failure_exit_code_supported? && $?.exitstatus != FAILURE_EXIT_CODE
47
103
  end
48
104
 
49
- def failure_exit_code_supported?(options={})
50
- return @failure_exit_code_supported if defined?(@failure_exit_code_supported)
51
- @failure_exit_code_supported ||= begin
52
- cmd_parts = []
53
- cmd_parts << "bundle exec" if (bundler? && options[:bundler].is_a?(TrueClass)) || (bundler? && options[:binstubs].is_a?(TrueClass))
54
- ( saved = true; options[:binstubs] = false ) if options[:binstubs].is_a?(TrueClass) # failure exit code support is independent of rspec location
55
- cmd_parts << rspec_exec(options)
56
- options[:binstubs] = true if saved
57
- cmd_parts << "--help"
58
- `#{cmd_parts.join(' ')}`.include? "--failure-exit-code"
105
+ # We can optimize this path by hitting up the drb server directly, circumventing the overhead
106
+ # of the user's shell, bundler and ruby environment.
107
+ def run_via_drb(paths, options)
108
+ require "shellwords"
109
+ argv = rspec_arguments(paths, options).shellsplit
110
+
111
+ # The user can specify --drb-port for rspec, we need to honor it.
112
+ if idx = argv.index("--drb-port")
113
+ port = argv[idx + 1].to_i
59
114
  end
115
+ port = ENV["RSPEC_DRB"] || 8989 unless port && port > 0
116
+
117
+ ret = drb_service(port.to_i).run(argv, $stderr, $stdout)
118
+ ret == 0
119
+ rescue DRb::DRbConnError
120
+ # Fall back to the shell runner; we don't want to mangle the environment!
121
+ run_via_shell(paths, options)
60
122
  end
61
123
 
62
- def failure_exit_code
63
- 2
124
+ def drb_used?
125
+ if @drb_used.nil?
126
+ @drb_used = @options[:cli] && @options[:cli].include?('--drb')
127
+ else
128
+ @drb_used
129
+ end
64
130
  end
65
131
 
66
- def determine_rspec_version
67
- if File.exist?("#{Dir.pwd}/spec/spec_helper.rb")
68
- File.new("#{Dir.pwd}/spec/spec_helper.rb").read.include?("Spec::Runner") ? 1 : 2
69
- elsif bundler?
70
- # Allow RSpactor to be tested with RSpactor (bundle show inside a bundle exec)
71
- ENV['BUNDLE_GEMFILE'] = "#{Dir.pwd}/Gemfile"
72
- `bundle show rspec`.include?("/rspec-1.") ? 1 : 2
132
+ # RSpec 1 & 2 use the same DRb call signature, and we can avoid loading a large chunk of rspec
133
+ # just to let DRb know what to do.
134
+ #
135
+ # For reference:
136
+ #
137
+ # * RSpec 1: https://github.com/myronmarston/rspec-1/blob/master/lib/spec/runner/drb_command_line.rb
138
+ # * RSpec 2: https://github.com/rspec/rspec-core/blob/master/lib/rspec/core/drb_command_line.rb
139
+ def drb_service(port)
140
+ require "drb/drb"
141
+
142
+ # Make sure we have a listener running
143
+ unless @drb_listener_running
144
+ begin
145
+ DRb.start_service("druby://localhost:0")
146
+ rescue SocketError, Errno::EADDRNOTAVAIL
147
+ DRb.start_service("druby://:0")
148
+ end
149
+
150
+ @drb_listener_running = true
151
+ end
152
+
153
+ @drb_services ||= {}
154
+ @drb_services[port.to_i] ||= DRbObject.new_with_uri("druby://127.0.0.1:#{port}")
155
+ end
156
+
157
+ def bundler_allowed?
158
+ if @bundler_allowed.nil?
159
+ @bundler_allowed = File.exist?("#{Dir.pwd}/Gemfile")
73
160
  else
74
- 2
161
+ @bundler_allowed
75
162
  end
76
163
  end
77
164
 
78
- def rspec_class
79
- case rspec_version
80
- when 1
81
- "Spec"
82
- when 2
83
- "RSpec"
165
+ def bundler?
166
+ if @bundler.nil?
167
+ @bundler = bundler_allowed? && @options[:bundler]
168
+ else
169
+ @bundler
84
170
  end
85
171
  end
86
172
 
87
- def rspec_exec(options = {})
88
- case rspec_version
89
- when 1
90
- options[:binstubs] == true && options[:bundler] != false ? "bin/spec" : "spec"
91
- when 2
92
- options[:binstubs] == true && options[:bundler] != false ? "bin/rspec" : "rspec"
173
+ def binstubs?
174
+ if @binstubs.nil?
175
+ @binstubs = bundler? && @options[:binstubs]
176
+ else
177
+ @binstubs
93
178
  end
94
179
  end
95
180
 
96
- def warn_deprectation(options={})
97
- [:color, :drb, :fail_fast, [:formatter, "format"]].each do |option|
98
- key, value = option.is_a?(Array) ? option : [option, option.to_s.gsub('_', '-')]
99
- if options.key?(key)
181
+ def determine_rspec_version
182
+ if File.exist?("#{Dir.pwd}/spec/spec_helper.rb")
183
+ File.new("#{Dir.pwd}/spec/spec_helper.rb").read.include?("Spec::Runner") ? 1 : 2
184
+ elsif bundler_allowed?
185
+ ENV['BUNDLE_GEMFILE'] = "#{Dir.pwd}/Gemfile"
186
+ `bundle show rspec`.include?("/rspec-1.") ? 1 : 2
187
+ else
188
+ 2
189
+ end
190
+ end
191
+
192
+ def deprecations_warnings
193
+ [:color, :drb, [:fail_fast, "fail-fast"], [:formatter, "format"]].each do |option|
194
+ key, value = option.is_a?(Array) ? option : [option, option.to_s]
195
+ if @options.key?(key)
196
+ @options.delete(key)
100
197
  UI.info %{DEPRECATION WARNING: The :#{key} option is deprecated. Pass standard command line argument "--#{value}" to RSpec with the :cli option.}
101
198
  end
102
199
  end
103
200
  end
201
+
104
202
  end
105
203
  end
106
204
  end
@@ -1,5 +1,5 @@
1
1
  module Guard
2
2
  module RSpecVersion
3
- VERSION = "0.6.0"
3
+ VERSION = "0.7.0"
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: guard-rspec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-02 00:00:00.000000000 Z
12
+ date: 2012-03-27 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: guard
16
- requirement: &70106639786600 !ruby/object:Gem::Requirement
16
+ requirement: &70179267450520 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 0.10.0
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70106639786600
24
+ version_requirements: *70179267450520
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: bundler
27
- requirement: &70106639786020 !ruby/object:Gem::Requirement
27
+ requirement: &70179267449820 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '1.0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70106639786020
35
+ version_requirements: *70179267449820
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rspec
38
- requirement: &70106639811000 !ruby/object:Gem::Requirement
38
+ requirement: &70179267449300 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,7 +43,7 @@ dependencies:
43
43
  version: '2.7'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70106639811000
46
+ version_requirements: *70179267449300
47
47
  description: Guard::RSpec automatically run your specs (much like autotest).
48
48
  email:
49
49
  - thibaud@thibaud.me
@@ -81,7 +81,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
81
81
  version: 1.3.6
82
82
  requirements: []
83
83
  rubyforge_project: guard-rspec
84
- rubygems_version: 1.8.12
84
+ rubygems_version: 1.8.16
85
85
  signing_key:
86
86
  specification_version: 3
87
87
  summary: Guard gem for RSpec