guard-rspectacle 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.md +214 -0
- data/lib/guard/rspectacle.rb +124 -0
- data/lib/guard/rspectacle/formatter.rb +76 -0
- data/lib/guard/rspectacle/humanity.rb +66 -0
- data/lib/guard/rspectacle/inspector.rb +43 -0
- data/lib/guard/rspectacle/notifier.rb +42 -0
- data/lib/guard/rspectacle/reloader.rb +36 -0
- data/lib/guard/rspectacle/runner.rb +137 -0
- data/lib/guard/rspectacle/templates/Guardfile +17 -0
- data/lib/guard/rspectacle/version.rb +6 -0
- metadata +123 -0
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
|
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:
|