Module: Guard

Defined in:
lib/guard.rb,
lib/guard/ui.rb,
lib/guard/cli.rb,
lib/guard/dsl.rb,
lib/guard/hook.rb,
lib/guard/group.rb,
lib/guard/guard.rb,
lib/guard/version.rb,
lib/guard/watcher.rb,
lib/guard/listener.rb,
lib/guard/notifier.rb,
lib/guard/interactor.rb,
lib/guard/dsl_describer.rb,
lib/guard/listeners/linux.rb,
lib/guard/listeners/darwin.rb,
lib/guard/listeners/polling.rb,
lib/guard/listeners/windows.rb

Overview

Guard is the main module for all Guard related modules and classes. Also other Guard implementation should use this namespace.

Defined Under Namespace

Modules: Hook, Notifier, UI Classes: CLI, Darwin, Dsl, DslDescriber, Group, Guard, Interactor, Linux, Listener, Polling, Watcher, Windows

Constant Summary collapse

GUARDFILE_TEMPLATE =

The Guardfile template for ‘guard init`

File.expand_path('../guard/templates/Guardfile', __FILE__)
VERSION =

The current gem version of Guard

'0.10.0'

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.interactorObject

Returns the value of attribute interactor.



22
23
24
# File 'lib/guard.rb', line 22

def interactor
  @interactor
end

.listenerObject

Returns the value of attribute listener.



22
23
24
# File 'lib/guard.rb', line 22

def listener
  @listener
end

.lockObject

Returns the value of attribute lock.



22
23
24
# File 'lib/guard.rb', line 22

def lock
  @lock
end

.optionsObject

Returns the value of attribute options.



22
23
24
# File 'lib/guard.rb', line 22

def options
  @options
end

Class Method Details

.add_group(name, options = {}) ⇒ Guard::Group

Add a Guard group.

Parameters:

  • name (String)

    the group name

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • halt_on_fail (Boolean)

    if a task execution should be halted for all Guards in this group if one Guard throws ‘:task_has_failed`

Returns:

  • (Guard::Group)

    the group added (or retrieved from the ‘@groups` variable if already present)



384
385
386
387
388
389
390
391
# File 'lib/guard.rb', line 384

def add_group(name, options = {})
  group = groups(name)
  if group.nil?
    group = Group.new(name, options)
    @groups << group
  end
  group
end

.add_guard(name, watchers = [], callbacks = [], options = {}) ⇒ Guard::Guard

Add a Guard to use.

Parameters:

  • name (String)

    the Guard name

  • watchers (Array<Watcher>) (defaults to: [])

    the list of declared watchers

  • callbacks (Array<Hash>) (defaults to: [])

    the list of callbacks

  • options (Hash) (defaults to: {})

    the Guard options (see the given Guard documentation)

Returns:



365
366
367
368
369
370
371
372
373
374
375
# File 'lib/guard.rb', line 365

def add_guard(name, watchers = [], callbacks = [], options = {})
  if name.to_sym == :ego
    UI.deprecation('Guard::Ego is now part of Guard. You can remove it from your Guardfile.')
  else
    guard_class = get_guard_class(name)
    callbacks.each { |callback| Hook.add_callback(callback[:listener], guard_class, callback[:events]) }
    guard = guard_class.new(watchers, options)
    @guards << guard
    guard
  end
end

.changed_paths(paths) ⇒ Array<String>

Detects the paths that have changed.

Deleted paths are prefixed by an exclamation point.

Parameters:

  • paths (Array<String>)

    the watched paths

Returns:

  • (Array<String>)

    the changed paths

See Also:



293
294
295
# File 'lib/guard.rb', line 293

def changed_paths(paths)
  paths.select { |f| !f.respond_to?(:start_with?) || !f.start_with?('!') }
end

.debug_command_executionObject

Adds a command logger in debug mode. This wraps common command execution functions and logs the executed command before execution.



448
449
450
451
452
453
454
455
456
457
458
459
460
# File 'lib/guard.rb', line 448

def debug_command_execution
  Kernel.send(:alias_method, :original_system, :system)
  Kernel.send(:define_method, :system) do |command, *args|
    ::Guard::UI.debug "Command execution: #{ command } #{ args.join(' ') }"
    original_system command, *args
  end

  Kernel.send(:alias_method, :original_backtick, :'`')
  Kernel.send(:define_method, :'`') do |command|
    ::Guard::UI.debug "Command execution: #{ command }"
    original_backtick command
  end
end

.deleted_paths(paths) ⇒ Array<String>

Detects the paths that have been deleted.

Deleted paths are prefixed by an exclamation point.

Parameters:

  • paths (Array<String>)

    the watched paths

Returns:

  • (Array<String>)

    the deleted paths

See Also:



305
306
307
# File 'lib/guard.rb', line 305

def deleted_paths(paths)
  paths.select { |f| f.respond_to?(:start_with?) && f.start_with?('!') }.map { |f| f.slice(1..-1) }
end

.get_guard_class(name) ⇒ Class?

Tries to load the Guard main class.

Parameters:

  • name (String)

    the name of the Guard

Returns:

  • (Class, nil)

    the loaded class



398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
# File 'lib/guard.rb', line 398

def get_guard_class(name)
  name        = name.to_s
  try_require = false
  const_name  = name.downcase.gsub('-', '')
  begin
    require "guard/#{ name.downcase }" if try_require
    self.const_get(self.constants.find { |c| c.to_s.downcase == const_name })
  rescue TypeError
    unless try_require
      try_require = true
      retry
    else
      UI.error "Could not find class Guard::#{ const_name.capitalize }"
    end
  rescue LoadError => loadError
    UI.error "Could not load 'guard/#{ name.downcase }' or find class Guard::#{ const_name.capitalize }"
    UI.error loadError.to_s
  end
end

.groups(filter = nil) ⇒ Object

Smart accessor for retrieving a specific group or several groups at once.

Parameters:

  • filter (NilClass) (defaults to: nil)

    returns all groups

  • filter (String, Symbol) (defaults to: nil)

    return the group with the given name, or nil if not found

  • filter (Regexp) (defaults to: nil)

    returns all groups matching the Regexp, or [] if no group found

See Also:



111
112
113
114
115
116
117
118
119
120
# File 'lib/guard.rb', line 111

def groups(filter = nil)
  case filter
  when String, Symbol
    @groups.find { |group| group.name == filter.to_sym }
  when Regexp
    @groups.find_all { |group| group.name.to_s =~ filter }
  else
    @groups
  end
end

.guard_gem_namesArray<String>

Returns a list of guard Gem names installed locally.

Returns:

  • (Array<String>)

    a list of guard gem names



437
438
439
440
441
442
443
# File 'lib/guard.rb', line 437

def guard_gem_names
  if Gem::Version.create(Gem::VERSION) >= Gem::Version.create('1.8.0')
    Gem::Specification.find_all.select { |x| x.name =~ /^guard-/ }
  else
    Gem.source_index.find_name(/^guard-/)
  end.map { |x| x.name.sub /^guard-/, '' }
end

.guard_symbol(guard) ⇒ Symbol

Get the symbol we have to catch when running a supervised task. If we are within a Guard group that has the ‘:halt_on_fail` option set, we do NOT catch it here, it will be catched at the group level.

Parameters:

Returns:

  • (Symbol)

    the symbol to catch

See Also:



348
349
350
351
352
353
354
355
# File 'lib/guard.rb', line 348

def guard_symbol(guard)
  if guard.group.class == Symbol
    group = groups(guard.group)
    group.options[:halt_on_fail] ? :no_catch : :task_has_failed
  else
    :task_has_failed
  end
end

.guards(filter = nil) ⇒ Object

Smart accessor for retrieving a specific guard or several guards at once.

Parameters:

  • filter (String, Symbol) (defaults to: nil)

    return the guard with the given name, or nil if not found

  • filter (Regexp) (defaults to: nil)

    returns all guards matching the Regexp, or [] if no guard found

  • filter (Hash) (defaults to: nil)

    returns all guards matching the given Hash. Example: ‘{ :name => ’rspec’, :group => ‘backend’ }‘, or [] if no guard found

  • filter (NilClass) (defaults to: nil)

    returns all guards

See Also:



84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/guard.rb', line 84

def guards(filter = nil)
  case filter
  when String, Symbol
    @guards.find { |guard| guard.class.to_s.downcase.sub('guard::', '') == filter.to_s.downcase.gsub('-', '') }
  when Regexp
    @guards.find_all { |guard| guard.class.to_s.downcase.sub('guard::', '') =~ filter }
  when Hash
    filter.inject(@guards) do |matches, (k, v)|
      if k.to_sym == :name
        matches.find_all { |guard| guard.class.to_s.downcase.sub('guard::', '') == v.to_s.downcase.gsub('-', '') }
      else
        matches.find_all { |guard| guard.send(k).to_sym == v.to_sym }
      end
    end
  else
    @guards
  end
end

.initialize_template(guard_name = nil) ⇒ Object

Creates the initial Guardfile template or add a Guard implementation Guardfile template to an existing Guardfile.

Parameters:

  • guard_name (String) (defaults to: nil)

    the name of the Guard to initialize

See Also:



31
32
33
34
35
36
37
38
39
40
41
42
43
44
# File 'lib/guard.rb', line 31

def initialize_template(guard_name = nil)
  if !File.exist?('Guardfile')
    ::Guard::UI.info "Writing new Guardfile to #{ Dir.pwd }/Guardfile"
    FileUtils.cp(GUARDFILE_TEMPLATE, 'Guardfile')
  elsif guard_name.nil?
    ::Guard::UI.error "Guardfile already exists at #{ Dir.pwd }/Guardfile"
    exit 1
  end

  if guard_name
    guard_class = ::Guard.get_guard_class(guard_name)
    guard_class.init(guard_name)
  end
end

.locate_guard(name) ⇒ String

Locate a path to a Guard gem.

Parameters:

  • name (String)

    the name of the Guard without the prefix ‘guard-`

Returns:

  • (String)

    the full path to the Guard gem



423
424
425
426
427
428
429
430
431
# File 'lib/guard.rb', line 423

def locate_guard(name)
  if Gem::Version.create(Gem::VERSION) >= Gem::Version.create('1.8.0')
    Gem::Specification.find_by_name("guard-#{ name }").full_gem_path
  else
    Gem.source_index.find_name("guard-#{ name }").last.full_gem_path
  end
rescue
  UI.error "Could not find 'guard-#{ name }' gem path."
end

.pauseObject

Pause Guard listening to file changes.



199
200
201
202
203
204
205
206
207
208
# File 'lib/guard.rb', line 199

def pause
  if listener.paused?
    UI.info 'Un-paused files modification listening', :reset => true
    listener.clear_changed_files
    listener.run
  else
    UI.info 'Paused files modification listening', :reset => true
    listener.pause
  end
end

.reload(scopes) ⇒ Object

Reload all Guards currently enabled.

Parameters:

  • An (Hash)

    hash with a guard or a group scope



177
178
179
180
181
182
183
# File 'lib/guard.rb', line 177

def reload(scopes)
  run do
    run_on_guards(scopes) do |guard|
      run_supervised_task(guard, :reload)
    end
  end
end

.reset_groupsObject

Initialize the groups array with the ‘:default` group.

See Also:



126
127
128
# File 'lib/guard.rb', line 126

def reset_groups
  @groups = [Group.new(:default)]
end

.run { ... } ⇒ Object

Run a block where the listener and the interactor is blocked.

Yields:

  • the block to run



225
226
227
228
229
230
231
232
233
234
235
236
237
# File 'lib/guard.rb', line 225

def run
  UI.clear if options[:clear]

  lock.synchronize do
    begin
      interactor.stop if interactor
      yield
    rescue Interrupt
    end

    interactor.start if interactor
  end
end

.run_all(scopes) ⇒ Object

Trigger ‘run_all` on all Guards currently enabled.

Parameters:

  • An (Hash)

    hash with a guard or a group scope



189
190
191
192
193
194
195
# File 'lib/guard.rb', line 189

def run_all(scopes)
  run do
    run_on_guards(scopes) do |guard|
      run_supervised_task(guard, :run_all)
    end
  end
end

.run_on_change(files) ⇒ Object

Trigger ‘run_on_change` on all Guards currently enabled.



212
213
214
215
216
217
218
# File 'lib/guard.rb', line 212

def run_on_change(files)
  run do
    run_on_guards do |guard|
      run_on_change_task(files, guard)
    end
  end
end

.run_on_change_task(files, guard) ⇒ Object

Run the ‘:run_on_change` task. When the option `:watch_all_modifications` is set, the task is split to run changed paths on Guard::Guard#run_on_change, whereas deleted paths run on Guard::Guard#run_on_deletion.

Parameters:

  • files (Array<String>)

    the list of files to pass to the task

  • guard (Guard::Guard)

    the guard to run

Raises:

  • (:task_has_failed)

    when task has failed



269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/guard.rb', line 269

def run_on_change_task(files, guard)
  paths = Watcher.match_files(guard, files)
  changes = changed_paths(paths)
  deletions = deleted_paths(paths)

  unless changes.empty?
    UI.debug "#{ guard.class.name }#run_on_change with #{ changes.inspect }"
    run_supervised_task(guard, :run_on_change, changes)
  end

  unless deletions.empty?
    UI.debug "#{ guard.class.name }#run_on_deletion with #{ deletions.inspect }"
    run_supervised_task(guard, :run_on_deletion, deletions)
  end
end

.run_on_guards(scopes = {}) ⇒ Object

Loop through all groups and run the given task (as block) for each Guard.

Stop the task run for the all Guards within a group if one Guard throws ‘:task_has_failed`.

Parameters:

  • An (Hash)

    hash with a guard or a group scope



246
247
248
249
250
251
252
253
254
255
256
257
258
259
# File 'lib/guard.rb', line 246

def run_on_guards(scopes = {})
  if guard = scopes[:guard]
    yield(guard)
  else
    groups = scopes[:group] ? [scopes[:group]] : @groups
    groups.each do |group|
      catch :task_has_failed do
        guards(:group => group.name).each do |guard|
          yield(guard)
        end
      end
    end
  end
end

.run_supervised_task(guard, task, *args) ⇒ Object

Run a Guard task, but remove the Guard when his work leads to a system failure.

When the Group has ‘:halt_on_fail` disabled, we’ve to catch ‘:task_has_failed` here in order to avoid an uncaught throw error.

Parameters:

  • guard (Guard::Guard)

    the Guard to execute

  • task (Symbol)

    the task to run

  • args (Array)

    the arguments for the task

Raises:

  • (:task_has_failed)

    when task has failed



319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
# File 'lib/guard.rb', line 319

def run_supervised_task(guard, task, *args)
  catch guard_symbol(guard) do
    guard.hook("#{ task }_begin", *args)
    result = guard.send(task, *args)
    guard.hook("#{ task }_end", result)

    result
  end

rescue Exception => ex
  UI.error("#{ guard.class.name } failed to achieve its <#{ task.to_s }>, exception was:" +
           "\n#{ ex.class }: #{ ex.message }\n#{ ex.backtrace.join("\n") }")

  guards.delete guard
  UI.info("\n#{ guard.class.name } has just been fired")

  ex
end

.setup(options = {}) ⇒ Object

Initialize the Guard singleton.

Parameters:

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • clear (Boolean)

    if auto clear the UI should be done

  • notify (Boolean)

    if system notifications should be shown

  • debug (Boolean)

    if debug output should be shown

  • group (Array<String>)

    the list of groups to start

  • watchdir (String)

    the director to watch

  • guardfile (String)

    the path to the Guardfile

  • watch_all_modifications (Boolean)

    watches all file modifications if true



56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/guard.rb', line 56

def setup(options = {})
  @lock = Mutex.new

  @options    = options
  @guards     = []
  self.reset_groups
  @interactor = Interactor.new unless options[:no_interactions]
  @listener   = Listener.select_and_init(options[:watchdir] && File.expand_path(options[:watchdir]), options)

  @options[:notify] && ENV['GUARD_NOTIFY'] != 'false' ? Notifier.turn_on : Notifier.turn_off

  UI.clear if @options[:clear]

  debug_command_execution if @options[:debug]

  self
end

.start(options = {}) ⇒ Object

Start Guard by evaluate the ‘Guardfile`, initialize the declared Guards and start the available file change listener.

Parameters:

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • clear (Boolean)

    if auto clear the UI should be done

  • notify (Boolean)

    if system notifications should be shown

  • debug (Boolean)

    if debug output should be shown

  • group (Array<String>)

    the list of groups to start

  • watchdir (String)

    the director to watch

  • guardfile (String)

    the path to the Guardfile



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/guard.rb', line 140

def start(options = {})
  setup(options)

  Dsl.evaluate_guardfile(options)

  listener.on_change do |files|
    Dsl.reevaluate_guardfile        if Watcher.match_guardfile?(files)
    listener.changed_files += files if Watcher.match_files?(guards, files)
  end

  UI.info "Guard is now watching at '#{ listener.directory }'"

  run_on_guards do |guard|
    run_supervised_task(guard, :start)
  end

  interactor.start if interactor
  listener.start
end

.stopObject

Stop Guard listening to file changes



162
163
164
165
166
167
168
169
170
171
# File 'lib/guard.rb', line 162

def stop
  UI.info 'Bye bye...', :reset => true

  run_on_guards do |guard|
    run_supervised_task(guard, :stop)
  end

  listener.stop
  abort
end