rspec-rails-watchr-emacs 0.9.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/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'http://rubygems.org'
2
+
3
+ # See gem's dependencies in: rspec-rails-watchr.gemspec
4
+ gemspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Elia Schito
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,122 @@
1
+ # ESpectator (a fork of [Spectator][spectator] that integrates with emacs)
2
+
3
+ ESpectator provides discreet notifications in the emacs modeline, via
4
+ the [Enotify][enotify] emacs notification system.
5
+
6
+ Test results are displayed in an emacs buffer, so no more switching
7
+ between emacs and the window were the test results are displayed are
8
+ necessary :-)
9
+
10
+ If you hate growl-style popups and prefer a simple indicator on the
11
+ modeline, ESpectator is for you. It's best used together with
12
+ [RSpec Org Formatter][RSpecOrgFormatter], which provides org formatted
13
+ test results that do look nice on emacs.
14
+
15
+ ## Usage
16
+
17
+ ### Emacs side
18
+
19
+ You need to install [Enotify][enotify] first. Please refer to its
20
+ README for this step.
21
+
22
+ Copy the emacs/enotify-espectator.el file in a directory in your
23
+ load-path and add this line after the enotify configuration in your
24
+ .emacs:
25
+
26
+ ```lisp
27
+ (require 'enotify-espectator)
28
+ ```
29
+
30
+ Note that enotify uses the TCP port 5000 to listen to notification
31
+ messages. If you specified a different port, refer to the ``Advanced''
32
+ section of this document to see how to specify various ESpectator
33
+ options
34
+
35
+ ### Watchr
36
+
37
+ In your specs.watchr file just add:
38
+
39
+ ```ruby
40
+ require 'rspec-rails-watchr-emacs'
41
+ @specs_watchr ||= Rspec::Rails::Watchr.new(self)
42
+ ```
43
+
44
+ Then launch `watchr` as usual (probably `bundle exec watchr`). If you
45
+ are using RspecOrgFormatter, see the *Advanced* section of this
46
+ document.
47
+
48
+ ### Instructions
49
+
50
+ The normal behavior is similar to `autotest --fast-start
51
+ --no-full-after-failed` but gives the user a bit more control over
52
+ execution. By hitting CTRL+C (or CMD+. on OSX) you get the following
53
+ prompt:
54
+
55
+ ^C (Interrupted with CTRL+C)
56
+ --- What to do now? (q=quit, a=all-specs, r=reload):
57
+
58
+ ### Advanced
59
+
60
+ ESpectator supports the following options (here reported with their
61
+ default values):
62
+ ```ruby
63
+ { :enotify_port => 5000, # TCP port for the enotify connection
64
+ :enotify_host => 'localhost', # host name for the enotify connection
65
+ :notification_message => { # Text displayed on the modeline when
66
+ :failure => "F", # there is at least 1 failing spec
67
+ :success => "S", # there are no failing or pending spec
68
+ :pending => "P" # there are no failing spec and at least 1 pending
69
+ },
70
+ :notification_face => { # Face used to display the text in the modeline
71
+ :failure => keyword(:failure),
72
+ :success => keyword(:success),
73
+ :pending => keyword(:warning)},
74
+ #
75
+ # custom_extract_summary_proc: takes the result text as argument
76
+ # and returns an hash of the form
77
+ # {:errors => #errors
78
+ # :pending => #pending
79
+ # :examples => #examples
80
+ # :status => (:success|:failure|:pending) }
81
+ :custom_extract_summary_proc => nil,
82
+ #
83
+ # index of the Rspec summary line.
84
+ # It should look like this:
85
+ # 25 examples, 2 failures, 1 pending
86
+ :error_count_line => -1,
87
+ #
88
+ # A proc that takes two arguments |path, specs|
89
+ # where path is the file that has been modified
90
+ # and specs is a vector containing all the spec
91
+ # files.
92
+ # It should return a vector containing the matching
93
+ # specs for `path'.
94
+ :custom_matcher => nil
95
+ }
96
+ ```
97
+ An example of a custom matcher:
98
+
99
+ ```ruby
100
+ @specs_watchr ||= Rspec::Rails::Watchr.new(self,
101
+ :custom_matcher => lambda { |path, specs|
102
+ case path
103
+ when %r{lib/calibration_with_coefficients}
104
+ specs.grep(%r{models/(logarithmic|polynomial)_calibration})
105
+ when %r{app/models/telemetry_parameter}
106
+ specs.grep(%r{models/telemetry_parameter})
107
+ end
108
+ })
109
+ ```
110
+
111
+ To use it with the [RSpec Org Formatter][RSpecOrgFormatter], the
112
+ :error_count_line option should be set to -6:
113
+ ```ruby
114
+ @specs_watchr ||= Rspec::Rails::Watchr.new(self, :error_count_line => -6)
115
+ ```
116
+
117
+
118
+ Copyright (c) 2012 Alessandro Piras, 2011 Elia Schito, released under the MIT license
119
+
120
+ [enotify]:https://github.com/laynor/enotify
121
+ [RSpecOrgFormatter]:https://github.com/laynor/rspec_org_formatter
122
+ [spectator]:https://github.com/elia/spectator
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
@@ -0,0 +1,29 @@
1
+ ;;;; Espectator plugin for enotify
2
+ (require 'enotify)
3
+
4
+ (defun enotify-rspec-result-buffer-name (id)
5
+ (format "*RSpec Results: %s*" id))
6
+
7
+ (defun enotify-rspec-result-message-handler (id data)
8
+ (let ((buf (get-buffer-create (enotify-rspec-result-buffer-name id))))
9
+ (save-current-buffer
10
+ (set-buffer buf)
11
+ (erase-buffer)
12
+ (insert data)
13
+ (flet ((message (&rest args) (apply 'format args)))
14
+ (org-mode)))))
15
+
16
+
17
+
18
+ (defvar enotify-rspec-result-message-handler 'enotify-rspec-result-message-handler)
19
+
20
+ (defun enotify-rspec-mouse-1-handler (event)
21
+ (interactive "e")
22
+ (switch-to-buffer-other-window
23
+ (enotify-rspec-result-buffer-name
24
+ (enotify-event->slot-id event))))
25
+
26
+ (defvar enotify-rspec-mouse-1-handler 'enotify-rspec-mouse-1-handler)
27
+
28
+ (provide 'enotify-espectator)
29
+
@@ -0,0 +1,364 @@
1
+ # coding: utf-8)
2
+
3
+ require 'rspec-rails-watchr-emacs/version'
4
+ require 'term/ansicolor'
5
+
6
+ class SpecWatchr
7
+ String.send :include, Term::ANSIColor
8
+
9
+
10
+ module CommandLine
11
+ def terminal_columns
12
+ cols = `stty -a`.scan(/ (\d+) columns/).flatten.first
13
+ $?.success? ? cols.to_i : nil
14
+ end
15
+
16
+ def run cmd
17
+ puts "=== running: #{cmd} ".ljust(terminal_columns, '=').cyan
18
+ results = `#{cmd}`
19
+ success = $?.success?
20
+ unless @custom_extract_summary_proc
21
+ puts " " + results.split("\n")[@error_count_line].strip.send(success ? :green : :red)
22
+ end
23
+ puts "===".ljust(terminal_columns, '=').cyan
24
+ # {:success => success, :results => message}
25
+ results
26
+ end
27
+
28
+ def clear!
29
+ system 'clear'
30
+ end
31
+ end
32
+
33
+ module EmacsConnection
34
+ def alist (hash)
35
+ hash.merge(:magic_convert_to => :alist)
36
+ end
37
+ def flatten (hash)
38
+ hash.merge(:magic_convert_to => :flat)
39
+ end
40
+ def keyword (symbol)
41
+ :"#{symbol.inspect}"
42
+ end
43
+ def elispify_symbol(symbol)
44
+ symbol.to_s.gsub(/_/,'-')
45
+ end
46
+ def hash_to_esexp (hash)
47
+ h = hash.clone
48
+ h.delete(:magic_convert_to)
49
+ case hash[:magic_convert_to]
50
+ when :alist
51
+ res = h.map { |k, v| "(#{object_to_esexp k} . #{object_to_esexp v})" }
52
+ "(#{res.join(' ')})"
53
+ when :flat
54
+ res = h.map { |k, v| "#{object_to_esexp k} #{object_to_esexp v}" }
55
+ "(#{res.join(' ')})"
56
+ else
57
+ if hash.keys.reduce(true) { |base, el| base && Symbol === el }
58
+ res = h.map { |k, v| "#{object_to_esexp keyword(k)} #{object_to_esexp v}" }
59
+ "(#{res.join(' ')})"
60
+ else
61
+ h[:magic_convert_to] = :alist
62
+ hash_to_esexp h
63
+ end
64
+ end
65
+ end
66
+ def object_to_esexp (object)
67
+ case object
68
+ when String
69
+ object.inspect
70
+ when Array
71
+ res = object.map { |el| object_to_esexp(el) }
72
+ "(#{res.join(' ')})"
73
+ when Symbol
74
+ elispify_symbol(object)
75
+ when Hash
76
+ hash_to_esexp object
77
+ else
78
+ object.to_s
79
+ end
80
+ end
81
+ def esend (object)
82
+ msg = object_to_esexp object
83
+ @sock.puts("|#{msg.length}|#{msg}")
84
+ # @sock.print("|#{msg.length}|")
85
+ # sleep 2
86
+ # @sock.puts msg
87
+ end
88
+
89
+ def rspec_status(err_cnt)
90
+ if err_cnt[:errors] > 0
91
+ :failure
92
+ elsif err_cnt[:pending] > 0
93
+ :pending
94
+ else
95
+ :success
96
+ end
97
+ end
98
+
99
+ def extract_rspec_counts(results, line)
100
+ err_line = results.split("\n")[line]
101
+ err_regex = /^(\d*)\sexamples?,\s(\d*)\s(errors?|failures?)[^\d]*((\d*)\spending)?/
102
+ _, examples, errors, _, pending = (err_line.match err_regex).to_a
103
+ summ = { :examples => examples.to_i, :errors => errors.to_i, :pending => pending.to_i }
104
+ summ.merge(:status => rspec_status(summ))
105
+ end
106
+
107
+ def extract_rspec_summary(results)
108
+ case @custom_extract_summary_proc
109
+ when Proc
110
+ @custom_extract_summary_proc.call(results)
111
+ else
112
+ begin
113
+ extract_rspec_counts(results, @error_count_line)
114
+ rescue
115
+ puts "--- Error while matching error counts.".red
116
+ print "--- Summary line number: ".yellow
117
+ @error_count_line = STDIN.gets.to_i
118
+ extract_rspec_summary results
119
+ end
120
+ end
121
+ end
122
+
123
+ def format_help(summary)
124
+ h = "#{summary[:errors]} errors\n"
125
+ h << ("#{summary[:pending]} pending\n" if summary[:pending]>0).to_s
126
+ h << "\nmouse-1: switch to result buffer"
127
+ end
128
+
129
+
130
+ def eregister
131
+ esend :register => @enotify_slot_id, :handler_fn => :enotify_rspec_result_message_handler
132
+ end
133
+
134
+
135
+
136
+ def esend_results(results)
137
+ summ = extract_rspec_summary(results)
138
+ status = summ[:status]
139
+ message = { :id => @enotify_slot_id,
140
+ :notification => {
141
+ :text => @notification_message[status],
142
+ :face => @notification_face[status],
143
+ :help => format_help(summ),
144
+ :mouse_1 => :enotify_rspec_mouse_1_handler},
145
+ :data => results,
146
+ }
147
+ esend message
148
+ end
149
+ end
150
+
151
+
152
+ module Specs
153
+
154
+ def rspec_command
155
+ @rspec_command ||= File.exist?('./.rspec') ? 'rspec' : 'spec'
156
+ end
157
+
158
+ def rspec_send_results(results)
159
+ begin
160
+ print "--- Sending notification to #{@enotify_host}:#{@enotify_port}" \
161
+ " through #{@enotify_slot_id}... ".cyan
162
+ esend_results results
163
+ puts "Success!".green
164
+ rescue
165
+ puts "Failed!".red
166
+ init_network
167
+ rspec_send_results results
168
+ end
169
+ end
170
+
171
+ def check_if_bundle_needed
172
+ if `bundle exec #{rspec_command} -v` == `#{rspec_command} -v`
173
+ @bundle = ""
174
+ else
175
+ @bundle = "bundle exec "
176
+ end
177
+ end
178
+
179
+ def rspec options
180
+ unless options.empty?
181
+ results = run("#{@bundle}#{rspec_command} #{options}")
182
+ # notify( success ? '♥♥ SUCCESS :) ♥♥' : '♠♠ FAILED >:( ♠♠' )
183
+ rspec_send_results(results)
184
+ end
185
+ end
186
+
187
+ def rspec_all
188
+ rspec 'spec'
189
+ end
190
+
191
+ def rspec_files *files
192
+ rspec files.join(' ')
193
+ end
194
+
195
+ def specs_for(path)
196
+ print "--- Searching specs for #{path.inspect}...".yellow
197
+ specs = match_specs path, Dir['spec/**/*_spec.rb']
198
+ puts specs.empty? ? ' nothing found.'.red : " #{specs.size} matched.".green
199
+ specs
200
+ end
201
+
202
+ def default_rails_matcher path, specs
203
+ specs.grep(/\b#{path}((_spec)?\.rb)?$/)
204
+ end
205
+
206
+ def match_specs path, specs
207
+ matched_specs = @custom_matcher.call(path, specs) if @custom_matcher
208
+ matched_specs = default_rails_matcher(path, specs) if matched_specs.nil?
209
+ end
210
+ end
211
+
212
+ module Control
213
+ def exit_watchr
214
+ @exiting = true
215
+ puts '--- Exiting...'.white
216
+ exit
217
+ end
218
+
219
+ def abort_watchr!
220
+ puts '--- Forcing abort...'.white
221
+ abort("\n")
222
+ end
223
+
224
+ def reload!
225
+ # puts ARGV.join(' ')
226
+ exec('bundle exec watchr')
227
+ end
228
+
229
+ def reload_file_list
230
+ require 'shellwords'
231
+ system "touch #{__FILE__.shellescape}"
232
+ # puts '--- Watch\'d file list reloaded.'.green
233
+ end
234
+
235
+ def trap_int!
236
+ # Ctrl-C
237
+
238
+ @interrupted ||= false
239
+
240
+ Signal.trap('INT') {
241
+ puts ' (Interrupted with CTRL+C)'.red
242
+ if @interrupted
243
+ @exiting ? abort_watchr : exit_watchr
244
+ else
245
+ @interrupted = true
246
+ # reload_file_list
247
+ print '--- What to do now? (q=quit, a=all-specs, r=reload): '.yellow
248
+ case STDIN.gets.chomp.strip.downcase
249
+ when 'q'; @interrupted = false; exit_watchr
250
+ when 'a'; @interrupted = false; rspec_all
251
+ when 'r'; @interrupted = false; reload!
252
+ else
253
+ @interrupted = false
254
+ puts '--- Bad input, ignored.'.yellow
255
+ end
256
+ puts '--- Waiting for changes...'.cyan
257
+ end
258
+ }
259
+ end
260
+ end
261
+
262
+
263
+
264
+ include CommandLine
265
+ include Specs
266
+ include Control
267
+ include EmacsConnection
268
+
269
+ def blank_string?(string)
270
+ string =~ /\A\s*\n?\z/
271
+ end
272
+
273
+ def rescue_sock_error
274
+ print "--- Enter Enotify host [localhost:5000]: ".yellow
275
+ host_and_port = STDIN.gets.strip
276
+ if blank_string?(host_and_port)
277
+ @enotify_host, @enotify_port = ['localhost', @default_options[:enotify_port]]
278
+ else
279
+ @enotify_host, @enotify_port = host_and_port.split(/\s:\s/)
280
+ @enotify_port = @enotify_port.to_i
281
+ end
282
+ init_network
283
+ end
284
+
285
+ def init_network
286
+ begin
287
+ print "=== Connecting to emacs... ".cyan
288
+ @sock = TCPSocket.new(@enotify_host, @enotify_port)
289
+ eregister
290
+ puts "Success!".green
291
+ rescue
292
+ puts "Failed!".red
293
+ rescue_sock_error
294
+ end
295
+ end
296
+
297
+ def initialize watchr, options = {}
298
+ @default_options = {
299
+ :enotify_port => 5000,
300
+ :enotify_host => 'localhost',
301
+ :notification_message => {:failure => "F", :success => "S", :pending => "P"},
302
+ :notification_face => {
303
+ :failure => keyword(:failure),
304
+ :success => keyword(:success),
305
+ :pending => keyword(:warning)},
306
+ # custom_extract_summary_proc: takes the result text as argument
307
+ # and returns an hash of the form
308
+ # {:errors => #errors
309
+ # :pending => #pending
310
+ # :examples => #examples
311
+ # :status => (:success|:failure|:pending) }
312
+ :custom_extract_summary_proc => nil,
313
+ :error_count_line => -1,
314
+
315
+ # custom_matcher : takes two arguments: the path of the modified
316
+ # file (CHECK) and an array of spec files. Returns an array of
317
+ # matching spec files for the path given.
318
+ :custom_matcher => nil }
319
+
320
+ options = @default_options.merge(options)
321
+ puts "========OPTIONS=========="
322
+ puts options
323
+ puts "========================="
324
+ @enotify_host = options[:enotify_host]
325
+ @enotify_port = options[:enotify_port]
326
+ @notification_message = options[:notification_message]
327
+ @notification_face = options[:notification_face]
328
+ @custom_extract_summary_proc = options[:custom_extract_summary_proc]
329
+ @error_count_line = options[:error_count_line]
330
+
331
+ @custom_matcher = options[:custom_matcher]
332
+
333
+ yield if block_given?
334
+ @enotify_slot_id = ((File.basename Dir.pwd).split('_').map { |s| s.capitalize }).join
335
+ check_if_bundle_needed
336
+ init_network
337
+ @watchr = watchr
338
+
339
+
340
+
341
+
342
+
343
+
344
+ watchr.watch('^spec/(.*)_spec\.rb$') {|m| rspec_files specs_for(m[1])}
345
+ watchr.watch('^(?:app|lib|script)/(.*)(?:\.rb|\.\w+|)$') {|m| rspec_files specs_for(m[1].gsub(/\.rb$/,''))}
346
+
347
+ trap_int!
348
+
349
+ puts '--- Waiting for changes...'.cyan
350
+ end
351
+ end
352
+
353
+
354
+ class Object
355
+ module Rspec
356
+ module Rails
357
+ module Watchr
358
+ def self.new *args, &block
359
+ SpecWatchr.new *args, &block
360
+ end
361
+ end
362
+ end
363
+ end
364
+ end
@@ -0,0 +1,7 @@
1
+ module Rspec
2
+ module Rails
3
+ module Watchr
4
+ VERSION = '0.9.0'
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rspec-rails-watchr-emacs/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = 'rspec-rails-watchr-emacs'
7
+ s.version = Rspec::Rails::Watchr::VERSION
8
+ s.authors = %w[Alessandro Piras]
9
+ s.email = %w[laynor@gmail.com]
10
+ s.homepage = 'https://github.com/laynor/spectator'
11
+ s.summary = %q{Watches specs for a Rails (2 or 3) project - notifications via Emacs enotify}
12
+ s.description = %q{Watches specs for a Rails (2 or 3) project - notifications via Emacs enotify. Fork of rspec-rails-watchr (spectator)}
13
+ s.license = 'MIT'
14
+
15
+ s.files = `git ls-files`.split("\n")
16
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ s.require_paths = %w[lib]
19
+
20
+ s.add_dependency 'watchr'
21
+ s.add_dependency 'term-ansicolor'
22
+ s.add_dependency 'notify'
23
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rspec-rails-watchr-emacs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Alessandro
9
+ - Piras
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-01-12 00:00:00.000000000Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: watchr
17
+ requirement: &76086330 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *76086330
26
+ - !ruby/object:Gem::Dependency
27
+ name: term-ansicolor
28
+ requirement: &76083960 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *76083960
37
+ - !ruby/object:Gem::Dependency
38
+ name: notify
39
+ requirement: &76083660 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :runtime
46
+ prerelease: false
47
+ version_requirements: *76083660
48
+ description: Watches specs for a Rails (2 or 3) project - notifications via Emacs
49
+ enotify. Fork of rspec-rails-watchr (spectator)
50
+ email:
51
+ - laynor@gmail.com
52
+ executables: []
53
+ extensions: []
54
+ extra_rdoc_files: []
55
+ files:
56
+ - .gitignore
57
+ - Gemfile
58
+ - MIT-LICENSE
59
+ - README.md
60
+ - Rakefile
61
+ - emacs/enotify-espectator.el
62
+ - lib/rspec-rails-watchr-emacs.rb
63
+ - lib/rspec-rails-watchr-emacs/version.rb
64
+ - rspec-rails-watchr-emacs.gemspec
65
+ homepage: https://github.com/laynor/spectator
66
+ licenses:
67
+ - MIT
68
+ post_install_message:
69
+ rdoc_options: []
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ! '>='
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ requirements: []
85
+ rubyforge_project:
86
+ rubygems_version: 1.8.5
87
+ signing_key:
88
+ specification_version: 3
89
+ summary: Watches specs for a Rails (2 or 3) project - notifications via Emacs enotify
90
+ test_files: []