rspec-rails-watchr-emacs 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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: []