module Guard

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

TODO: remove this file in next major version

TODO: rework this class from the bottom-up

- remove dependency on Session and Scope
- extract into a separate gem

Constants

UPGRADE_WIKI_URL
VERSION

Attributes

interactor[R]
listener[R]
queue[R]
state[R]

Public Class Methods

async_queue_add(changes) click to toggle source

Asynchronously trigger changes

Currently supported args:

@example Old style hash:
  async_queue_add(modified: ['foo'], added: ['bar'], removed: [])

@example New style signals with args:
  async_queue_add([:guard_pause, :unpaused ])
# File lib/guard.rb, line 87
def async_queue_add(changes)
  @queue << changes

  # Putting interactor in background puts guard into foreground
  # so it can handle change notifications
  Thread.new { interactor.background }
end
init(cmdline_options) click to toggle source
# File lib/guard.rb, line 73
def init(cmdline_options)
  @state = Internals::State.new(cmdline_options)
end
setup(cmdline_options = {}) click to toggle source

Initializes the Guard singleton:

  • Initialize the internal Guard state;

  • Create the interactor

  • Select and initialize the file change listener.

@option options [Boolean] clear if auto clear the UI should be done @option options [Boolean] notify if system notifications should be shown @option options [Boolean] debug if debug output should be shown @option options [Array<String>] group the list of groups to start @option options [Array<String>] watchdir the directories to watch @option options [String] guardfile the path to the Guardfile

@return [Guard] the Guard singleton

# File lib/guard.rb, line 44
def setup(cmdline_options = {})
  init(cmdline_options)

  @queue = Internals::Queue.new(Guard)

  _evaluate(state.session.evaluator_options)

  # NOTE: this should be *after* evaluate so :directories can work
  # TODO: move listener setup to session?
  @listener = Listen.send(*state.session.listener_args, &_listener_callback)

  ignores = state.session.guardfile_ignore
  @listener.ignore(ignores) unless ignores.empty?

  ignores = state.session.guardfile_ignore_bang
  @listener.ignore!(ignores) unless ignores.empty?

  Notifier.connect(state.session.notify_options)

  traps = Internals::Traps
  traps.handle("USR1") { async_queue_add([:guard_pause, :paused]) }
  traps.handle("USR2") { async_queue_add([:guard_pause, :unpaused]) }

  @interactor = Interactor.new(state.session.interactor_name == :sleep)
  traps.handle("INT") { @interactor.handle_interrupt }

  self
end

Private Class Methods

_evaluate(options) click to toggle source
# File lib/guard.rb, line 132
def _evaluate(options)
  evaluator = Guardfile::Evaluator.new(options)
  evaluator.evaluate

  UI.reset_and_clear

  msg = "No plugins found in Guardfile, please add at least one."
  UI.error msg if _pluginless_guardfile?

  if evaluator.inline?
    UI.info("Using inline Guardfile.")
  elsif evaluator.custom?
    UI.info("Using Guardfile at #{ evaluator.path }.")
  end
rescue Guardfile::Evaluator::NoPluginsError => e
  UI.error(e.message)
end
_guardfile_deprecated_check(modified) click to toggle source

TODO: remove at some point TODO: not tested because collides with ongoing refactoring

# File lib/guard.rb, line 152
def _guardfile_deprecated_check(modified)
  modified.map!(&:to_s)
  regexp = %r{^(?:.+/)?Guardfile$}
  guardfiles = modified.select { |path| regexp.match(path) }
  return if guardfiles.empty?

  guardfile = Pathname("Guardfile").realpath
  real_guardfiles = guardfiles.detect do |path|
    /^Guardfile$/.match(path) || Pathname(path).expand_path == guardfile
  end

  return unless real_guardfiles

  UI.warning "Guardfile changed -- _guard-core will exit.\n"
  exit 2 # nonzero to break any while loop
end
_listener_callback() click to toggle source
# File lib/guard.rb, line 113
def _listener_callback
  lambda do |modified, added, removed|
    relative_paths = {
      modified: _relative_pathnames(modified),
      added: _relative_pathnames(added),
      removed: _relative_pathnames(removed)
    }

    _guardfile_deprecated_check(relative_paths[:modified])

    async_queue_add(relative_paths) if _relevant_changes?(relative_paths)
  end
end
_pluginless_guardfile?() click to toggle source

TODO: obsoleted? (move to Dsl?)

# File lib/guard.rb, line 128
def _pluginless_guardfile?
  state.session.plugins.all.empty?
end
_relative_pathnames(paths) click to toggle source
# File lib/guard.rb, line 109
def _relative_pathnames(paths)
  paths.map { |path| _relative_pathname(path) }
end
_relevant_changes?(changes) click to toggle source

Check if any of the changes are actually watched for TODO: why iterate twice? reuse this info when running tasks

# File lib/guard.rb, line 99
def _relevant_changes?(changes)
  # TODO: no coverage!
  files = changes.values.flatten(1)
  scope = Guard.state.scope
  watchers = scope.grouped_plugins.map do |_group, plugins|
    plugins.map(&:watchers).flatten
  end.flatten
  watchers.any? { |watcher| files.any? { |file| watcher.match(file) } }
end