guard 1.0.3 → 1.1.0.alpha

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