Module: Guard

Defined in:
lib/guard.rb,
lib/guard/ui.rb,
lib/guard/dsl.rb,
lib/guard/cli.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/notifiers/gntp.rb,
lib/guard/listeners/linux.rb,
lib/guard/notifiers/growl.rb,
lib/guard/listeners/darwin.rb,
lib/guard/listeners/polling.rb,
lib/guard/listeners/windows.rb,
lib/guard/interactors/simple.rb,
lib/guard/notifiers/libnotify.rb,
lib/guard/notifiers/rb_notifu.rb,
lib/guard/interactors/readline.rb,
lib/guard/notifiers/notifysend.rb,
lib/guard/notifiers/growl_notify.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, ReadlineInteractor, SimpleInteractor, Watcher, Windows

Constant Summary

GUARDFILE_TEMPLATE =

The Guardfile template for `guard init`

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

The location of user defined templates

File.expand_path('~/.guard/templates')
VERSION =

The current gem version of Guard

'1.0.2'

Class Attribute Summary (collapse)

Class Method Summary (collapse)

Class Attribute Details

+ (Object) interactor

Returns the value of attribute interactor



24
25
26
# File 'lib/guard.rb', line 24

def interactor
  @interactor
end

+ (Object) listener

Returns the value of attribute listener



24
25
26
# File 'lib/guard.rb', line 24

def listener
  @listener
end

+ (Object) lock

Returns the value of attribute lock



24
25
26
# File 'lib/guard.rb', line 24

def lock
  @lock
end

+ (Object) options

Returns the value of attribute options



24
25
26
# File 'lib/guard.rb', line 24

def options
  @options
end

Class Method Details

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

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)



457
458
459
460
461
462
463
464
# File 'lib/guard.rb', line 457

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

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

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:



438
439
440
441
442
443
444
445
446
447
448
# File 'lib/guard.rb', line 438

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

+ (Array<String>) changed_paths(paths)

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:



366
367
368
# File 'lib/guard.rb', line 366

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

+ (Object) create_guardfile(options = {})

Creates the initial Guardfile template when it does not already exist.

Parameters:

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

    The options for creating a Guardfile

Options Hash (options):

  • :abort_on_existence (Boolean)

    Whether to abort or not when a Guardfile already exists

See Also:

  • Guard::CLI.init


34
35
36
37
38
39
40
41
42
# File 'lib/guard.rb', line 34

def create_guardfile(options = {})
  if !File.exist?('Guardfile')
    ::Guard::UI.info "Writing new Guardfile to #{ Dir.pwd }/Guardfile"
    FileUtils.cp(GUARDFILE_TEMPLATE, 'Guardfile')
  elsif options[:abort_on_existence]
    ::Guard::UI.error "Guardfile already exists at #{ Dir.pwd }/Guardfile"
    abort
  end
end

+ (Object) debug_command_execution

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



534
535
536
537
538
539
540
541
542
543
544
545
546
# File 'lib/guard.rb', line 534

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

+ (Array<String>) deleted_paths(paths)

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:



378
379
380
# File 'lib/guard.rb', line 378

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

+ (Class?) get_guard_class(name, fail_gracefully = false)

Tries to load the Guard main class. This transforms the supplied Guard name into a class name:

  • `guardname` will become `Guard::Guardname`

  • `dashed-guard-name` will become `Guard::DashedGuardName`

  • `underscore_guard_name` will become `Guard::UnderscoreGuardName`

When no class is found with the strict case sensitive rules, another try is made to locate the class without matching case:

  • `rspec` will find a class `Guard::RSpec`

Parameters:

  • name (String)

    the name of the Guard

  • fail_gracefully (Boolean) (defaults to: false)

    whether error messages should not be printed

Returns:

  • (Class, nil)

    the loaded class



482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
# File 'lib/guard.rb', line 482

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

+ (Array<Group>) groups(filter = nil)

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

Examples:

Filter groups by String or Symbol

Guard.groups('backend')
Guard.groups(:backend)

Filter groups by Regexp

Guard.groups(/(back|front)end/)

Parameters:

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

    the filter to apply to the Groups

Returns:

  • (Array<Group>)

    the filtered groups

See Also:



169
170
171
172
173
174
175
176
177
178
# File 'lib/guard.rb', line 169

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

+ (Array<String>) guard_gem_names

Returns a list of guard Gem names installed locally.

Returns:

  • (Array<String>)

    a list of guard gem names



523
524
525
526
527
528
529
# File 'lib/guard.rb', line 523

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

+ (Symbol) guard_symbol(guard)

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:



421
422
423
424
425
426
427
428
# File 'lib/guard.rb', line 421

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

+ (Array<Guard>) guards(filter = nil)

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

Examples:

Filter Guards by String or Symbol

Guard.guards('rspec')
Guard.guards(:rspec)

Filter Guards by Regexp

Guard.guards(/rsp.+/)

Filter Guards by Hash

Guard.guards({ :name => 'rspec', :group => 'backend' })

Parameters:

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

    the filter to apply to the Guards

Returns:

  • (Array<Guard>)

    the filtered Guards

See Also:



134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/guard.rb', line 134

def guards(filter = nil)
  @guards ||= []

  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

+ (Object) initialize_all_templates

Adds the templates of all installed Guard implementations to an existing Guardfile.

See Also:

  • Guard::CLI.init


78
79
80
# File 'lib/guard.rb', line 78

def initialize_all_templates
  guard_gem_names.each {|g| initialize_template(g) }
end

+ (Object) initialize_template(guard_name)

Adds the Guardfile template of a Guard implementation to an existing Guardfile.

Parameters:

  • guard_name (String)

    the name of the Guard or template to initialize

See Also:

  • Guard::CLI.init


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

def initialize_template(guard_name)
  guard_class = ::Guard.get_guard_class(guard_name, true)

  if guard_class
    guard_class.init(guard_name)
  elsif File.exist?(File.join(HOME_TEMPLATES, guard_name))
    content  = File.read('Guardfile')
    template = File.read(File.join(HOME_TEMPLATES, guard_name))

    File.open('Guardfile', 'wb') do |f|
      f.puts(content)
      f.puts("")
      f.puts(template)
    end

    ::Guard::UI.info "#{ guard_name } template added to Guardfile, feel free to edit it"
  else
    const_name  = guard_name.downcase.gsub('-', '')
    UI.error "Could not load 'guard/#{ guard_name.downcase }' or '~/.guard/templates/#{ guard_name.downcase }' or find class Guard::#{ const_name.capitalize }"
  end
end

+ (String) locate_guard(name)

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



509
510
511
512
513
514
515
516
517
# File 'lib/guard.rb', line 509

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

+ (Object) pause

Pause Guard listening to file changes.



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

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

+ (Object) reload(scopes)

Reload all Guards currently enabled.

Parameters:

  • scopes (Hash)

    an hash with a guard or a group scope



249
250
251
252
253
254
255
# File 'lib/guard.rb', line 249

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

+ (Object) reset_groups

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

See Also:



184
185
186
# File 'lib/guard.rb', line 184

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

+ (Object) run { ... }

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

Yields:

  • the block to run



297
298
299
300
301
302
303
304
305
306
307
308
309
# File 'lib/guard.rb', line 297

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

+ (Object) run_all(scopes)

Trigger `run_all` on all Guards currently enabled.

Parameters:

  • scopes (Hash)

    an hash with a guard or a group scope



261
262
263
264
265
266
267
# File 'lib/guard.rb', line 261

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

+ (Object) run_on_change(files)

Trigger `run_on_change` on all Guards currently enabled.



284
285
286
287
288
289
290
# File 'lib/guard.rb', line 284

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

+ (Object) run_on_change_task(files, guard)

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



342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'lib/guard.rb', line 342

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

+ (Object) run_on_guards(scopes = {}) { ... }

Loop through all groups and run the given task for each Guard.

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

Parameters:

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

    an hash with a guard or a group scope

Yields:

  • the task to run



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

def run_on_guards(scopes = {})
  if scope_guard = scopes[:guard]
    yield(scope_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

+ (Object) run_supervised_task(guard, task, *args)

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



392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
# File 'lib/guard.rb', line 392

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

+ (Object) setup(options = {})

Initialize the Guard singleton:

  • Initialize the internal Guard state.

  • Create the interactor when necessary for user interaction.

  • Select and initialize the 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

  • verbose (Boolean)

    if verbose 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



96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/guard.rb', line 96

def setup(options = {})
  @lock       = Mutex.new
  @options    = options
  @guards     = []
  self.reset_groups
  @listener   = Listener.select_and_init(options)

  if Signal.list.keys.include?('USR1')
    Signal.trap('USR1') { ::Guard.pause unless @listener.paused? }
  end

  if Signal.list.keys.include?('USR2')
    Signal.trap('USR2') { ::Guard.pause if @listener.paused? }
  end

  UI.clear if @options[:clear]
  debug_command_execution if @options[:verbose]

  self
end

+ (Object) start(options = {})

Start Guard by evaluate the `Guardfile`, initialize the declared Guards and start the available file change listener. Main method for Guard that is called from the CLI when guard starts.

  • Setup Guard internals

  • Evaluate the `Guardfile`

  • Configure Notifiers

  • Initialize the declared Guards

  • 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



205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
# File 'lib/guard.rb', line 205

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

  Dsl.evaluate_guardfile(options)
  UI.error 'No guards found in Guardfile, please add at least one.' if ::Guard.guards.empty?

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

  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

  unless options[:no_interactions]
    @interactor = Interactor.fabricate
    @interactor.start if @interactor
  end

  listener.start
end

+ (Object) stop

Stop Guard listening to file changes



234
235
236
237
238
239
240
241
242
243
# File 'lib/guard.rb', line 234

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

  interactor.stop if interactor
  listener.stop

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