joshbuddy-guard 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/CHANGELOG.md +370 -0
  2. data/LICENSE +20 -0
  3. data/README.md +470 -0
  4. data/bin/fsevent_watch_guard +0 -0
  5. data/bin/guard +6 -0
  6. data/images/failed.png +0 -0
  7. data/images/pending.png +0 -0
  8. data/images/success.png +0 -0
  9. data/lib/guard.rb +463 -0
  10. data/lib/guard/cli.rb +125 -0
  11. data/lib/guard/dsl.rb +370 -0
  12. data/lib/guard/dsl_describer.rb +150 -0
  13. data/lib/guard/group.rb +37 -0
  14. data/lib/guard/guard.rb +129 -0
  15. data/lib/guard/hook.rb +118 -0
  16. data/lib/guard/interactor.rb +116 -0
  17. data/lib/guard/listener.rb +351 -0
  18. data/lib/guard/listeners/darwin.rb +60 -0
  19. data/lib/guard/listeners/linux.rb +91 -0
  20. data/lib/guard/listeners/polling.rb +55 -0
  21. data/lib/guard/listeners/windows.rb +61 -0
  22. data/lib/guard/notifier.rb +290 -0
  23. data/lib/guard/templates/Guardfile +2 -0
  24. data/lib/guard/ui.rb +193 -0
  25. data/lib/guard/version.rb +6 -0
  26. data/lib/guard/watcher.rb +114 -0
  27. data/lib/vendor/darwin/Gemfile +6 -0
  28. data/lib/vendor/darwin/Guardfile +8 -0
  29. data/lib/vendor/darwin/LICENSE +20 -0
  30. data/lib/vendor/darwin/README.rdoc +254 -0
  31. data/lib/vendor/darwin/Rakefile +21 -0
  32. data/lib/vendor/darwin/ext/extconf.rb +61 -0
  33. data/lib/vendor/darwin/ext/fsevent/fsevent_watch.c +226 -0
  34. data/lib/vendor/darwin/lib/rb-fsevent.rb +2 -0
  35. data/lib/vendor/darwin/lib/rb-fsevent/fsevent.rb +105 -0
  36. data/lib/vendor/darwin/lib/rb-fsevent/version.rb +3 -0
  37. data/lib/vendor/darwin/rb-fsevent.gemspec +24 -0
  38. data/lib/vendor/darwin/spec/fixtures/folder1/file1.txt +0 -0
  39. data/lib/vendor/darwin/spec/fixtures/folder1/folder2/file2.txt +0 -0
  40. data/lib/vendor/darwin/spec/rb-fsevent/fsevent_spec.rb +75 -0
  41. data/lib/vendor/darwin/spec/spec_helper.rb +24 -0
  42. data/lib/vendor/linux/MIT-LICENSE +20 -0
  43. data/lib/vendor/linux/README.md +66 -0
  44. data/lib/vendor/linux/Rakefile +54 -0
  45. data/lib/vendor/linux/VERSION +1 -0
  46. data/lib/vendor/linux/lib/rb-inotify.rb +17 -0
  47. data/lib/vendor/linux/lib/rb-inotify/event.rb +139 -0
  48. data/lib/vendor/linux/lib/rb-inotify/native.rb +31 -0
  49. data/lib/vendor/linux/lib/rb-inotify/native/flags.rb +89 -0
  50. data/lib/vendor/linux/lib/rb-inotify/notifier.rb +308 -0
  51. data/lib/vendor/linux/lib/rb-inotify/watcher.rb +83 -0
  52. data/lib/vendor/linux/rb-inotify.gemspec +53 -0
  53. data/lib/vendor/windows/Gemfile +4 -0
  54. data/lib/vendor/windows/README.md +34 -0
  55. data/lib/vendor/windows/Rakefile +18 -0
  56. data/lib/vendor/windows/lib/rb-fchange.rb +14 -0
  57. data/lib/vendor/windows/lib/rb-fchange/event.rb +29 -0
  58. data/lib/vendor/windows/lib/rb-fchange/native.rb +45 -0
  59. data/lib/vendor/windows/lib/rb-fchange/native/flags.rb +78 -0
  60. data/lib/vendor/windows/lib/rb-fchange/notifier.rb +149 -0
  61. data/lib/vendor/windows/lib/rb-fchange/version.rb +3 -0
  62. data/lib/vendor/windows/lib/rb-fchange/watcher.rb +99 -0
  63. data/lib/vendor/windows/rb-fchange.gemspec +34 -0
  64. data/lib/vendor/windows/spec/fixtures/folder1/file1.txt +0 -0
  65. data/lib/vendor/windows/spec/fixtures/folder1/folder2/file2.txt +0 -0
  66. data/lib/vendor/windows/spec/rb-fchange/fchange_spec.rb +119 -0
  67. data/lib/vendor/windows/spec/spec_helper.rb +21 -0
  68. data/man/guard.1 +96 -0
  69. data/man/guard.1.html +181 -0
  70. metadata +193 -0
Binary file
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'guard'
4
+ require 'guard/cli'
5
+
6
+ Guard::CLI.start
Binary file
Binary file
Binary file
@@ -0,0 +1,463 @@
1
+ require 'thread'
2
+
3
+ # Guard is the main module for all Guard related modules and classes.
4
+ # Also other Guard implementation should use this namespace.
5
+ #
6
+ module Guard
7
+
8
+ autoload :UI, 'guard/ui'
9
+ autoload :Dsl, 'guard/dsl'
10
+ autoload :DslDescriber, 'guard/dsl_describer'
11
+ autoload :Group, 'guard/group'
12
+ autoload :Interactor, 'guard/interactor'
13
+ autoload :Listener, 'guard/listener'
14
+ autoload :Watcher, 'guard/watcher'
15
+ autoload :Notifier, 'guard/notifier'
16
+ autoload :Hook, 'guard/hook'
17
+
18
+ # The Guardfile template for `guard init`
19
+ GUARDFILE_TEMPLATE = File.expand_path('../guard/templates/Guardfile', __FILE__)
20
+
21
+ class << self
22
+ attr_accessor :options, :interactor, :listener, :lock
23
+
24
+ # Creates the initial Guardfile template or add a Guard implementation
25
+ # Guardfile template to an existing Guardfile.
26
+ #
27
+ # @see Guard::Guard.init
28
+ #
29
+ # @param [String] guard_name the name of the Guard to initialize
30
+ #
31
+ def initialize_template(guard_name = nil)
32
+ if !File.exist?('Guardfile')
33
+ ::Guard::UI.info "Writing new Guardfile to #{ Dir.pwd }/Guardfile"
34
+ FileUtils.cp(GUARDFILE_TEMPLATE, 'Guardfile')
35
+ elsif guard_name.nil?
36
+ ::Guard::UI.error "Guardfile already exists at #{ Dir.pwd }/Guardfile"
37
+ exit 1
38
+ end
39
+
40
+ if guard_name
41
+ guard_class = ::Guard.get_guard_class(guard_name)
42
+ guard_class.init(guard_name)
43
+ end
44
+ end
45
+
46
+ # Initialize the Guard singleton.
47
+ #
48
+ # @option options [Boolean] clear if auto clear the UI should be done
49
+ # @option options [Boolean] notify if system notifications should be shown
50
+ # @option options [Boolean] debug if debug output should be shown
51
+ # @option options [Array<String>] group the list of groups to start
52
+ # @option options [String] watchdir the director to watch
53
+ # @option options [String] guardfile the path to the Guardfile
54
+ # @option options [Boolean] watch_all_modifications watches all file modifications if true
55
+ #
56
+ def setup(options = {})
57
+ @lock = Mutex.new
58
+
59
+ @options = options
60
+ @guards = []
61
+ self.reset_groups
62
+ @interactor = Interactor.new unless options[:no_interactions]
63
+ @listener = Listener.select_and_init(options[:watchdir] && File.expand_path(options[:watchdir]), options)
64
+
65
+ @options[:notify] && ENV['GUARD_NOTIFY'] != 'false' ? Notifier.turn_on : Notifier.turn_off
66
+
67
+ UI.clear if @options[:clear]
68
+
69
+ debug_command_execution if @options[:debug]
70
+
71
+ self
72
+ end
73
+
74
+ # Smart accessor for retrieving a specific guard or several guards at once.
75
+ #
76
+ # @param [String, Symbol] filter return the guard with the given name, or nil if not found
77
+ # @param [Regexp] filter returns all guards matching the Regexp, or [] if no guard found
78
+ # @param [Hash] filter returns all guards matching the given Hash.
79
+ # Example: `{ :name => 'rspec', :group => 'backend' }`, or [] if no guard found
80
+ # @param [NilClass] filter returns all guards
81
+ #
82
+ # @see Guard.groups
83
+ #
84
+ def guards(filter = nil)
85
+ case filter
86
+ when String, Symbol
87
+ @guards.find { |guard| guard.class.to_s.downcase.sub('guard::', '') == filter.to_s.downcase.gsub('-', '') }
88
+ when Regexp
89
+ @guards.find_all { |guard| guard.class.to_s.downcase.sub('guard::', '') =~ filter }
90
+ when Hash
91
+ filter.inject(@guards) do |matches, (k, v)|
92
+ if k.to_sym == :name
93
+ matches.find_all { |guard| guard.class.to_s.downcase.sub('guard::', '') == v.to_s.downcase.gsub('-', '') }
94
+ else
95
+ matches.find_all { |guard| guard.send(k).to_sym == v.to_sym }
96
+ end
97
+ end
98
+ else
99
+ @guards
100
+ end
101
+ end
102
+
103
+ # Smart accessor for retrieving a specific group or several groups at once.
104
+ #
105
+ # @param [NilClass] filter returns all groups
106
+ # @param [String, Symbol] filter return the group with the given name, or nil if not found
107
+ # @param [Regexp] filter returns all groups matching the Regexp, or [] if no group found
108
+ #
109
+ # @see Guard.guards
110
+ #
111
+ def groups(filter = nil)
112
+ case filter
113
+ when String, Symbol
114
+ @groups.find { |group| group.name == filter.to_sym }
115
+ when Regexp
116
+ @groups.find_all { |group| group.name.to_s =~ filter }
117
+ else
118
+ @groups
119
+ end
120
+ end
121
+
122
+ # Initialize the groups array with the `:default` group.
123
+ #
124
+ # @see Guard.groups
125
+ #
126
+ def reset_groups
127
+ @groups = [Group.new(:default)]
128
+ end
129
+
130
+ # Start Guard by evaluate the `Guardfile`, initialize the declared Guards
131
+ # and start the available file change listener.
132
+ #
133
+ # @option options [Boolean] clear if auto clear the UI should be done
134
+ # @option options [Boolean] notify if system notifications should be shown
135
+ # @option options [Boolean] debug if debug output should be shown
136
+ # @option options [Array<String>] group the list of groups to start
137
+ # @option options [String] watchdir the director to watch
138
+ # @option options [String] guardfile the path to the Guardfile
139
+ #
140
+ def start(options = {})
141
+ setup(options)
142
+
143
+ Dsl.evaluate_guardfile(options)
144
+
145
+ listener.on_change do |files|
146
+ Dsl.reevaluate_guardfile if Watcher.match_guardfile?(files)
147
+ listener.changed_files += files if Watcher.match_files?(guards, files)
148
+ end
149
+
150
+ UI.info "Guard is now watching at '#{ listener.directory }'"
151
+
152
+ run_on_guards do |guard|
153
+ run_supervised_task(guard, :start)
154
+ end
155
+
156
+ interactor.start if interactor
157
+ listener.start
158
+ end
159
+
160
+ # Stop Guard listening to file changes
161
+ #
162
+ def stop
163
+ UI.info 'Bye bye...', :reset => true
164
+
165
+ run_on_guards do |guard|
166
+ run_supervised_task(guard, :stop)
167
+ end
168
+
169
+ listener.stop
170
+ abort
171
+ end
172
+
173
+ # Reload all Guards currently enabled.
174
+ #
175
+ # @param [Hash] An hash with a guard or a group scope
176
+ #
177
+ def reload(scopes)
178
+ run do
179
+ run_on_guards(scopes) do |guard|
180
+ run_supervised_task(guard, :reload)
181
+ end
182
+ end
183
+ end
184
+
185
+ # Trigger `run_all` on all Guards currently enabled.
186
+ #
187
+ # @param [Hash] An hash with a guard or a group scope
188
+ #
189
+ def run_all(scopes)
190
+ run do
191
+ run_on_guards(scopes) do |guard|
192
+ run_supervised_task(guard, :run_all)
193
+ end
194
+ end
195
+ end
196
+
197
+ # Pause Guard listening to file changes.
198
+ #
199
+ def pause
200
+ if listener.paused?
201
+ UI.info 'Un-paused files modification listening', :reset => true
202
+ listener.clear_changed_files
203
+ listener.run
204
+ else
205
+ UI.info 'Paused files modification listening', :reset => true
206
+ listener.pause
207
+ end
208
+ end
209
+
210
+ # Trigger `run_on_change` on all Guards currently enabled.
211
+ #
212
+ def run_on_change(files)
213
+ run do
214
+ run_on_guards do |guard|
215
+ run_on_change_task(files, guard)
216
+ end
217
+ end
218
+ end
219
+
220
+ # Run a block where the listener and the interactor is
221
+ # blocked.
222
+ #
223
+ # @yield the block to run
224
+ #
225
+ def run
226
+ UI.clear if options[:clear]
227
+
228
+ lock.synchronize do
229
+ begin
230
+ interactor.stop if interactor
231
+ yield
232
+ rescue Interrupt
233
+ end
234
+
235
+ interactor.start if interactor
236
+ end
237
+ end
238
+
239
+ # Loop through all groups and run the given task (as block) for each Guard.
240
+ #
241
+ # Stop the task run for the all Guards within a group if one Guard
242
+ # throws `:task_has_failed`.
243
+ #
244
+ # @param [Hash] An hash with a guard or a group scope
245
+ #
246
+ def run_on_guards(scopes = {})
247
+ if guard = scopes[:guard]
248
+ yield(guard)
249
+ else
250
+ groups = scopes[:group] ? [scopes[:group]] : @groups
251
+ groups.each do |group|
252
+ catch :task_has_failed do
253
+ guards(:group => group.name).each do |guard|
254
+ yield(guard)
255
+ end
256
+ end
257
+ end
258
+ end
259
+ end
260
+
261
+ # Run the `:run_on_change` task. When the option `:watch_all_modifications` is set,
262
+ # the task is split to run changed paths on {Guard::Guard#run_on_change}, whereas
263
+ # deleted paths run on {Guard::Guard#run_on_deletion}.
264
+ #
265
+ # @param [Array<String>] files the list of files to pass to the task
266
+ # @param [Guard::Guard] guard the guard to run
267
+ # @raise [:task_has_failed] when task has failed
268
+ #
269
+ def run_on_change_task(files, guard)
270
+ paths = Watcher.match_files(guard, files)
271
+ changes = changed_paths(paths)
272
+ deletions = deleted_paths(paths)
273
+
274
+ unless changes.empty?
275
+ UI.debug "#{ guard.class.name }#run_on_change with #{ changes.inspect }"
276
+ run_supervised_task(guard, :run_on_change, changes)
277
+ end
278
+
279
+ unless deletions.empty?
280
+ UI.debug "#{ guard.class.name }#run_on_deletion with #{ deletions.inspect }"
281
+ run_supervised_task(guard, :run_on_deletion, deletions)
282
+ end
283
+ end
284
+
285
+ # Detects the paths that have changed.
286
+ #
287
+ # Deleted paths are prefixed by an exclamation point.
288
+ # @see Guard::Listener#modified_files
289
+ #
290
+ # @param [Array<String>] paths the watched paths
291
+ # @return [Array<String>] the changed paths
292
+ #
293
+ def changed_paths(paths)
294
+ paths.select { |f| !f.respond_to?(:start_with?) || !f.start_with?('!') }
295
+ end
296
+
297
+ # Detects the paths that have been deleted.
298
+ #
299
+ # Deleted paths are prefixed by an exclamation point.
300
+ # @see Guard::Listener#modified_files
301
+ #
302
+ # @param [Array<String>] paths the watched paths
303
+ # @return [Array<String>] the deleted paths
304
+ #
305
+ def deleted_paths(paths)
306
+ paths.select { |f| f.respond_to?(:start_with?) && f.start_with?('!') }.map { |f| f.slice(1..-1) }
307
+ end
308
+
309
+ # Run a Guard task, but remove the Guard when his work leads to a system failure.
310
+ #
311
+ # When the Group has `:halt_on_fail` disabled, we've to catch `:task_has_failed`
312
+ # here in order to avoid an uncaught throw error.
313
+ #
314
+ # @param [Guard::Guard] guard the Guard to execute
315
+ # @param [Symbol] task the task to run
316
+ # @param [Array] args the arguments for the task
317
+ # @raise [:task_has_failed] when task has failed
318
+ #
319
+ def run_supervised_task(guard, task, *args)
320
+ catch guard_symbol(guard) do
321
+ guard.hook("#{ task }_begin", *args)
322
+ result = guard.send(task, *args)
323
+ guard.hook("#{ task }_end", result)
324
+
325
+ result
326
+ end
327
+
328
+ rescue Exception => ex
329
+ UI.error("#{ guard.class.name } failed to achieve its <#{ task.to_s }>, exception was:" +
330
+ "\n#{ ex.class }: #{ ex.message }\n#{ ex.backtrace.join("\n") }")
331
+
332
+ guards.delete guard
333
+ UI.info("\n#{ guard.class.name } has just been fired")
334
+
335
+ ex
336
+ end
337
+
338
+ # Get the symbol we have to catch when running a supervised task.
339
+ # If we are within a Guard group that has the `:halt_on_fail`
340
+ # option set, we do NOT catch it here, it will be catched at the
341
+ # group level.
342
+ #
343
+ # @see .run_on_guards
344
+ #
345
+ # @param [Guard::Guard] guard the Guard to execute
346
+ # @return [Symbol] the symbol to catch
347
+ #
348
+ def guard_symbol(guard)
349
+ if guard.group.class == Symbol
350
+ group = groups(guard.group)
351
+ group.options[:halt_on_fail] ? :no_catch : :task_has_failed
352
+ else
353
+ :task_has_failed
354
+ end
355
+ end
356
+
357
+ # Add a Guard to use.
358
+ #
359
+ # @param [String] name the Guard name
360
+ # @param [Array<Watcher>] watchers the list of declared watchers
361
+ # @param [Array<Hash>] callbacks the list of callbacks
362
+ # @param [Hash] options the Guard options (see the given Guard documentation)
363
+ # @return [Guard::Guard] the guard added
364
+ #
365
+ def add_guard(name, watchers = [], callbacks = [], options = {})
366
+ if name.to_sym == :ego
367
+ UI.deprecation('Guard::Ego is now part of Guard. You can remove it from your Guardfile.')
368
+ else
369
+ guard_class = get_guard_class(name)
370
+ callbacks.each { |callback| Hook.add_callback(callback[:listener], guard_class, callback[:events]) }
371
+ guard = guard_class.new(watchers, options)
372
+ @guards << guard
373
+ guard
374
+ end
375
+ end
376
+
377
+ # Add a Guard group.
378
+ #
379
+ # @param [String] name the group name
380
+ # @option options [Boolean] halt_on_fail if a task execution
381
+ # should be halted for all Guards in this group if one Guard throws `:task_has_failed`
382
+ # @return [Guard::Group] the group added (or retrieved from the `@groups` variable if already present)
383
+ #
384
+ def add_group(name, options = {})
385
+ group = groups(name)
386
+ if group.nil?
387
+ group = Group.new(name, options)
388
+ @groups << group
389
+ end
390
+ group
391
+ end
392
+
393
+ # Tries to load the Guard main class.
394
+ #
395
+ # @param [String] name the name of the Guard
396
+ # @return [Class, nil] the loaded class
397
+ #
398
+ def get_guard_class(name)
399
+ name = name.to_s
400
+ try_require = false
401
+ const_name = name.downcase.gsub('-', '')
402
+ begin
403
+ require "guard/#{ name.downcase }" if try_require
404
+ self.const_get(self.constants.find { |c| c.to_s.downcase == const_name })
405
+ rescue TypeError
406
+ unless try_require
407
+ try_require = true
408
+ retry
409
+ else
410
+ UI.error "Could not find class Guard::#{ const_name.capitalize }"
411
+ end
412
+ rescue LoadError => loadError
413
+ UI.error "Could not load 'guard/#{ name.downcase }' or find class Guard::#{ const_name.capitalize }"
414
+ UI.error loadError.to_s
415
+ end
416
+ end
417
+
418
+ # Locate a path to a Guard gem.
419
+ #
420
+ # @param [String] name the name of the Guard without the prefix `guard-`
421
+ # @return [String] the full path to the Guard gem
422
+ #
423
+ def locate_guard(name)
424
+ if Gem::Version.create(Gem::VERSION) >= Gem::Version.create('1.8.0')
425
+ Gem::Specification.find_by_name("guard-#{ name }").full_gem_path
426
+ else
427
+ Gem.source_index.find_name("guard-#{ name }").last.full_gem_path
428
+ end
429
+ rescue
430
+ UI.error "Could not find 'guard-#{ name }' gem path."
431
+ end
432
+
433
+ # Returns a list of guard Gem names installed locally.
434
+ #
435
+ # @return [Array<String>] a list of guard gem names
436
+ #
437
+ def guard_gem_names
438
+ if Gem::Version.create(Gem::VERSION) >= Gem::Version.create('1.8.0')
439
+ Gem::Specification.find_all.select { |x| x.name =~ /^guard-/ }
440
+ else
441
+ Gem.source_index.find_name(/^guard-/)
442
+ end.map { |x| x.name.sub /^guard-/, '' }
443
+ end
444
+
445
+ # Adds a command logger in debug mode. This wraps common command
446
+ # execution functions and logs the executed command before execution.
447
+ #
448
+ def debug_command_execution
449
+ Kernel.send(:alias_method, :original_system, :system)
450
+ Kernel.send(:define_method, :system) do |command, *args|
451
+ ::Guard::UI.debug "Command execution: #{ command } #{ args.join(' ') }"
452
+ original_system command, *args
453
+ end
454
+
455
+ Kernel.send(:alias_method, :original_backtick, :'`')
456
+ Kernel.send(:define_method, :'`') do |command|
457
+ ::Guard::UI.debug "Command execution: #{ command }"
458
+ original_backtick command
459
+ end
460
+ end
461
+
462
+ end
463
+ end