Initial yardoc for the DSL class.

This commit is contained in:
Michael Kessler 2011-09-20 11:11:40 +02:00
parent e126c7f609
commit aa55d48b96

View File

@ -1,54 +1,150 @@
module Guard
# The DSL class provides the methods that are used in each `Guardfile` to describe
# the behaviour of Guard.
#
# The main keywords of the DSL are `guard` and `watch`, which are necessary to define
# which Guards are used a what file changes they are watching.
#
# Optionally you can group the Guards with the `group` keyword and ignore certain paths
# with the `ignore_paths` keyword.
#
# A more advanced DSL use is the `callback` keyword, that allows you to execute arbitrary
# code before or after any of the `start`, `stop`, `reload`, `run_all` and `run_on_change`
# guards' method. You can even insert more hooks inside these methods.
# Please [checkout the Wiki page](https://github.com/guard/guard/wiki/Hooks-and-callbacks) for more details.
#
# The DSL will also evaluate normal Ruby code.
#
# There are two possible locations for the `Guardfile`:
# - The `Guardfile` in the current directory where Guard has been started
# - The `.Guardfile` in your home directory.
#
# In addition, if a user configuration `.guard.rb` in your home directory is found, it will
# be appended to the current project `Guardfile`.
#
# @example A sample of a complex Guardfile
# group 'frontend' do
# guard 'passenger', :ping => true do
# watch('config/application.rb')
# watch('config/environment.rb')
# watch(%r{^config/environments/.+\.rb})
# watch(%r{^config/initializers/.+\.rb})
# end
#
# guard 'livereload', :apply_js_live => false do
# watch(%r{^app/.+\.(erb|haml)})
# watch(%r{^app/helpers/.+\.rb})
# watch(%r{^public/javascripts/.+\.js})
# watch(%r{^public/stylesheets/.+\.css})
# watch(%r{^public/.+\.html})
# watch(%r{^config/locales/.+\.yml})
# end
# end
#
# group 'backend' do
# # Reload the bundle when the Gemfile is modified
# guard 'bundler' do
# watch('Gemfile')
# end
#
# # for big project you can fine tune the "timeout" before Spork's launch is considered failed
# guard 'spork', :wait => 40 do
# watch('Gemfile')
# watch('config/application.rb')
# watch('config/environment.rb')
# watch(%r{^config/environments/.+\.rb})
# watch(%r{^config/initializers/.+\.rb})
# watch('spec/spec_helper.rb')
# end
#
# # use RSpec 2, from the system's gem and with some direct RSpec CLI options
# guard 'rspec', :version => 2, :cli => "--color --drb -f doc", :bundler => false do
# watch('spec/spec_helper.rb') { "spec" }
# watch('app/controllers/application_controller.rb') { "spec/controllers" }
# watch('config/routes.rb') { "spec/routing" }
# watch(%r{^spec/support/(controllers|acceptance)_helpers\.rb}) { |m| "spec/#{m[1]}" }
# watch(%r{^spec/.+_spec\.rb})
#
# watch(%r{^app/controllers/(.+)_(controller)\.rb}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
#
# watch(%r{^app/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
# watch(%r{^lib/(.+)\.rb}) { |m| "spec/lib/#{m[1]}_spec.rb" }
# end
# end
#
class Dsl
class << self
@@options = nil
# Evaluate the DSL methods in the `Guardfile`.
#
# @param [Hash] options the Guard options
# @raise [ArgumentError] when options are not a Hash
#
def evaluate_guardfile(options = { })
options.is_a?(Hash) or raise ArgumentError.new("evaluate_guardfile not passed a Hash!")
raise ArgumentError.new('No option hash passed to evaluate_guardfile!') unless options.is_a?(Hash)
@@options = options.dup
fetch_guardfile_contents
instance_eval_guardfile(guardfile_contents_with_user_config)
UI.error "No guards found in Guardfile, please add at least one." if !::Guard.guards.nil? && ::Guard.guards.empty?
UI.error 'No guards found in Guardfile, please add at least one.' if !::Guard.guards.nil? && ::Guard.guards.empty?
end
# Reevaluate the Guardfile to update the current Guard configuration
# when the `Guardfile` has been changed after Guard is started.
#
def reevaluate_guardfile
::Guard.guards.clear
@@options.delete(:guardfile_contents)
Dsl.evaluate_guardfile(@@options)
msg = "Guardfile has been re-evaluated."
msg = 'Guardfile has been re-evaluated.'
UI.info(msg)
Notifier.notify(msg)
end
# Evaluate the content of the `Guardfile`.
#
# @param [String] contents the content to evaluate.
#
def instance_eval_guardfile(contents)
begin
new.instance_eval(contents, @@options[:guardfile_path], 1)
rescue
UI.error "Invalid Guardfile, original error is:\n#{ $! }"
exit 1
end
end
# Test if the current `Guardfile` contains a specific Guard.
#
# @param [String] guard_name the name of the Guard
# @return [Boolean] whether the Guard has been declared
#
def guardfile_include?(guard_name)
guardfile_contents.match(/^guard\s*\(?\s*['":]#{ guard_name }['"]?/)
end
# Read the current `Guardfile` content.
#
# @param [String] the path to the Guardfile
#
def read_guardfile(guardfile_path)
begin
@@options[:guardfile_path] = guardfile_path
@@options[:guardfile_contents] = File.read(guardfile_path)
rescue
UI.error("Error reading file #{ guardfile_path }")
exit 1
end
end
# Get the content to evaluate.
#
# @return [String] the content of the Guardfile.
#
def fetch_guardfile_contents
# TODO: do we need .rc file interaction?
if @@options[:guardfile_contents]
UI.info "Using inline Guardfile."
UI.info 'Using inline Guardfile.'
@@options[:guardfile_path] = 'Inline Guardfile'
elsif @@options[:guardfile]
@ -64,7 +160,7 @@ module Guard
if File.exist?(guardfile_default_path)
read_guardfile(guardfile_default_path)
else
UI.error "No Guardfile found, please create one with `guard init`."
UI.error 'No Guardfile found, please create one with `guard init`.'
exit 1
end
end
@ -75,43 +171,101 @@ module Guard
end
end
# Get the content of the `Guardfile`.
#
# @return [String] the Guardfile content
#
def guardfile_contents
@@options ? @@options[:guardfile_contents] : ""
@@options ? @@options[:guardfile_contents] : ''
end
# Get the content of the `Guardfile` and the global
# user configuration file.
#
# @see #user_config_path
#
# @return [String] the Guardfile content
#
def guardfile_contents_with_user_config
config = File.read(user_config_path) if File.exist?(user_config_path)
[guardfile_contents, config].join("\n")
end
# Get the file path to the project `Guardfile`.
#
# @return [String] the path to the Guardfile
#
def guardfile_path
@@options ? @@options[:guardfile_path] : ""
@@options ? @@options[:guardfile_path] : ''
end
# Tests if the current `Guardfile` content is usable.
#
# @return [Boolean] if the Guardfile is usable
#
def guardfile_contents_usable?
guardfile_contents && guardfile_contents.size >= 'guard :a'.size # smallest guard-definition
end
# Gets the default path of the `Guardfile`.
# This returns the `Guardfile` from the current directory when existing,
# or the global `Guardfile` at the home directory.
#
# @return [String] the path to the Guardfile
#
def guardfile_default_path
File.exist?(local_guardfile_path) ? local_guardfile_path : home_guardfile_path
end
private
# The path to the `Guardfile` that is located at
# the directory where Guard has been started from.
#
# @param [String] the path to the local Guardfile
#
def local_guardfile_path
File.join(Dir.pwd, "Guardfile")
File.join(Dir.pwd, 'Guardfile')
end
# The path to the `.Guardfile` that is located at
# the users home directory.
#
# @param [String] the path to ~/.Guardfile
#
def home_guardfile_path
File.expand_path(File.join("~", ".Guardfile"))
File.expand_path(File.join('~', '.Guardfile'))
end
# The path to the user configuration `.guard.rb`
# that is located at the users home directory.
#
# @param [String] the path to ~/.guard.rb
#
def user_config_path
File.expand_path(File.join("~", ".guard.rb"))
File.expand_path(File.join('~', '.guard.rb'))
end
end
# Declares a group of guards to be run with `guard start --group group_name`
#
# @example Declare two groups of Guards
# group 'backend' do
# guard 'spork'
# guard 'rspec'
# end
#
# group 'frontend' do
# guard 'passenger'
# guard 'livereload'
# end
#
# @param [String] name the group's name called from the CLI
# @yield a block where you can declare several guards
#
# @see Dsl#guard
#
def group(name, &guard_definition)
@groups = @@options[:group] || []
name = name.to_sym
@ -123,23 +277,69 @@ module Guard
end
end
def guard(name, options = {}, &watch_and_callback_definition)
# Declare a guard to be used when running `guard start`.
#
# The name parameter is usually the name of the gem without
# the 'guard-' prefix.
#
# The available options are different for each Guard implementation.
#
# @example Declare a Guard
# guard 'rspec' do
# end
#
# @param [String] name the Guard name
# @param [Hash] options the options accepted by the Guard
# @yield a block where you can declare several watch patterns and actions
#
# @see Dsl#watch
#
def guard(name, options = {})
@watchers = []
@callbacks = []
watch_and_callback_definition.call if watch_and_callback_definition
yield if block_given?
options.update(:group => (@current_group || :default))
::Guard.add_guard(name.to_s.downcase.to_sym, @watchers, @callbacks, options)
end
# Define a pattern to be watched in order to run actions on file modification.
#
# @example Declare watchers for a Guard
# guard 'rspec' do
# watch('spec/spec_helper.rb')
# watch(%r{^.+_spec.rb})
# watch(%r{^app/controllers/(.+).rb}) { |m| 'spec/acceptance/#{m[1]}s_spec.rb' }
# end
#
# @param [String, Regexp] pattern the pattern to be watched by the guard
# @yield a block to be run when the pattern is matched
# @yieldparam [MatchData] m matches of the pattern
# @yieldreturn a directory, a filename, an array of directories / filenames, or nothing (can be an arbitrary command)
#
def watch(pattern, &action)
@watchers << ::Guard::Watcher.new(pattern, action)
end
# Define a callback to execute arbitary code before or after any of
# the `start`, `stop`, `reload`, `run_all` and `run_on_change` guards' method.
#
# @params [Array] args the callback arguments
# @yield a block with listeners
#
def callback(*args, &listener)
listener, events = args.size > 1 ? args : [listener, args[0]]
@callbacks << { :events => events, :listener => listener }
end
# Ignore certain paths globally.
#
# @example Ignore some paths
# ignore_paths .git, .svn
#
# @params [Array] paths the list of paths to ignore
#
def ignore_paths(*paths)
UI.info "Ignoring paths: #{ paths.join(', ') }"
::Guard.listener.ignore_paths.push(*paths)