guard 1.0.3 → 1.1.0.alpha

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.
Files changed (77) hide show
  1. data/CHANGELOG.md +0 -6
  2. data/README.md +38 -30
  3. data/lib/guard.rb +158 -285
  4. data/lib/guard/cli.rb +16 -2
  5. data/lib/guard/dsl.rb +41 -20
  6. data/lib/guard/dsl_describer.rb +1 -1
  7. data/lib/guard/group.rb +1 -1
  8. data/lib/guard/guard.rb +39 -5
  9. data/lib/guard/guardfile.rb +70 -0
  10. data/lib/guard/runner.rb +179 -0
  11. data/lib/guard/ui.rb +1 -1
  12. data/lib/guard/version.rb +2 -4
  13. data/lib/guard/watcher.rb +1 -0
  14. metadata +16 -77
  15. data/bin/fsevent_watch_guard_guard +0 -0
  16. data/lib/guard/listener.rb +0 -376
  17. data/lib/guard/listeners/darwin.rb +0 -62
  18. data/lib/guard/listeners/linux.rb +0 -93
  19. data/lib/guard/listeners/polling.rb +0 -55
  20. data/lib/guard/listeners/windows.rb +0 -63
  21. data/lib/vendor/darwin/Gemfile +0 -6
  22. data/lib/vendor/darwin/Guardfile +0 -8
  23. data/lib/vendor/darwin/LICENSE +0 -20
  24. data/lib/vendor/darwin/README.rdoc +0 -255
  25. data/lib/vendor/darwin/Rakefile +0 -21
  26. data/lib/vendor/darwin/bin/fsevent_watch +0 -0
  27. data/lib/vendor/darwin/ext/fsevent_watch/Info.plist +0 -38
  28. data/lib/vendor/darwin/ext/fsevent_watch/LICENSE +0 -21
  29. data/lib/vendor/darwin/ext/fsevent_watch/fsevent_watch.xcodeproj/project.pbxproj +0 -254
  30. data/lib/vendor/darwin/ext/fsevent_watch/fsevent_watch/TSICTString.c +0 -394
  31. data/lib/vendor/darwin/ext/fsevent_watch/fsevent_watch/TSICTString.h +0 -74
  32. data/lib/vendor/darwin/ext/fsevent_watch/fsevent_watch/cli.c +0 -160
  33. data/lib/vendor/darwin/ext/fsevent_watch/fsevent_watch/cli.h +0 -45
  34. data/lib/vendor/darwin/ext/fsevent_watch/fsevent_watch/common.h +0 -34
  35. data/lib/vendor/darwin/ext/fsevent_watch/fsevent_watch/compat.c +0 -20
  36. data/lib/vendor/darwin/ext/fsevent_watch/fsevent_watch/compat.h +0 -40
  37. data/lib/vendor/darwin/ext/fsevent_watch/fsevent_watch/main.c +0 -509
  38. data/lib/vendor/darwin/ext/fsevent_watch/xcconfig/Common.xcconfig +0 -82
  39. data/lib/vendor/darwin/ext/fsevent_watch/xcconfig/Debug.xcconfig +0 -19
  40. data/lib/vendor/darwin/ext/fsevent_watch/xcconfig/Release.xcconfig +0 -23
  41. data/lib/vendor/darwin/ext/fsevent_watch/xcconfig/fsevent_watch.xcconfig +0 -17
  42. data/lib/vendor/darwin/ext/rakefile.rb +0 -47
  43. data/lib/vendor/darwin/ext/rb-fsevent.xcconfig +0 -33
  44. data/lib/vendor/darwin/lib/rb-fsevent.rb +0 -2
  45. data/lib/vendor/darwin/lib/rb-fsevent/fsevent.rb +0 -111
  46. data/lib/vendor/darwin/lib/rb-fsevent/version.rb +0 -3
  47. data/lib/vendor/darwin/rb-fsevent.gemspec +0 -25
  48. data/lib/vendor/darwin/spec/fixtures/folder1/file1.txt +0 -0
  49. data/lib/vendor/darwin/spec/fixtures/folder1/folder2/file2.txt +0 -0
  50. data/lib/vendor/darwin/spec/rb-fsevent/fsevent_spec.rb +0 -88
  51. data/lib/vendor/darwin/spec/spec_helper.rb +0 -23
  52. data/lib/vendor/linux/MIT-LICENSE +0 -20
  53. data/lib/vendor/linux/README.md +0 -66
  54. data/lib/vendor/linux/Rakefile +0 -54
  55. data/lib/vendor/linux/VERSION +0 -1
  56. data/lib/vendor/linux/lib/rb-inotify.rb +0 -17
  57. data/lib/vendor/linux/lib/rb-inotify/event.rb +0 -139
  58. data/lib/vendor/linux/lib/rb-inotify/native.rb +0 -31
  59. data/lib/vendor/linux/lib/rb-inotify/native/flags.rb +0 -89
  60. data/lib/vendor/linux/lib/rb-inotify/notifier.rb +0 -308
  61. data/lib/vendor/linux/lib/rb-inotify/watcher.rb +0 -83
  62. data/lib/vendor/linux/rb-inotify.gemspec +0 -53
  63. data/lib/vendor/windows/Gemfile +0 -4
  64. data/lib/vendor/windows/README.md +0 -34
  65. data/lib/vendor/windows/Rakefile +0 -18
  66. data/lib/vendor/windows/lib/rb-fchange.rb +0 -14
  67. data/lib/vendor/windows/lib/rb-fchange/event.rb +0 -29
  68. data/lib/vendor/windows/lib/rb-fchange/native.rb +0 -45
  69. data/lib/vendor/windows/lib/rb-fchange/native/flags.rb +0 -78
  70. data/lib/vendor/windows/lib/rb-fchange/notifier.rb +0 -149
  71. data/lib/vendor/windows/lib/rb-fchange/version.rb +0 -3
  72. data/lib/vendor/windows/lib/rb-fchange/watcher.rb +0 -99
  73. data/lib/vendor/windows/rb-fchange.gemspec +0 -34
  74. data/lib/vendor/windows/spec/fixtures/folder1/file1.txt +0 -0
  75. data/lib/vendor/windows/spec/fixtures/folder1/folder2/file2.txt +0 -0
  76. data/lib/vendor/windows/spec/rb-fchange/fchange_spec.rb +0 -119
  77. data/lib/vendor/windows/spec/spec_helper.rb +0 -21
data/CHANGELOG.md CHANGED
@@ -1,9 +1,3 @@
1
- ## 1.0.3 - 14 May, 2012
2
-
3
- ### Improvement
4
-
5
- - Improve Thor dependency '~> 0.14.6' => '>= 0.14.6'. ([@thibaudgg][])
6
-
7
1
  ## 1.0.2 - 30 April, 2012
8
2
 
9
3
  ### Improvements
data/README.md CHANGED
@@ -34,7 +34,8 @@ Contents
34
34
  * [notification](#guardfile-dsl-notification)
35
35
  * [interactor](#guardfile-dsl-interactor)
36
36
  * [callback](#guardfile-dsl-callback)
37
- * [ignore_paths](#guardfile-dsl-ignore-paths)
37
+ * [ignore](#guardfile-dsl-ignore)
38
+ * [filter](#guardfile-dsl-filter)
38
39
  * [Example](#guardfile-dsl-example)
39
40
  * [Shared configurations](#shared-configurations)
40
41
  * [Advanced Linux system configuration](#advanced-linux-system-configuration)
@@ -47,13 +48,9 @@ Contents
47
48
  Features
48
49
  --------
49
50
 
50
- * [FSEvent](http://en.wikipedia.org/wiki/FSEvents) support on Mac OS X.
51
- * [Inotify](http://en.wikipedia.org/wiki/Inotify) support on Linux.
52
- * [Directory Change Notification](http://msdn.microsoft.com/en-us/library/aa365261\(VS.85\).aspx) support on Windows.
53
- * Polling on the other operating systems.
54
- * Automatic and super fast file modification detection when polling is not used.
55
- Even new and deleted files are detected.
51
+ * File system changes handled by our awesome [Listen](https://github.com/guard/listen) gem.
56
52
  * Support for visual system notifications.
53
+ * Huge ([more than 120](https://rubygems.org/search?query=guard-)) guard extensions eco-system.
57
54
  * Tested against Ruby 1.8.7, 1.9.2, 1.9.3, REE and the latest versions of JRuby & Rubinius.
58
55
 
59
56
  <a name="screencast" />
@@ -358,40 +355,40 @@ $ guard --guardfile ~/.your_global_guardfile
358
355
  $ guard -G ~/.your_global_guardfile # shortcut
359
356
  ```
360
357
 
361
- #### `-A`/`--watch-all-modifications` option
358
+ #### `-i`/`--no-interactions` option
362
359
 
363
- Guard can optionally watch all file modifications like moves or deletions with:
360
+ Turn off completely any Guard terminal interactions with:
364
361
 
365
362
  ```bash
366
- $ guard start -A
367
- $ guard start --watch-all-modifications
363
+ $ guard start -i
364
+ $ guard start --no-interactions
368
365
  ```
369
366
 
370
- #### `-i`/`--no-interactions` option
367
+ #### `-B`/`--no-bundler-warning` option
371
368
 
372
- Turn off completely any Guard terminal interactions with:
369
+ Skip Bundler warning when a Gemfile exists in the project directory but Guard is not run with Bundler.
373
370
 
374
371
  ```bash
375
- $ guard start -i
376
- $ guard start --no-interactions
372
+ $ guard start -B
373
+ $ guard start --no-bundler-warning
377
374
  ```
378
375
 
379
- #### `-I`/`--no-vendor` option
376
+ #### `-l`/`--latency` option
380
377
 
381
- Ignore the use of vendored gems with:
378
+ Overwrite Listen default latency, usefull when your harddrive/system is slow.
382
379
 
383
380
  ```bash
384
- $ guard start -I
385
- $ guard start --no-vendor
381
+ $ guard start -l 1.5
382
+ $ guard start --latency 1.5
386
383
  ```
387
384
 
388
- #### `-B`/`--no-bundler-warning` option
385
+ #### `-p`/`--force-polling` option
389
386
 
390
- Skip Bundler warning when a Gemfile exists in the project directory but Guard is not run with Bundler.
387
+ Force Listen polling listener usage.
391
388
 
392
389
  ```bash
393
- $ guard start -B
394
- $ guard start --no-bundler-warning
390
+ $ guard start -p
391
+ $ guard start --force-polling
395
392
  ```
396
393
 
397
394
  <a name="usage-list" />
@@ -682,22 +679,33 @@ end
682
679
  Please see the [hooks and callbacks](https://github.com/guard/guard/wiki/Hooks-and-callbacks) page in the Guard wiki for
683
680
  more details.
684
681
 
685
- <a name="guardfile-dsl-ignore-paths" />
686
- ### ignore_paths
682
+ <a name="guardfile-dsl-ignore" />
683
+ ### ignore
684
+
685
+ The `ignore` method allows you to ignore specific paths. This comes is handy when you have large
686
+ amounts of non-source data in you project. By default [`.rbx`, `.bundle`, `.git`, `.svn`, `log`, `tmp`, `vendor`](https://github.com/guard/listen/blob/master/lib/listen/directory_record.rb#L14) are ignored.
687
+ Please note that method only accept regexps. More on the [Listen README](https://github.com/guard/listen#the-patterns-for-filtering-and-ignoring-paths).
688
+
689
+ ```ruby
690
+ ignore %r{^ignored/path/}, /public/
691
+ ```
692
+
693
+ <a name="guardfile-dsl-filter" />
694
+ ### filter
687
695
 
688
- The `ignore_paths` method allows you to ignore top level directories altogether. This comes is handy when you have large
689
- amounts of non-source data in you project. By default `.bundle`, `.git`, `log`, `tmp`, and `vendor` are ignored.
690
- Currently it is only possible to ignore the immediate descendants of the watched directory.
696
+ The `filter` method allows you to filter specific paths.
697
+ Please note that method only accept regexps. More on the [Listen README](https://github.com/guard/listen#the-patterns-for-filtering-and-ignoring-paths).
691
698
 
692
699
  ```ruby
693
- ignore_paths 'public'
700
+ filter /\.txt$/, /.*\.zip/
694
701
  ```
695
702
 
696
703
  <a name="guardfile-dsl-example" />
697
704
  ### Example
698
705
 
699
706
  ```ruby
700
- ignore_paths 'foo', 'bar'
707
+ ignore %r{^ignored/path/}, /public/
708
+ filter /\.txt$/, /.*\.zip/
701
709
 
702
710
  notification :growl_notify
703
711
  notification :gntp, :host => '192.168.1.5'
data/lib/guard.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'thread'
2
+ require 'listen'
2
3
 
3
4
  # Guard is the main module for all Guard related modules and classes.
4
5
  # Also other Guard implementation should use this namespace.
@@ -6,13 +7,14 @@ require 'thread'
6
7
  module Guard
7
8
 
8
9
  autoload :UI, 'guard/ui'
10
+ autoload :Guardfile, 'guard/guardfile'
9
11
  autoload :Dsl, 'guard/dsl'
10
12
  autoload :DslDescriber, 'guard/dsl_describer'
11
13
  autoload :Group, 'guard/group'
12
14
  autoload :Interactor, 'guard/interactor'
13
- autoload :Listener, 'guard/listener'
14
15
  autoload :Watcher, 'guard/watcher'
15
16
  autoload :Notifier, 'guard/notifier'
17
+ autoload :Runner, 'guard/runner'
16
18
  autoload :Hook, 'guard/hook'
17
19
 
18
20
  # The Guardfile template for `guard init`
@@ -21,63 +23,7 @@ module Guard
21
23
  HOME_TEMPLATES = File.expand_path('~/.guard/templates')
22
24
 
23
25
  class << self
24
- attr_accessor :options, :interactor, :listener, :lock
25
-
26
- # Creates the initial Guardfile template when it does not
27
- # already exist.
28
- #
29
- # @see Guard::CLI.init
30
- #
31
- # @param [Hash] options The options for creating a Guardfile
32
- # @option options [Boolean] :abort_on_existence Whether to abort or not when a Guardfile already exists
33
- #
34
- def create_guardfile(options = {})
35
- if !File.exist?('Guardfile')
36
- ::Guard::UI.info "Writing new Guardfile to #{ Dir.pwd }/Guardfile"
37
- FileUtils.cp(GUARDFILE_TEMPLATE, 'Guardfile')
38
- elsif options[:abort_on_existence]
39
- ::Guard::UI.error "Guardfile already exists at #{ Dir.pwd }/Guardfile"
40
- abort
41
- end
42
- end
43
-
44
- # Adds the Guardfile template of a Guard implementation
45
- # to an existing Guardfile.
46
- #
47
- # @see Guard::CLI.init
48
- #
49
- # @param [String] guard_name the name of the Guard or template to initialize
50
- #
51
- def initialize_template(guard_name)
52
- guard_class = ::Guard.get_guard_class(guard_name, true)
53
-
54
- if guard_class
55
- guard_class.init(guard_name)
56
- elsif File.exist?(File.join(HOME_TEMPLATES, guard_name))
57
- content = File.read('Guardfile')
58
- template = File.read(File.join(HOME_TEMPLATES, guard_name))
59
-
60
- File.open('Guardfile', 'wb') do |f|
61
- f.puts(content)
62
- f.puts("")
63
- f.puts(template)
64
- end
65
-
66
- ::Guard::UI.info "#{ guard_name } template added to Guardfile, feel free to edit it"
67
- else
68
- const_name = guard_name.downcase.gsub('-', '')
69
- UI.error "Could not load 'guard/#{ guard_name.downcase }' or '~/.guard/templates/#{ guard_name.downcase }' or find class Guard::#{ const_name.capitalize }"
70
- end
71
- end
72
-
73
- # Adds the templates of all installed Guard implementations
74
- # to an existing Guardfile.
75
- #
76
- # @see Guard::CLI.init
77
- #
78
- def initialize_all_templates
79
- guard_gem_names.each {|g| initialize_template(g) }
80
- end
26
+ attr_accessor :options, :interactor, :runner, :listener, :lock
81
27
 
82
28
  # Initialize the Guard singleton:
83
29
  #
@@ -91,98 +37,97 @@ module Guard
91
37
  # @option options [Array<String>] group the list of groups to start
92
38
  # @option options [String] watchdir the director to watch
93
39
  # @option options [String] guardfile the path to the Guardfile
94
- # @option options [Boolean] watch_all_modifications watches all file modifications if true
40
+ # @deprecated @option options [Boolean] watch_all_modifications watches all file modifications if true
41
+ # @deprecated @option options [Boolean] no_vendor ignore vendored dependencies
95
42
  #
96
43
  def setup(options = {})
97
44
  @lock = Mutex.new
98
45
  @options = options
99
- @guards = []
100
- self.reset_groups
101
- @listener = Listener.select_and_init(options)
46
+ @watchdir = (options[:watchdir] && File.expand_path(options[:watchdir])) || Dir.pwd
47
+ @runner = Runner.new
102
48
 
103
- if Signal.list.keys.include?('USR1')
104
- Signal.trap('USR1') { ::Guard.pause unless @listener.paused? }
105
- end
49
+ UI.clear
50
+ deprecated_options_warning
106
51
 
107
- if Signal.list.keys.include?('USR2')
108
- Signal.trap('USR2') { ::Guard.pause if @listener.paused? }
109
- end
52
+ setup_groups
53
+ setup_guards
54
+ setup_listener
55
+ setup_signal_traps
110
56
 
111
- UI.clear if @options[:clear]
112
57
  debug_command_execution if @options[:verbose]
113
58
 
59
+ Dsl.evaluate_guardfile(options)
60
+ UI.error 'No guards found in Guardfile, please add at least one.' if @guards.empty?
61
+
62
+ runner.deprecation_warning # Guard deprecation go here
63
+
64
+ setup_notifier
65
+ setup_interactor
66
+
114
67
  self
115
68
  end
116
69
 
117
- # Smart accessor for retrieving a specific guard or several guards at once.
70
+ # Initialize the groups array with the `:default` group.
118
71
  #
119
72
  # @see Guard.groups
120
73
  #
121
- # @example Filter Guards by String or Symbol
122
- # Guard.guards('rspec')
123
- # Guard.guards(:rspec)
74
+ def setup_groups
75
+ @groups = [Group.new(:default)]
76
+ end
77
+
78
+ # Initialize the guards array to an empty array.
124
79
  #
125
- # @example Filter Guards by Regexp
126
- # Guard.guards(/rsp.+/)
80
+ # @see Guard.guards
127
81
  #
128
- # @example Filter Guards by Hash
129
- # Guard.guards({ :name => 'rspec', :group => 'backend' })
82
+ def setup_guards
83
+ @guards = []
84
+ end
85
+
86
+ # Sets up traps to catch signlas used to control Guard.
130
87
  #
131
- # @param [String, Symbol, Regexp, Hash] filter the filter to apply to the Guards
132
- # @return [Array<Guard>] the filtered Guards
88
+ # Currently two signals are cought:
89
+ # - `USR1` which pauses listening to changes.
90
+ # - `USR2` which resumes listening to changes.
133
91
  #
134
- def guards(filter = nil)
135
- @guards ||= []
92
+ def setup_signal_traps
93
+ if Signal.list.keys.include?('USR1')
94
+ Signal.trap('USR1') { ::Guard.pause unless @listener.paused? }
95
+ end
136
96
 
137
- case filter
138
- when String, Symbol
139
- @guards.find { |guard| guard.class.to_s.downcase.sub('guard::', '') == filter.to_s.downcase.gsub('-', '') }
140
- when Regexp
141
- @guards.find_all { |guard| guard.class.to_s.downcase.sub('guard::', '') =~ filter }
142
- when Hash
143
- filter.inject(@guards) do |matches, (k, v)|
144
- if k.to_sym == :name
145
- matches.find_all { |guard| guard.class.to_s.downcase.sub('guard::', '') == v.to_s.downcase.gsub('-', '') }
146
- else
147
- matches.find_all { |guard| guard.send(k).to_sym == v.to_sym }
148
- end
149
- end
150
- else
151
- @guards
97
+ if Signal.list.keys.include?('USR2')
98
+ Signal.trap('USR2') { ::Guard.pause if @listener.paused? }
152
99
  end
153
100
  end
154
101
 
155
- # Smart accessor for retrieving a specific group or several groups at once.
156
- #
157
- # @see Guard.guards
102
+ # Initializes the listener and registers a callback for changes.
158
103
  #
159
- # @example Filter groups by String or Symbol
160
- # Guard.groups('backend')
161
- # Guard.groups(:backend)
162
- #
163
- # @example Filter groups by Regexp
164
- # Guard.groups(/(back|front)end/)
165
- #
166
- # @param [String, Symbol, Regexp] filter the filter to apply to the Groups
167
- # @return [Array<Group>] the filtered groups
168
- #
169
- def groups(filter = nil)
170
- case filter
171
- when String, Symbol
172
- @groups.find { |group| group.name == filter.to_sym }
173
- when Regexp
174
- @groups.find_all { |group| group.name.to_s =~ filter }
175
- else
176
- @groups
104
+ def setup_listener
105
+ listener_callback = lambda do |modified, added, removed|
106
+ Dsl.reevaluate_guardfile if Watcher.match_guardfile?(modified)
107
+ runner.run_on_changes(modified, added, removed)
177
108
  end
109
+
110
+ listener_options = { :relative_paths => true }
111
+ %w[latency force_polling].each do |option|
112
+ listener_options[option.to_sym] = options[option] if options.key?(option)
113
+ end
114
+
115
+ @listener = Listen.to(@watchdir, listener_options).change(&listener_callback)
178
116
  end
179
117
 
180
- # Initialize the groups array with the `:default` group.
118
+ # Enables or disables the notifier based on user's configurations.
181
119
  #
182
- # @see Guard.groups
120
+ def setup_notifier
121
+ options[:notify] && ENV['GUARD_NOTIFY'] != 'false' ? Notifier.turn_on : Notifier.turn_off
122
+ end
123
+
124
+ # Initializes the interactor unless the user has specified not to.
183
125
  #
184
- def reset_groups
185
- @groups = [Group.new(:default)]
126
+ def setup_interactor
127
+ unless options[:no_interactions]
128
+ @interactor = Interactor.fabricate
129
+ @interactor.start if @interactor
130
+ end
186
131
  end
187
132
 
188
133
  # Start Guard by evaluate the `Guardfile`, initialize the declared Guards
@@ -204,40 +149,20 @@ module Guard
204
149
  #
205
150
  def start(options = {})
206
151
  setup(options)
152
+ UI.info "Guard is now watching at '#{ @watchdir }'"
207
153
 
208
- Dsl.evaluate_guardfile(options)
209
- UI.error 'No guards found in Guardfile, please add at least one.' if ::Guard.guards.empty?
210
-
211
- options[:notify] && ENV['GUARD_NOTIFY'] != 'false' ? Notifier.turn_on : Notifier.turn_off
212
-
213
- listener.on_change do |files|
214
- Dsl.reevaluate_guardfile if Watcher.match_guardfile?(files)
215
- listener.changed_files += files if Watcher.match_files?(guards, files)
216
- end
217
-
218
- UI.info "Guard is now watching at '#{ listener.directory }'"
219
-
220
- run_on_guards do |guard|
221
- run_supervised_task(guard, :start)
222
- end
223
-
224
- unless options[:no_interactions]
225
- @interactor = Interactor.fabricate
226
- @interactor.start if @interactor
227
- end
154
+ interactor.start if interactor
228
155
 
156
+ runner.run(:start)
229
157
  listener.start
230
158
  end
231
159
 
232
160
  # Stop Guard listening to file changes
233
161
  #
234
162
  def stop
235
- run_on_guards do |guard|
236
- run_supervised_task(guard, :stop)
237
- end
238
-
239
163
  interactor.stop if interactor
240
164
  listener.stop
165
+ runner.run(:stop)
241
166
 
242
167
  UI.info 'Bye bye...', :reset => true
243
168
  end
@@ -247,11 +172,8 @@ module Guard
247
172
  # @param [Hash] scopes an hash with a guard or a group scope
248
173
  #
249
174
  def reload(scopes)
250
- run do
251
- run_on_guards(scopes) do |guard|
252
- run_supervised_task(guard, :reload)
253
- end
254
- end
175
+ UI.clear
176
+ runner.run(:reload, scopes)
255
177
  end
256
178
 
257
179
  # Trigger `run_all` on all Guards currently enabled.
@@ -259,11 +181,8 @@ module Guard
259
181
  # @param [Hash] scopes an hash with a guard or a group scope
260
182
  #
261
183
  def run_all(scopes)
262
- run do
263
- run_on_guards(scopes) do |guard|
264
- run_supervised_task(guard, :run_all)
265
- end
266
- end
184
+ UI.clear
185
+ runner.run(:run_all, scopes)
267
186
  end
268
187
 
269
188
  # Pause Guard listening to file changes.
@@ -271,159 +190,73 @@ module Guard
271
190
  def pause
272
191
  if listener.paused?
273
192
  UI.info 'Un-paused files modification listening', :reset => true
274
- listener.clear_changed_files
275
- listener.run
193
+ listener.unpause
276
194
  else
277
195
  UI.info 'Paused files modification listening', :reset => true
278
196
  listener.pause
279
197
  end
280
198
  end
281
199
 
282
- # Trigger `run_on_change` on all Guards currently enabled.
200
+ # Smart accessor for retrieving a specific guard or several guards at once.
283
201
  #
284
- def run_on_change(files)
285
- run do
286
- run_on_guards do |guard|
287
- run_on_change_task(files, guard)
288
- end
289
- end
290
- end
291
-
292
- # Run a block where the listener and the interactor is
293
- # blocked.
202
+ # @see Guard.groups
294
203
  #
295
- # @yield the block to run
204
+ # @example Filter Guards by String or Symbol
205
+ # Guard.guards('rspec')
206
+ # Guard.guards(:rspec)
296
207
  #
297
- def run
298
- UI.clear if options[:clear]
299
-
300
- lock.synchronize do
301
- begin
302
- interactor.stop if interactor
303
- yield
304
- rescue Interrupt
305
- end
306
-
307
- interactor.start if interactor
308
- end
309
- end
310
-
311
- # Loop through all groups and run the given task for each Guard.
208
+ # @example Filter Guards by Regexp
209
+ # Guard.guards(/rsp.+/)
312
210
  #
313
- # Stop the task run for the all Guards within a group if one Guard
314
- # throws `:task_has_failed`.
211
+ # @example Filter Guards by Hash
212
+ # Guard.guards({ :name => 'rspec', :group => 'backend' })
315
213
  #
316
- # @param [Hash] scopes an hash with a guard or a group scope
317
- # @yield the task to run
214
+ # @param [String, Symbol, Regexp, Hash] filter the filter to apply to the Guards
215
+ # @return [Array<Guard>] the filtered Guards
318
216
  #
319
- def run_on_guards(scopes = {})
320
- if scope_guard = scopes[:guard]
321
- yield(scope_guard)
322
- else
323
- groups = scopes[:group] ? [scopes[:group]] : @groups
324
- groups.each do |group|
325
- catch :task_has_failed do
326
- guards(:group => group.name).each do |guard|
327
- yield(guard)
328
- end
217
+ def guards(filter = nil)
218
+ @guards ||= []
219
+
220
+ case filter
221
+ when String, Symbol
222
+ @guards.find { |guard| guard.class.to_s.downcase.sub('guard::', '') == filter.to_s.downcase.gsub('-', '') }
223
+ when Regexp
224
+ @guards.find_all { |guard| guard.class.to_s.downcase.sub('guard::', '') =~ filter }
225
+ when Hash
226
+ filter.inject(@guards) do |matches, (k, v)|
227
+ if k.to_sym == :name
228
+ matches.find_all { |guard| guard.class.to_s.downcase.sub('guard::', '') == v.to_s.downcase.gsub('-', '') }
229
+ else
230
+ matches.find_all { |guard| guard.send(k).to_sym == v.to_sym }
329
231
  end
330
232
  end
233
+ else
234
+ @guards
331
235
  end
332
236
  end
333
237
 
334
- # Run the `:run_on_change` task. When the option `:watch_all_modifications` is set,
335
- # the task is split to run changed paths on {Guard::Guard#run_on_change}, whereas
336
- # deleted paths run on {Guard::Guard#run_on_deletion}.
337
- #
338
- # @param [Array<String>] files the list of files to pass to the task
339
- # @param [Guard::Guard] guard the guard to run
340
- # @raise [:task_has_failed] when task has failed
341
- #
342
- def run_on_change_task(files, guard)
343
- paths = Watcher.match_files(guard, files)
344
- changes = changed_paths(paths)
345
- deletions = deleted_paths(paths)
346
-
347
- unless changes.empty?
348
- UI.debug "#{ guard.class.name }#run_on_change with #{ changes.inspect }"
349
- run_supervised_task(guard, :run_on_change, changes)
350
- end
351
-
352
- unless deletions.empty?
353
- UI.debug "#{ guard.class.name }#run_on_deletion with #{ deletions.inspect }"
354
- run_supervised_task(guard, :run_on_deletion, deletions)
355
- end
356
- end
357
-
358
- # Detects the paths that have changed.
359
- #
360
- # Deleted paths are prefixed by an exclamation point.
361
- # @see Guard::Listener#modified_files
362
- #
363
- # @param [Array<String>] paths the watched paths
364
- # @return [Array<String>] the changed paths
365
- #
366
- def changed_paths(paths)
367
- paths.select { |f| !f.respond_to?(:start_with?) || !f.start_with?('!') }
368
- end
369
-
370
- # Detects the paths that have been deleted.
371
- #
372
- # Deleted paths are prefixed by an exclamation point.
373
- # @see Guard::Listener#modified_files
374
- #
375
- # @param [Array<String>] paths the watched paths
376
- # @return [Array<String>] the deleted paths
377
- #
378
- def deleted_paths(paths)
379
- paths.select { |f| f.respond_to?(:start_with?) && f.start_with?('!') }.map { |f| f.slice(1..-1) }
380
- end
381
-
382
- # Run a Guard task, but remove the Guard when his work leads to a system failure.
383
- #
384
- # When the Group has `:halt_on_fail` disabled, we've to catch `:task_has_failed`
385
- # here in order to avoid an uncaught throw error.
238
+ # Smart accessor for retrieving a specific group or several groups at once.
386
239
  #
387
- # @param [Guard::Guard] guard the Guard to execute
388
- # @param [Symbol] task the task to run
389
- # @param [Array] args the arguments for the task
390
- # @raise [:task_has_failed] when task has failed
240
+ # @see Guard.guards
391
241
  #
392
- def run_supervised_task(guard, task, *args)
393
- catch guard_symbol(guard) do
394
- guard.hook("#{ task }_begin", *args)
395
- result = guard.send(task, *args)
396
- guard.hook("#{ task }_end", result)
397
-
398
- result
399
- end
400
-
401
- rescue Exception => ex
402
- UI.error("#{ guard.class.name } failed to achieve its <#{ task.to_s }>, exception was:" +
403
- "\n#{ ex.class }: #{ ex.message }\n#{ ex.backtrace.join("\n") }")
404
-
405
- guards.delete guard
406
- UI.info("\n#{ guard.class.name } has just been fired")
407
-
408
- ex
409
- end
410
-
411
- # Get the symbol we have to catch when running a supervised task.
412
- # If we are within a Guard group that has the `:halt_on_fail`
413
- # option set, we do NOT catch it here, it will be catched at the
414
- # group level.
242
+ # @example Filter groups by String or Symbol
243
+ # Guard.groups('backend')
244
+ # Guard.groups(:backend)
415
245
  #
416
- # @see .run_on_guards
246
+ # @example Filter groups by Regexp
247
+ # Guard.groups(/(back|front)end/)
417
248
  #
418
- # @param [Guard::Guard] guard the Guard to execute
419
- # @return [Symbol] the symbol to catch
249
+ # @param [String, Symbol, Regexp] filter the filter to apply to the Groups
250
+ # @return [Array<Group>] the filtered groups
420
251
  #
421
- def guard_symbol(guard)
422
- if guard.group.class == Symbol
423
- group = groups(guard.group)
424
- group.options[:halt_on_fail] ? :no_catch : :task_has_failed
252
+ def groups(filter = nil)
253
+ case filter
254
+ when String, Symbol
255
+ @groups.find { |group| group.name == filter.to_sym }
256
+ when Regexp
257
+ @groups.find_all { |group| group.name.to_s =~ filter }
425
258
  else
426
- :task_has_failed
259
+ @groups
427
260
  end
428
261
  end
429
262
 
@@ -463,6 +296,25 @@ module Guard
463
296
  group
464
297
  end
465
298
 
299
+ # Runs a block where the interactor is
300
+ # blocked and execution is synchronized
301
+ # to avoid state inconsistency.
302
+ #
303
+ # @yield the block to run
304
+ #
305
+ def within_preserved_state
306
+ lock.synchronize do
307
+ begin
308
+ interactor.stop if interactor
309
+ @result = yield
310
+ rescue Interrupt
311
+ end
312
+
313
+ interactor.start if interactor
314
+ end
315
+ @result
316
+ end
317
+
466
318
  # Tries to load the Guard main class. This transforms the supplied Guard
467
319
  # name into a class name:
468
320
  #
@@ -545,5 +397,26 @@ module Guard
545
397
  end
546
398
  end
547
399
 
400
+ # Deprecation message for the `watch_all_modifications` start option
401
+ WATCH_ALL_MODIFICATIONS_DEPRECATION = <<-EOS.gsub(/^\s*/, '')
402
+ Starting with Guard v1.1 the 'watch_all_modifications' option is removed and is now always on.
403
+ EOS
404
+
405
+ # Deprecation message for the `no_vendor` start option
406
+ NO_VENDOR_DEPRECATION = <<-EOS.gsub(/^\s*/, '')
407
+ Starting with Guard v1.1 the 'no_vendor' option is removed because the monitoring gems are now
408
+ a part of a new gem called Listen.
409
+
410
+ You can specify a custom version of any monitoring gem directly in your Gemfile
411
+ if you want to overwrite Listen's default monitoring gems.
412
+ EOS
413
+
414
+ # Displays a warning for each deprecated options used.
415
+ #
416
+ def deprecated_options_warning
417
+ ::Guard::UI.deprecation(WATCH_ALL_MODIFICATIONS_DEPRECATION) if options[:watch_all_modifications]
418
+ ::Guard::UI.deprecation(NO_VENDOR_DEPRECATION) if options[:no_vendor]
419
+ end
420
+
548
421
  end
549
422
  end