lib/sprout/executable.rb in sprout-1.0.35.pre vs lib/sprout/executable.rb in sprout-1.1.2.pre
- old
+ new
@@ -10,10 +10,11 @@
require 'sprout/executable/paths'
require 'sprout/executable/url'
require 'sprout/executable/urls'
require 'sprout/executable/parameter_factory'
require 'rake/clean'
+require 'sprout/executable/base'
module Sprout
##
# The Sprout::Executable module exposes a Domain Specific Language
@@ -34,558 +35,8 @@
DEFAULT_FILE_EXPRESSION = '/**/**/*'
DEFAULT_PREFIX = '--'
DEFAULT_SHORT_PREFIX = '-'
- extend Concern
-
- module ClassMethods
- ##
- # +add_param+ is the workhorse of the Task.
- # This method is used to add new shell parameters to the executable interface.
- #
- # +name+ is a symbol or string that represents the parameter that you would like to add
- # such as :debug or :source_path.
- #
- # +type+ is a class reference of the Executable::Param that you'd like to use.
- # At the time of this writing, add_param will accept 2 class references that
- # do not extend Param - String and File. The ParameterFactory will automatically
- # resolve these to the correct data type when they are created.
- #
- # Boolean true or false
- # File Path to a file
- # Number Any number
- # Path Path to a directory
- # String Any string value
- # Url Basic URL
- #
- # Files Collection of files
- # Paths Collection of directories
- # Strings Collection of arbitrary strings
- # Urls Collection of URLs
- #
- # Be sure to check out the Sprout::Executable::Param class to learn more about
- # working with executable parameters.
- #
- # Once parameters have been added using the +add_param+ method, clients
- # can set and get those parameters from any newly created executable instance,
- # or from the command line.
- #
- # In the case of an executable delegate, parameter values will be sent to the
- # command line executable in the order they are added using +add_param+.
- #
- # In the case of a Ruby executable, command line parameters will be interpreted
- # in the order they are defined using +add_param+.
- #
- def add_param(name, type, options=nil) # :yields: Sprout::Executable::Param
- raise Sprout::Errors::UsageError.new "[DEPRECATED] add_param no longer uses closures, you can provide the same values as a hash in the optional last argument." if block_given?
- raise Sprout::Errors::UsageError.new "The first parameter (name:SymbolOrString) is required" if name.nil?
- raise Sprout::Errors::UsageError.new "The second parameter (type:Class) is required" if type.nil?
- raise Sprout::Errors::UsageError.new "The type parameter must be a Class by reference" if !type.is_a?(Class)
-
- options ||= {}
- options[:name] = name
- options[:type] = type
- # TODO: Integrate the RDOC-parsed parameter description here:
- #options[:description] ||= Sprout::RDocParser.description_for_caller caller.shift
-
- create_param_accessors options
- static_parameter_collection << options
- options
- end
-
- def add_param_alias new_name, old_name
- create_param_accessors :name => new_name, :real_name => old_name
- end
-
- def static_parameter_collection
- @static_parameter_collection ||= []
- end
-
- def static_default_value_collection
- @static_default_value_collection ||= []
- end
-
- def set key, value
- set_default_value key, value
- end
-
- private
-
- def accessor_can_be_defined_at name
- if(instance_defines? name)
- message = "add_param called with a name that is already in use (#{name}=) on (#{self})"
- raise Sprout::Errors::DuplicateMemberError.new(message)
- end
- end
-
- def create_param_accessors options
- name = options[:name]
- real_name = options[:real_name] || name
- accessor_can_be_defined_at name
-
- # define the writer:
- define_method("#{name}=") do |value|
- if(!options[:writer].nil?)
- value = self.send(options[:writer], value)
- end
- param_hash[real_name].value = value
- instance_variable_set("@#{name}", value)
- end
-
- # define the reader:
- define_method(name) do
- if(options[:reader].nil?)
- if(param_hash[real_name].nil?)
- raise Sprout::Errors::UsageError.new "Unable to use requested parameter (#{real_name}) try adding it using:\n\n add_param :#{real_name}, String\n\n"
- end
- param_hash[real_name].value
- else
- self.send(options[:reader])
- end
- end
- end
-
- def instance_defines? name
- # In Ruby 1.9.1 instance_methods are symbols,
- # In Ruby 1.8.7 instance_methods are strings.
- # Boo.
- self.instance_methods.include?(name.to_s) ||
- self.instance_methods.include?(name)
- end
-
- def set_default_value key, value
- if(!defined? key)
- raise Sprout::Errors::UsageError.new("Cannot set default value (#{value}) for unknown parameter (#{key})")
- end
- static_default_value_collection << { :name => key, :value => value }
- end
- end
-
- module InstanceMethods
- ##
- # The default RubyGem that we will use when requesting our executable.
- #
- # Classes that include the Executable can set the default value for this property
- # at the class level with:
- #
- # set :pkg_name, 'sprout-sometoolname'
- #
- # But that value can be overridden on each instance like:
- #
- # executable = SomeToolTask.new
- # executable.pkg_name = 'sprout-othertoolname'
- #
- # This parameter is required - either from the including class or instance
- # configuration.
- #
- attr_accessor :pkg_name
-
- ##
- # The default RubyGem version that we will use when requesting our executable.
- #
- # Classes that include the Task can set the default value for this property
- # at the class level with:
- #
- # set :pkg_version, '>= 1.0.3'
- #
- # But that value can be overriden on each instance like:
- #
- # executable = SomeToolTask.new
- # too.pkg_version = '>= 2.0.0'
- #
- # This parameter is required - either from the including class or instance
- # configuration.
- #
- attr_accessor :pkg_version
-
-
- ##
- # The default command line prefix that should be used in front of parameter
- # names.
- #
- # The default value for this parameter is '--', but some command line
- # applications (like MXMLC) prefer '-'.
- #
- attr_accessor :default_prefix
-
- ##
- # The default command line prefix for short name parameters.
- #
- # This value defaults to '-', but can be changed to whatever a particular
- # tool prefers.
- #
- attr_accessor :default_short_prefix
-
- ##
- # The default Sprout executable that we will use for this executable.
- #
- # Classes that include the Task can set the default value for this property
- # at the class level with:
- #
- # set :executable, :mxmlc
- #
- # But that value can be overriden on each instance like:
- #
- # executable = SomeToolTask.new
- # too.executable :compc
- #
- # This parameter is required - either from the including class or instance
- # configuration.
- #
- attr_accessor :executable
-
- ##
- # Configure the executable instance to output failure messages to
- # stderr and abort with non-zero response.
- attr_accessor :abort_on_failure
-
- ##
- # If the executable is configured as a Rake::Task, it will extract the
- # Rake::Task[:name] property and apply it to this field.
- #
- # Concrete parameters can pull this value from their +belongs_to+
- # parameter.
- attr_accessor :rake_task_name
-
- attr_reader :param_hash
- attr_reader :params
- attr_reader :prerequisites
-
- def initialize
- super
- @abort_on_failure = true
- @appended_args = nil
- @prepended_args = nil
- @param_hash = {}
- @params = []
- @prerequisites = []
- @option_parser = OptionParser.new
- @default_prefix = DEFAULT_PREFIX
- @default_short_prefix = DEFAULT_SHORT_PREFIX
- initialize_defaults
- initialize_parameters
- end
-
- def parse! commandline_options
- begin
- option_parser.parse! commandline_options
- parse_extra_options! commandline_options
- validate unless help_requested? commandline_options
- rescue StandardError => e
- handle_parse_error e
- end
- end
-
- ##
- # Execute the feature after calling parse
- # with command line arguments.
- #
- # Subclasses will generally override this method
- # if they are a Ruby executable, but if you're
- # just delegating to an external CLI application,
- # calling execute will wind up executing the
- # external process.
- def execute
- execute_delegate
- end
-
- ##
- # Call the provided executable delegate.
- #
- # This method is generally called from Rake task wrappers.
- #
- def execute_delegate
- system_execute binary_path, to_shell
- end
-
- def prepare
- params.each do |param|
- param.prepare
- end
- end
-
- def to_rake *args
- # Define the file task first - so that
- # desc blocks hook up to it...
- outer_task = create_outer_task *args
- update_rake_task_name_from_args *args
- yield self if block_given?
- prepare
-
- # TODO: Tried auto-updating with library
- # prerequisites, but this led to strange
- # behavior with multiple registrations.
- handle_library_prerequisites outer_task.prerequisites
-
- # Add the library resolution rake task
- # as a prerequisite
- outer_task.prerequisites << task(Sprout::Library::TASK_NAME)
- prerequisites.each do |prereq|
- outer_task.prerequisites << prereq
- end
- outer_task
- end
-
- ##
- # This will create a hash of ONLY values that are created
- # using +add_param+, properties that are created with
- # attr_accessor must be handled manually, or patches are welcome!
- def to_hash
- result = {}
- params.each do |param|
- result[param.name] = self.send(param.name)
- end
- result
- end
-
- ##
- # This will ignore unknown parameters, because our very
- # first use case, is when generators call other generators
- # and generator A might have different parameters than
- # generator B.
- def from_hash hash
- hash.each_pair do |key, value|
- if(self.respond_to?(key))
- self.send "#{key}=", value
- end
- end
- end
-
- def to_help
- option_parser.to_s
- end
-
- # Create a string that represents this configured executable for shell execution
- def to_shell
- return @to_shell_proc.call(self) unless @to_shell_proc.nil?
-
- result = []
- result << @prepended_args unless @prepended_args.nil?
- params.each do |param|
- if(param.visible?)
- result << param.to_shell
- end
- end
- result << @appended_args unless @appended_args.nil?
- return result.join(' ')
- end
-
- ##
- # Called by Parameters like :path and :paths
- #
- def default_file_expression
- @default_file_expression ||= Sprout::Executable::DEFAULT_FILE_EXPRESSION
- end
-
- protected
-
- ##
- # Create the outer rake task.
- # For most executables, this will be a Rake::File task,
- # This is a template method that should be overridden
- # for executables that do not result in the creation of
- # a file.
- #
- # @see update_rake_task_name_from_args
- def create_outer_task *args
- file *args do
- execute
- end
- end
-
- ##
- # This method will add the current task to the Rake CLEAN
- # collection.
- #
- # Any Executable that does not create a Rake::File task
- # should also override this method and prevent it from
- # calling +CLEAN.add+.
- #
- # @see create_outer_task
- def update_rake_task_name_from_args *args
- self.rake_task_name = parse_rake_task_arg args.last
- CLEAN.add(self.rake_task_name)
- self.rake_task_name
- end
-
- def parse_rake_task_arg arg
- return arg if arg.is_a?(Symbol) || arg.is_a?(String)
- arg.each_pair do |key, value|
- return key
- end
- nil
- end
-
- def parse_extra_options! options
- options.each do |value|
- params.each do |param|
- if param.hidden_name?
- self.send "#{param.name}=", value
- break
- end
- end
- end
- end
-
- ##
- # This method will generally be overridden
- # by subclasses and they can do whatever customization
- # is necessary for a particular library type.
- #
- # It's important to note that this value can be
- # a String path to a file (or folder), or it can
- # be an Array of paths to files (or folders).
- def library_added path_or_paths
- end
-
- def system_execute binary, params
- Sprout.current_system.execute binary, params
- end
-
- private
-
- def binary_path
- Sprout::Executable.load(executable, pkg_name, pkg_version).path
- end
-
- def handle_library_prerequisites items
- items.each do |task_name|
- t = Rake.application[task_name]
- if(!t.sprout_entity.nil?)
- library_added t.sprout_entity.installed_project_path
- end
- end
- end
-
- def help_requested? options
- options.include? '--help'
- end
-
- def handle_parse_error error
- if(abort_on_failure)
- parts = []
- parts << nil
- parts << "[ERROR - #{error.class.name}] #{error.message}"
- parts << nil
- parts << option_parser.to_s
- parts << nil
- abort parts.join("\n")
- else
- raise error
- end
- end
-
- def initialize_parameters
- add_help_param
- assembled_parameter_collection.each do |declaration|
- param = initialize_parameter declaration
- short = param.option_parser_short_name
-
- option_parser.on short,
- param.option_parser_declaration,
- param.description do |value|
- if(param.is_a?(CollectionParam) && param.delimiter == '+=')
- eval "self.#{param.name} << '#{value}'"
- else
- self.send "#{param.name}=", value
- end
- end
- end
- end
-
- def initialize_defaults
- assembled_default_parameter_collection.reverse.each do |option|
- #puts ">> updating default on: #{self} for: #{option[:name]} with: #{option[:value]}"
- self.send "#{option[:name]}=", option[:value]
- end
- end
-
- def assembled_parameter_collection
- assembled_static_collection :static_parameter_collection
- end
-
- def assembled_default_parameter_collection
- assembled_static_collection :static_default_value_collection
- end
-
- def assembled_static_collection collection_name
- collection = []
- inheritance_chain.reverse.each do |clazz|
- if(clazz.respond_to?(collection_name))
- collection.concat clazz.send(collection_name)
- end
- end
- collection
- end
-
- def inheritance_chain
- chain = []
- clazz = self.class
- while clazz do
- chain << clazz
- clazz = clazz.superclass
- end
- chain
- end
-
- def add_help_param
- option_parser.on '--help', 'Display this help message' do
- puts option_parser.to_s
- exit
- end
- end
-
- def initialize_parameter declaration
- name = declaration[:name]
- name_s = name.to_s
- type = declaration[:type]
-
- # First ensure the named accessor doesn't yet exist...
- if(parameter_hash_includes? name)
- raise Sprout::Errors::ExecutableError.new("ToolTask.add_param called with existing parameter name: #{name_s}")
- end
-
- create_parameter declaration
- end
-
- def create_parameter declaration
- param = ParameterFactory.create declaration[:type]
- param.belongs_to = self
-
- begin
- declaration.each_pair do |key, value|
- param.send "#{key}=", value
- end
- rescue ArgumentError
- raise Sprout::Errors::UsageError.new "Unexpected parameter option encountered with: #{key} and value: #{value}"
- end
-
- raise Sprout::Errors::UsageError.new "Parameter name is required" if(param.name.nil?)
-
- param_hash[param.name.to_sym] = param
- params << param
-
- # Expose this parameter to command line arguments:
- #add_commandline_param param
-
- param
- end
-
- def parameter_hash_includes? name
- param_hash.has_key? name.to_sym
- end
-
- def validate
- params.each do |param|
- param.validate
- end
- end
-
- def option_parser
- @option_parser
- end
-
- end
end
end