require "guard/config" require "guard/deprecated/evaluator" unless Guard::Config.new.strict? require "guard/options" require "guard/plugin" require "guard/dsl" module Guard module Guardfile # This class is responsible for evaluating the Guardfile. It delegates to # Guard::Dsl for the actual objects generation from the Guardfile content. # # @see Guard::Dsl # # TODO: rename this to a Locator or Loader or something class Evaluator Deprecated::Evaluator.add_deprecated(self) unless Config.new.strict? ERROR_NO_GUARDFILE = "No Guardfile found,"\ " please create one with `guard init`." attr_reader :options, :guardfile_path ERROR_NO_PLUGINS = "No Guard plugins found in Guardfile,"\ " please add at least one." class Error < RuntimeError end class NoGuardfileError < Error end class NoCustomGuardfile < Error end class NoPluginsError < Error end def guardfile_source @source end # Initializes a new Guard::Guardfile::Evaluator object. # # @option opts [String] guardfile the path to a valid Guardfile # @option opts [String] contents a string representing the # content of a valid Guardfile # def initialize(opts = {}) @type = nil @path = nil @user_config = nil opts = _from_deprecated(opts) if opts[:contents] @type = :inline @contents = opts[:contents] else if opts[:guardfile] @type = :custom @path = Pathname(opts[:guardfile]) # may be updated by _read end end end # Evaluates the DSL methods in the `Guardfile`. # # @example Programmatically evaluate a Guardfile # Guard::Guardfile::Evaluator.new.evaluate # # @example Programmatically evaluate a Guardfile with a custom Guardfile # path # # options = { guardfile: '/Users/guardfile/MyAwesomeGuardfile' } # Guard::Guardfile::Evaluator.new(options).evaluate # # @example Programmatically evaluate a Guardfile with an inline Guardfile # # options = { contents: 'guard :rspec' } # Guard::Guardfile::Evaluator.new(options).evaluate # def evaluate inline? || _use_provided || _use_default! contents = _guardfile_contents fail NoPluginsError, ERROR_NO_PLUGINS unless /guard/m =~ contents Dsl.new.evaluate(contents, @path || "", 1) end # Tests if the current `Guardfile` contains a specific Guard plugin. # # @example Programmatically test if a Guardfile contains a specific Guard # plugin # # File.read('Guardfile') # => "guard :rspec" # # Guard::Guardfile::Evaluator.new.guardfile_include?('rspec) # => true # # @param [String] plugin_name the name of the Guard # @return [Boolean] whether the Guard plugin has been declared # def guardfile_include?(plugin_name) /^\s*guard\s*\(?\s*['":]#{plugin_name}['"]?/.match(@contents) end attr_reader :path def custom? @type == :custom end # Gets the content of the `Guardfile` concatenated with the global # user configuration file. # # @example Programmatically get the content of the current Guardfile # Guard::Guardfile::Evaluator.new.guardfile_contents # => "guard :rspec" # # @return [String] the Guardfile content # def guardfile_contents config = File.read(_user_config_path) if File.exist?(_user_config_path) [_guardfile_contents_without_user_config, config].compact.join("\n") end def inline? @type == :inline end private def _guardfile_contents_without_user_config @guardfile_contents || "" end def _instance_eval_guardfile(contents) Dsl.new.evaluate(contents, @guardfile_path || "", 1) rescue => ex ::Guard::UI.error "Invalid Guardfile, original error is:\n#{ $! }" raise ex end def _fetch_guardfile_contents _use_inline || _use_provided || _use_default @evaluated = true return if _guardfile_contents_usable? UI.error "No Guard plugins found in Guardfile,"\ " please add at least one." end def _use_inline source_from_option = @source.nil? && options[:guardfile_contents] inline = @source == :inline return false unless (source_from_option) || inline @source = :inline @guardfile_contents = options[:guardfile_contents] ::Guard::UI.info "Using inline Guardfile." true end def _use_provided return unless custom? @path, @contents = _read(@path) true rescue Errno::ENOENT fail NoCustomGuardfile, "No Guardfile exists at #{ @path }." end def _use_default! @path, @contents = _read("Guardfile") @type = :default rescue Errno::ENOENT begin @path, @contents = _read("~/.Guardfile") @type = :default rescue Errno::ENOENT fail NoGuardfileError, ERROR_NO_GUARDFILE end end def _read(path) full_path = Pathname(path).expand_path [full_path, full_path.read] rescue Errno::ENOENT fail rescue SystemCallError => e ::Guard::UI.error "Error reading file #{full_path}:" ::Guard::UI.error e.inspect ::Guard::UI.error e.backtrace abort end def _guardfile_contents @user_config ||= Pathname("~/.guard.rb").expand_path.read [@contents, @user_config].compact.join("\n") rescue Errno::ENOENT @contents || "" end def _guardfile_contents_usable? guardfile_contents && guardfile_contents =~ /guard/m end def _from_deprecated(opts) res = opts.dup if opts.key?(:guardfile_contents) res[:contents] = opts[:guardfile_contents] end res end end end end