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
more...

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)

[View source]

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:

[View source]

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:

[View source]

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.

[View source]

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:

[View source]

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

[View source]

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:

[View source]

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

[View source]

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:

[View source]

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:

[View source]

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:

[View source]

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

[View source]

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.

[View source]

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

[View source]

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:

[View source]

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

[View source]

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

[View source]

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.

[View source]

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

[View source]

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

[View source]

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

[View source]

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

[View source]

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

[View source]

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

[View source]

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