guard-rspec 0.6.0 → 0.7.0

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.
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