guard-rspectacle 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 - 2012 Michael Kessler
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,214 @@
1
+ # Guard::RSpectacle [![Build Status](https://secure.travis-ci.org/netzpirat/guard-rspectacle.png)](http://travis-ci.org/netzpirat/guard-rspectacle)
2
+
3
+ Guard::RSpectacle automatically tests your application with [RSpec]() when files are modified.
4
+
5
+ Tested on MRI Ruby 1.8.7, 1.9.2, 1.9.3, REE and the latest versions of JRuby & Rubinius.
6
+
7
+ If you have any questions please join us on our [Google group](http://groups.google.com/group/guard-dev) or on `#guard`
8
+ (irc.freenode.net).
9
+
10
+ ## Prove of concept
11
+
12
+ **This is an early stage proof of concept. The idea is that Guard starts the Rails environment, reloads changed Ruby files and starts the RSpec runner embedded in the current process.**
13
+
14
+ ## Install
15
+
16
+ ### Guard and Guard::RSpectacle
17
+
18
+ Please be sure to have [Guard](https://github.com/guard/guard) installed.
19
+
20
+ Add it to your `Gemfile`, preferably inside the development group:
21
+
22
+ gem 'guard-rspectacle', :git => 'git://github.com/netzpirat/guard-rspectacle.git'
23
+
24
+ Add guard definition to your `Guardfile` by running this command:
25
+
26
+ $ guard init rspectacle
27
+
28
+ ## Usage
29
+
30
+ Please read the [Guard usage documentation](https://github.com/guard/guard#readme) for information about Guard.
31
+
32
+ ## Guardfile
33
+
34
+ Guard::RSpectacle can be adapted to all kind of projects. Please read the
35
+ [Guard documentation](https://github.com/guard/guard#readme) for more information about the Guardfile DSL.
36
+
37
+ ```ruby
38
+ guard :rspectacle do
39
+ watch('spec/spec_helper.rb') { %w(spec/spec_helper spec) }
40
+ watch('config/routes.rb') { %w(config/routes.rb spec/routing) }
41
+ watch('app/controllers/application_controller.rb') { 'spec/controllers' }
42
+
43
+ watch(%r{^spec/.+_spec\.rb$})
44
+
45
+ watch(%r{^app/(.+)\.rb$}) { |m| ["app/#{m[1]}.rb", "spec/#{m[1]}_spec.rb"] }
46
+ watch(%r{^lib/(.+)\.rb$}) { |m| ["lib/#{m[1]}.rb", "spec/lib/#{m[1]}_spec.rb"] }
47
+ watch(%r{^app/controllers/(.+)_controller\.rb$}) { |m| [
48
+ "app/controllers/#{m[1]}_controller.rb",
49
+ "spec/controllers/#{m[1]}_spec.rb",
50
+ "spec/routing/#{m[1]}_routing_spec.rb",
51
+ "spec/acceptance/#{m[1]}_spec.rb"
52
+ ]}
53
+ end
54
+ ```
55
+
56
+ **NOTE: When using `watch` with a block, you must return all files that should be reloaded.**
57
+
58
+ ## Options
59
+
60
+ There are many options that can customize Guard::Jasmine to your needs. Options are simply supplied as hash when
61
+ defining the Guard in your `Guardfile`:
62
+
63
+ ```ruby
64
+ guard :rspectacle, :cli => '--format Fuubar --backtrace --tag @focus', :all_on_start => false do
65
+ ...
66
+ end
67
+ ```
68
+
69
+ ### General options
70
+
71
+ The general options configures the environment that is needed to run Guard::RSpectacular and RSpec:
72
+
73
+ ```ruby
74
+ :cli => '--tag @focus' # RSpec CLI options
75
+ # default: ''
76
+ ```
77
+
78
+ ### Spec runner options
79
+
80
+ The spec runner options configures the behavior driven development (or BDD) cycle:
81
+
82
+ ```ruby
83
+ :all_on_start => false # Run all specs on start.
84
+ # default: true
85
+
86
+ :keep_failed => false # Keep failed examples and add them to the next run again.
87
+ # default: true
88
+
89
+ :keep_pending => false # Keep pending examples and add them to the next run again.
90
+ # default: true
91
+
92
+ :all_after_pass => false # Run all specs after all examples have passed again after failing.
93
+ # default: true
94
+ ```
95
+
96
+ ### System notifications options
97
+
98
+ These options affects what system notifications (growl, libnotify or notifu) are shown after a spec run:
99
+
100
+ ```ruby
101
+ :notifications => false # Show success and error notifications.
102
+ # default: true
103
+
104
+ :hide_success => true # Disable successful spec run notification.
105
+ # default: false
106
+ ```
107
+
108
+ ## Important note on reloading
109
+
110
+ The ability to run specs immediately comes at a cost:
111
+
112
+ 1. In your `Guardfile`, you have to specify which files should be reloaded (apart from specs to be executed). But don't
113
+ worry, the default template takes care of it.
114
+ 2. When a file is changed, it is reloaded the Ruby code with
115
+ [Kernel#load](http://ruby-doc.org/core-1.9.3/Kernel.html#method-i-load), which only re-interprets the file.
116
+
117
+ This, for example, means that a method already defined on a class (including `initialize`) will not be removed
118
+ simply by deleting that method from the source code:
119
+
120
+ ```ruby
121
+ class Dinner
122
+ def initialize
123
+ raise "Too early"
124
+ end
125
+ end
126
+ ```
127
+
128
+ The spec that uses this class will fail for the obvious reason. So your first thought may be to just remove `initialize`
129
+ method. But that will not work and you should rewrite the class above:
130
+
131
+ ```ruby
132
+ class Dinner
133
+ def initialize
134
+ super
135
+ end
136
+ end
137
+ ```
138
+
139
+ When you are done testing, restart `guard` to load the file afresh. Unfortunately this inconvenience can't be fixed
140
+ easily (suggest if you know how?). So just keep in mind: **you are monkey-patching within a single `guard` session**.
141
+
142
+ ## Alternatives
143
+
144
+ Please have a look at the rock solid [guard-rspec](https://github.com/guard/guard-rspec). Guard::Rspectacular uses it
145
+ for continuous testing.
146
+
147
+ ## Issues
148
+
149
+ You can report issues and feature requests to [GitHub Issues](https://github.com/netzpirat/guard-rspectacle/issues).
150
+ Try to figure out where the issue belongs to: Is it an issue with Guard itself or with Guard::RSpectacle? Please don't
151
+ ask question in the issue tracker, instead join us in our [Google group](http://groups.google.com/group/guard-dev) or on
152
+ `#guard` (irc.freenode.net).
153
+
154
+ When you file an issue, please try to follow to these simple rules if applicable:
155
+
156
+ * Make sure you run Guard with `bundle exec` first.
157
+ * Add debug information to the issue by running Guard with the `--verbose` option.
158
+ * Add your `Guardfile` and `Gemfile` to the issue.
159
+ * Make sure that the issue is reproducible with your description.
160
+
161
+ ## Development
162
+
163
+ - Documentation hosted at [RubyDoc](http://rubydoc.info/github/guard/guard-rspectacle/master/frames).
164
+ - Source hosted at [GitHub](https://github.com/netzpirat/guard-rspectacle).
165
+
166
+ Pull requests are very welcome! Please try to follow these simple rules if applicable:
167
+
168
+ * Please create a topic branch for every separate change you make.
169
+ * Make sure your patches are well tested.
170
+ * Update the [Yard](http://yardoc.org/) documentation.
171
+ * Update the README.
172
+ * Update the CHANGELOG for noteworthy changes.
173
+ * Please **do not change** the version number.
174
+
175
+ For questions please join us in our [Google group](http://groups.google.com/group/guard-dev) or on
176
+ `#guard` (irc.freenode.net).
177
+
178
+ ## Contributors
179
+
180
+ * [Dmytrii Nagirniak](https://github.com/dnagir)
181
+ * [Felipe Kaufmann](https://github.com/effkay)
182
+
183
+ ## Acknowledgment
184
+
185
+ - [David Chelimsky](https://github.com/dchelimsky) for [RSpec](https://github.com/rspec) to write elegant tests with
186
+ passion.
187
+ - All the authors of the numerous [Guards](https://github.com/guard) available for making the Guard ecosystem so much
188
+ growing and comprehensive.
189
+
190
+ ## License
191
+
192
+ (The MIT License)
193
+
194
+ Copyright (c) 2011 - 2012 Michael Kessler
195
+
196
+ Permission is hereby granted, free of charge, to any person obtaining
197
+ a copy of this software and associated documentation files (the
198
+ 'Software'), to deal in the Software without restriction, including
199
+ without limitation the rights to use, copy, modify, merge, publish,
200
+ distribute, sublicense, and/or sell copies of the Software, and to
201
+ permit persons to whom the Software is furnished to do so, subject to
202
+ the following conditions:
203
+
204
+ The above copyright notice and this permission notice shall be
205
+ included in all copies or substantial portions of the Software.
206
+
207
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
208
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
209
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
210
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
211
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
212
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
213
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
214
+
@@ -0,0 +1,124 @@
1
+ require 'guard'
2
+ require 'guard/guard'
3
+ require 'guard/watcher'
4
+
5
+ module Guard
6
+
7
+ # The RSpecRails guard that gets notifications about the following
8
+ # Guard events: `start`, `stop`, `reload`, `run_all` and `run_on_change`.
9
+ #
10
+ class RSpectacle < Guard
11
+
12
+ autoload :Formatter, 'guard/rspectacle/formatter'
13
+ autoload :Humanity, 'guard/rspectacle/humanity'
14
+ autoload :Inspector, 'guard/rspectacle/inspector'
15
+ autoload :Runner, 'guard/rspectacle/runner'
16
+ autoload :Reloader, 'guard/rspectacle/reloader'
17
+ autoload :Notifier, 'guard/rspectacle/notifier'
18
+
19
+ attr_accessor :last_run_passed, :rerun_examples
20
+
21
+ DEFAULT_OPTIONS = {
22
+ :cli => '',
23
+ :notification => true,
24
+ :hide_success => false,
25
+ :all_on_start => true,
26
+ :keep_failed => true,
27
+ :keep_pending => true,
28
+ :all_after_pass => true,
29
+ }
30
+
31
+ # Initialize Guard::RSpecRails.
32
+ #
33
+ # @param [Array<Guard::Watcher>] watchers the watchers in the Guard block
34
+ # @param [Hash] options the options for the Guard
35
+ # @option options [String] :cli the RSpec CLI options
36
+ # @option options [Boolean] :notification show notifications
37
+ # @option options [Boolean] :hide_success hide success message notification
38
+ # @option options [Boolean] :all_on_start Run all specs on start
39
+ # @option options [Boolean] :keep_failed keep failed examples and add them to the next run again
40
+ # @option options [Boolean] :keep_pending keep pending examples and add them to the next run again
41
+ # @option options [Boolean] :all_after_pass run all specs after all examples have passed again after failing
42
+ #
43
+ def initialize(watchers = [], options = {})
44
+ options = DEFAULT_OPTIONS.merge(options)
45
+
46
+ super(watchers, options)
47
+
48
+ self.last_run_passed = true
49
+ self.rerun_examples = []
50
+ end
51
+
52
+ # Gets called once when Guard starts.
53
+ #
54
+ # @raise [:task_has_failed] when run_on_change has failed
55
+ #
56
+ def start
57
+ ENV['RAILS_ENV'] ||= 'test'
58
+ require './spec/spec_helper'
59
+
60
+ Formatter.info "RSpectacle is ready in #{ ENV['RAILS_ENV'] } environment."
61
+ run_all if options[:all_on_start]
62
+ end
63
+
64
+ # Gets called when the Guard should reload itself.
65
+ #
66
+ # @raise [:task_has_failed] when run_on_change has failed
67
+ #
68
+ def reload
69
+ Dir.glob('**/*.rb').each { |file| Reloader.reload_file(file) }
70
+
71
+ self.last_run_passed = true
72
+ self.rerun_examples = []
73
+ end
74
+
75
+ # Gets called when all specs should be run.
76
+ #
77
+ # @raise [:task_has_failed] when run_on_change has failed
78
+ #
79
+ def run_all
80
+ passed, failed_examples, passed_examples, pending_examples = Runner.run(['spec'], options)
81
+
82
+ if options[:keep_pending]
83
+ self.rerun_examples = failed_examples + pending_examples
84
+ else
85
+ self.rerun_examples = failed_examples
86
+ end
87
+
88
+ self.last_run_passed = passed
89
+
90
+ throw :task_has_failed unless passed
91
+ end
92
+
93
+ # Gets called when watched paths and files have changes.
94
+ #
95
+ # @param [Array<String>] paths the changed paths and files
96
+ # @raise [:task_has_failed] when run_on_change has failed
97
+ #
98
+ def run_on_change(paths)
99
+ specs = Inspector.clean(paths)
100
+ return false if specs.empty?
101
+
102
+ specs += self.rerun_examples if options[:keep_failed]
103
+
104
+ # RSpec reloads the files, so reload only non spec files
105
+ (paths - specs).each { |path| Reloader.reload_file(path) }
106
+
107
+ passed, failed_examples, passed_examples, pending_examples = Runner.run(specs, options)
108
+
109
+ if options[:keep_pending]
110
+ self.rerun_examples += failed_examples + pending_examples
111
+ else
112
+ self.rerun_examples += failed_examples
113
+ end
114
+
115
+ self.rerun_examples -= passed_examples
116
+
117
+ run_all if passed && !self.last_run_passed && options[:all_after_pass]
118
+ self.last_run_passed = passed
119
+
120
+ throw :task_has_failed unless passed
121
+ end
122
+
123
+ end
124
+ end
@@ -0,0 +1,76 @@
1
+ module Guard
2
+ class RSpectacle
3
+
4
+ # The Guard::RSpectacle formatter collects console and
5
+ # system notification methods and enhances them with
6
+ # some color information.
7
+ #
8
+ module Formatter
9
+ class << self
10
+
11
+ # Print an info message to the console.
12
+ #
13
+ # @param [String] message the message to print
14
+ # @param [Hash] options the output options
15
+ # @option options [Boolean] :reset reset the UI
16
+ #
17
+ def info(message, options = { })
18
+ ::Guard::UI.info(message, options)
19
+ end
20
+
21
+ # Print a debug message to the console.
22
+ #
23
+ # @param [String] message the message to print
24
+ # @param [Hash] options the output options
25
+ # @option options [Boolean] :reset reset the UI
26
+ #
27
+ def debug(message, options = { })
28
+ ::Guard::UI.debug(message, options)
29
+ end
30
+
31
+ # Print a red error message to the console.
32
+ #
33
+ # @param [String] message the message to print
34
+ # @param [Hash] options the output options
35
+ # @option options [Boolean] :reset reset the UI
36
+ #
37
+ def error(message, options = { })
38
+ ::Guard::UI.error(color(message, ';31'), options)
39
+ end
40
+
41
+ # Print a green success message to the console.
42
+ #
43
+ # @param [String] message the message to print
44
+ # @param [Hash] options the output options
45
+ # @option options [Boolean] :reset reset the UI
46
+ #
47
+ def success(message, options = { })
48
+ ::Guard::UI.info(color(message, ';32'), options)
49
+ end
50
+
51
+ # Outputs a system notification.
52
+ #
53
+ # @param [String] message the message to print
54
+ # @param [Hash] options the output options
55
+ # @option options [Symbol, String] :image the image to use, either :failed, :pending or :success, or an image path
56
+ # @option options [String] :title the title of the system notification
57
+ #
58
+ def notify(message, options = { })
59
+ ::Guard::Notifier.notify(message, options)
60
+ end
61
+
62
+ private
63
+
64
+ # Print a info message to the console.
65
+ #
66
+ # @param [String] test the text to colorize
67
+ # @param [String] color_code the color code
68
+ #
69
+ def color(text, color_code)
70
+ ::Guard::UI.send(:color_enabled?) ? "\e[0#{ color_code }m#{ text }\e[0m" : text
71
+ end
72
+
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,66 @@
1
+ module Guard
2
+ class RSpectacle
3
+
4
+ # The humanity class helps to bring some randomness
5
+ # into the so boring and static messages from rspectacle.
6
+ #
7
+ class Humanity
8
+ class << self
9
+
10
+ # Picks a random success message.
11
+ #
12
+ # @return [String] a success message
13
+ #
14
+ def success
15
+ pick [
16
+ 'How cool, all works!',
17
+ 'Awesome, all passing!',
18
+ 'Well done, mate!',
19
+ 'You rock!',
20
+ 'Good job!',
21
+ 'Yep!'
22
+ ]
23
+ end
24
+
25
+ # Picks a random pending message.
26
+ #
27
+ # @return [String] a pending message
28
+ #
29
+ def pending
30
+ pick [
31
+ 'Almost there!',
32
+ 'Final spurt!',
33
+ 'One more please!'
34
+ ]
35
+ end
36
+
37
+ # Picks a random failure message.
38
+ #
39
+ # @return [String] a failure message
40
+ #
41
+ def failure
42
+ pick [
43
+ 'Try harder, failing.',
44
+ 'Failing, not there yet...',
45
+ 'Ups, you did it again.',
46
+ 'Nope.',
47
+ 'Still red.'
48
+ ]
49
+ end
50
+
51
+ private
52
+
53
+ # Picks one item from array at random.
54
+ #
55
+ # @param [Array] array of items to pick from.
56
+ # @return [Object] a randomly chosen item from the array
57
+ #
58
+ def pick(items)
59
+ items[rand items.length]
60
+ end
61
+
62
+ end
63
+
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,43 @@
1
+ module Guard
2
+ class RSpectacle
3
+
4
+ # The inspector verifies if the changed paths are valid
5
+ # for Guard::RSpectacle.
6
+ #
7
+ module Inspector
8
+ class << self
9
+
10
+ # Clean the changed paths and return only valid
11
+ # RSpec specs.
12
+ #
13
+ # @param [Array<String>] paths the changed paths
14
+ # @return [Array<String>] the valid spec files
15
+ #
16
+ def clean(paths)
17
+ paths.uniq!
18
+ paths.compact!
19
+
20
+ if paths.include?('spec')
21
+ paths = ['spec']
22
+ else
23
+ paths = paths.select { |p| rspec_spec?(p) }
24
+ end
25
+
26
+ paths
27
+ end
28
+
29
+ private
30
+
31
+ # Tests if the file is valid.
32
+ #
33
+ # @param [String] file the file
34
+ # @return [Boolean] when the file valid
35
+ #
36
+ def rspec_spec?(path)
37
+ path =~ /_spec\.rb$/ && File.exists?(path)
38
+ end
39
+
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,42 @@
1
+ require 'rspec/core/formatters/base_formatter'
2
+
3
+ module Guard
4
+ class RSpectacle
5
+
6
+ # Simple RSpec formatter that just stores the last spec run result
7
+ # at class level.
8
+ #
9
+ class Notifier < ::RSpec::Core::Formatters::BaseFormatter
10
+
11
+ class << self
12
+ attr_accessor :duration
13
+ attr_accessor :example_count
14
+ attr_accessor :failure_count
15
+ attr_accessor :pending_count
16
+ attr_accessor :failed_examples
17
+ attr_accessor :pending_examples
18
+ attr_accessor :passed_examples
19
+ end
20
+
21
+ def initialize(output)
22
+ super
23
+ @passed_examples = []
24
+ end
25
+
26
+ def example_passed(example)
27
+ @passed_examples << example
28
+ end
29
+
30
+ def dump_summary(duration, example_count, failure_count, pending_count)
31
+ ::Guard::RSpectacle::Notifier.duration = duration
32
+ ::Guard::RSpectacle::Notifier.example_count = example_count
33
+ ::Guard::RSpectacle::Notifier.failure_count = failure_count
34
+ ::Guard::RSpectacle::Notifier.pending_count = pending_count
35
+ ::Guard::RSpectacle::Notifier.failed_examples = @failed_examples.map { |example| example.location }
36
+ ::Guard::RSpectacle::Notifier.pending_examples = @pending_examples.map { |example| example.location }
37
+ ::Guard::RSpectacle::Notifier.passed_examples = @passed_examples.map { |example| example.location }
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,36 @@
1
+ module Guard
2
+ class RSpectacle
3
+
4
+ # The reloader class handles reloading of changed
5
+ # files.
6
+ #
7
+ class Reloader
8
+ class << self
9
+
10
+ # Reloads the given file.
11
+ #
12
+ # @param [String] file the changed file
13
+ # @return [Boolean] the load status
14
+ # @raise [:task_has_failed] when run_on_change has failed
15
+ #
16
+ def reload_file(file)
17
+ return false unless file =~ /\.rb$/
18
+
19
+ if File.exists?(file)
20
+ Formatter.info "Reload #{ file }"
21
+ load file
22
+
23
+ else
24
+ false
25
+ end
26
+
27
+ rescue Exception => e
28
+ Formatter.error "Error reloading file #{ file }: #{ e.message }"
29
+
30
+ throw :task_has_failed
31
+ end
32
+
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,137 @@
1
+ # coding: utf-8
2
+
3
+ require 'rspec/core/runner'
4
+
5
+ module Guard
6
+ class RSpectacle
7
+
8
+ # The RSpectacle runner handles the execution of the rspec test.
9
+ #
10
+ module Runner
11
+
12
+ class << self
13
+
14
+ # Run a suite of RSpec examples.
15
+ #
16
+ # For reference, see:
17
+ # - https://github.com/rspec/rspec-core/blob/master/lib/rspec/core/runner.rb
18
+ # - https://github.com/rspec/rspec-core/blob/master/spec/rspec/core/configuration_options_spec.rb
19
+ #
20
+ # @param [Array<String>] files the specs to run
21
+ # @param [Hash] options the options
22
+ # @option options [String] :cli the RSpec CLI options
23
+ # @option options [Boolean] :notification show notifications
24
+ # @option options [Boolean] :hide_success hide success message notification
25
+ # @param [IO] err the error stream
26
+ # @param [IO] out the output stream
27
+ # @return [Array] the spec result: status, failed_examples, passed_examples, pending_examples
28
+ #
29
+ def run(files, options, err=$stderr, out=$stdout)
30
+ rspec_options = options[:cli].to_s.split
31
+ rspec_options.delete('--drb')
32
+ rspec_options += rspectacular_options + files
33
+
34
+ begin
35
+ status = ::RSpec::Core::Runner.run(rspec_options, err, out)
36
+
37
+ passed = status == 0
38
+ failed_examples = ::Guard::RSpectacle::Notifier.failed_examples || []
39
+ pending_examples = ::Guard::RSpectacle::Notifier.pending_examples || []
40
+ passed_examples = ::Guard::RSpectacle::Notifier.passed_examples || []
41
+ duration = ::Guard::RSpectacle::Notifier.duration || 0.0
42
+ example_count = ::Guard::RSpectacle::Notifier.example_count || -1
43
+ failure_count = ::Guard::RSpectacle::Notifier.failure_count || -1
44
+ pending_count = ::Guard::RSpectacle::Notifier.pending_count || -1
45
+
46
+ if options[:notification]
47
+
48
+ message = " #{ example_count } example#{ example_count == 1 ? '' : 's' }"
49
+ message << ", #{ failure_count } failure#{ failure_count == 1 ? '' : 's' }"
50
+ message << " (#{ pending_count } pending)" if pending_count > 0
51
+ message << "\nin #{ round(duration) } seconds"
52
+
53
+ if failure_count == 0 && pending_count == 0
54
+ ::Guard::RSpectacle::Formatter.notify(::Guard::RSpectacle::Humanity.success + message,
55
+ :title => 'RSpec results',
56
+ :image => :success,
57
+ :priority => 2) if !options[:hide_success]
58
+
59
+ elsif failure_count == 0 && pending_count > 0
60
+ ::Guard::RSpectacle::Formatter.notify(::Guard::RSpectacle::Humanity.pending + message,
61
+ :title => 'RSpec results',
62
+ :image => :pending,
63
+ :priority => -1)
64
+
65
+ else
66
+ ::Guard::RSpectacle::Formatter.notify(::Guard::RSpectacle::Humanity.failure + message,
67
+ :title => 'RSpec results',
68
+ :image => :failed,
69
+ :priority => -2)
70
+ end
71
+ end
72
+
73
+ [passed, relative(failed_examples), relative(passed_examples), relative(pending_examples)]
74
+
75
+ rescue Exception => e
76
+ ::Guard::RSpectacle::Formatter.error("Error running specs: #{ e.message }")
77
+
78
+ [false, [], [], []]
79
+ end
80
+ end
81
+
82
+ private
83
+
84
+ # Make all the paths relative to the current working
85
+ # directory (the project dir).
86
+ #
87
+ # @paramn [Array<String>] the absolute paths
88
+ # @return [Array<String>] the relative paths
89
+ #
90
+ def relative(paths)
91
+ paths.map { |path| path.gsub(Dir.pwd + '/', '') }
92
+ end
93
+
94
+ # Returns the RSpec options needed to run RSpectacular.
95
+ #
96
+ # @return [Array<String>] the cli options
97
+ #
98
+ def rspectacular_options
99
+ options = []
100
+
101
+ options << '--require'
102
+ options << "#{ File.dirname(__FILE__) }/notifier.rb"
103
+ options << '--format'
104
+ options << 'Guard::RSpectacle::Notifier'
105
+ options << '--out'
106
+ options << null_device
107
+
108
+ options
109
+ end
110
+
111
+ # Returns a null device for all OS.
112
+ #
113
+ # @return [String] the name of the null device
114
+ #
115
+ def null_device
116
+ RUBY_PLATFORM.index('mswin') ? 'NUL' : '/dev/null'
117
+ end
118
+
119
+ # Round the float.
120
+ #
121
+ # @param [Float] float the number
122
+ # @param [Integer] decimals the decimals to round to
123
+ # @return [Float] the rounded float
124
+ #
125
+ def round(float, decimals=4)
126
+ if Float.instance_method(:round).arity == 0 # Ruby 1.8
127
+ factor = 10**decimals
128
+ (float*factor).round / factor.to_f
129
+ else # Ruby 1.9
130
+ float.round(decimals)
131
+ end
132
+ end
133
+ end
134
+
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,17 @@
1
+ # NOTE: When using watch with a block, you must return all files that should be reloaded.
2
+ guard :rspectacle do
3
+ watch('spec/spec_helper.rb') { %w(spec/spec_helper spec) }
4
+ watch('config/routes.rb') { %w(config/routes.rb spec/routing) }
5
+ watch('app/controllers/application_controller.rb') { 'spec/controllers' }
6
+
7
+ watch(%r{^spec/.+_spec\.rb$})
8
+
9
+ watch(%r{^app/(.+)\.rb$}) { |m| ["app/#{m[1]}.rb", "spec/#{m[1]}_spec.rb"] }
10
+ watch(%r{^lib/(.+)\.rb$}) { |m| ["lib/#{m[1]}.rb", "spec/lib/#{m[1]}_spec.rb"] }
11
+ watch(%r{^app/controllers/(.+)_controller\.rb$}) { |m| [
12
+ "app/controllers/#{m[1]}_controller.rb",
13
+ "spec/controllers/#{m[1]}_spec.rb",
14
+ "spec/routing/#{m[1]}_routing_spec.rb",
15
+ "spec/acceptance/#{m[1]}_spec.rb"
16
+ ]}
17
+ end
@@ -0,0 +1,6 @@
1
+ module Guard
2
+ module RSpectacleVersion
3
+ # Guard::Spec version that is used for the Gem specification
4
+ VERSION = '0.1.0'
5
+ end
6
+ end
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: guard-rspectacle
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Michael Kessler
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-01-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: guard
16
+ requirement: &70328220924200 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0.8'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70328220924200
25
+ - !ruby/object:Gem::Dependency
26
+ name: rspec
27
+ requirement: &70328220923740 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: 2.8.0
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70328220923740
36
+ - !ruby/object:Gem::Dependency
37
+ name: bundler
38
+ requirement: &70328220923240 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ~>
42
+ - !ruby/object:Gem::Version
43
+ version: '1.0'
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: *70328220923240
47
+ - !ruby/object:Gem::Dependency
48
+ name: guard-rspec
49
+ requirement: &70328220907740 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '0.6'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70328220907740
58
+ - !ruby/object:Gem::Dependency
59
+ name: yard
60
+ requirement: &70328220907360 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70328220907360
69
+ - !ruby/object:Gem::Dependency
70
+ name: kramdown
71
+ requirement: &70328220906880 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70328220906880
80
+ description: Guard::RSpectacle automatically tests your code with RSpec
81
+ email:
82
+ - michi@netzpiraten.ch
83
+ executables: []
84
+ extensions: []
85
+ extra_rdoc_files: []
86
+ files:
87
+ - lib/guard/rspectacle/formatter.rb
88
+ - lib/guard/rspectacle/humanity.rb
89
+ - lib/guard/rspectacle/inspector.rb
90
+ - lib/guard/rspectacle/notifier.rb
91
+ - lib/guard/rspectacle/reloader.rb
92
+ - lib/guard/rspectacle/runner.rb
93
+ - lib/guard/rspectacle/templates/Guardfile
94
+ - lib/guard/rspectacle/version.rb
95
+ - lib/guard/rspectacle.rb
96
+ - LICENSE
97
+ - README.md
98
+ homepage: http://github.com/netzpirat/guard-rspectacle
99
+ licenses: []
100
+ post_install_message:
101
+ rdoc_options: []
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ required_rubygems_version: !ruby/object:Gem::Requirement
111
+ none: false
112
+ requirements:
113
+ - - ! '>='
114
+ - !ruby/object:Gem::Version
115
+ version: 1.3.6
116
+ requirements: []
117
+ rubyforge_project: guard-rspec
118
+ rubygems_version: 1.8.10
119
+ signing_key:
120
+ specification_version: 3
121
+ summary: Guard gem for RSpec testing
122
+ test_files: []
123
+ has_rdoc: