require 'rbconfig' require 'fileutils' require 'facet/fileutils/safe_ln' require 'facet/string/tabto' require 'facet/module/basename' require 'facet/module/attr_setter' require 'facet/filelist' unless defined?( FileList ) require 'facet/openobject' require 'reap/projectinfo' # _____ _ ___ ___ _ # |_ _|_ _ __| |__ | _ ) __ _ ___ ___ / __| |__ _ ______ # | |/ _` (_-< / / | _ \/ _` (_- Hello! # puts master.default #=> Yo! # puts mytask.default #=> Yo! (inherited from master) # end # end # # With the corresponding settings in the ProjectInfo file as: # # default: Yo! # # mytask: # message: Hello! # module Reap # Hash of all possible tasks # { task name => task class } def self.registry Task.task_list end # Hash of tasks available to this project def self.tasks unless @tasks @tasks = {} registry.each do |name, klass| @tasks[name] = klass if klass.available? end end @tasks end # Task base class. class Task #include ::Config include ::FileUtils RUBY = ::Config::CONFIG['ruby_install_name'] class << self # When this class is inherited the new task is registered # by adding it to the task_list hash. def inherited( base ) task_list[base.task_name] = base end # Task list hash. def task_list @task_list ||= {} end # === Task DSL methods #def task_section_required( val ) ; @section_required = val ; end #def section_required? ; @section_required ; end # Provided the tasks name. This defualt's the task's # class' name downcased, so it is rarely needed. def task_name basename.downcase end # Short one-line description of the task. def task_desc( text=nil, &block ) return @task_desc = proc { text } if text return @task_desc = block if block_given? return @task_desc.call end # Takes a string or a block the evaluates to a string # providing detailed help information about the task. def task_help( text=nil, &block ) return @task_help = proc { text } if text return @task_help = block if block_given? return @task_help.call end # Set alias for 'task' attribute, which provides # inherited (from master) access to section data. def task_attr( name ) define_method(name) { @task } end # How to determine if a task is available for use. # By default a taks is only available if the Project # file exists and the task's section also exists. # Common was to loosen this resriction are to # only require the file, not the seciton: # # task_available { Reap.projectfile? } # # Or have no restrictions: # # task_available true # def task_available( bool=false, &block ) if bool (class << self; self; end).class_eval { define_method( :available? ){ true } } else (class << self; self; end).class_eval { define_method( :available?, &block ) } end end # Is the task available for use? By default # a task is only made available if the ProjectInfo # is present and the task's section is present. def available? return false unless Reap.projectfile? #if section_required? return has_section? #end #true end # Project file exists and has task's section? def has_section? return false unless Reap.projectfile? ProjectInfo.instance.info.key?( task_name ) end # Class-level access to Project file's master data # provided via a CascadingOpenObject. def master @master ||= ProjectInfo.instance.to_cascading_open_object end end #<< class # instance methods def task_name ; self.class.task_name ; end def task_desc ; self.class.task_desc ; end def task_help ; self.class.task_help ; end #def section_required? ; self.class.section_required? ; end #def master ; ::ProjectInfo.info ; end def master ; self.class.master ; end def section ; @section ; end def task ; @task ; end def initialize( *args ) #@master = CascadingOpenObject.new( $PROJECT_INFO ) @args = args end # Run task for each section entires given. def execute( section=nil ) section = section || master[task_name] case section when Array section.each do |s| initiate( s ) end else initiate( section ) end end # Per section entry execution of task. def initiate( section ) @section = CascadingOpenObject.new( section ) task_properties = {} # needed? #self.class.task_only_properties.each { |t| section[t] ||= nil } task_properties = CascadingOpenObject.new( section ) task_properties.__parent__ = master @task = task_properties # deprecate init ? if respond_to?( :init ) if method(:init).arity == 0 init else init( *@args ) end end if method(:run).arity == 0 run else run( *@args ) end end # interface #def init # raise "not implemented for '#{task_name}' task" #end def run raise "no action defined for task #{task_name}" end # Task support methods def use_subsection( name ) subsection = @section.__send__(name) if subsection subsection.__parent__ = @section @task = subsection end end def sh( arg ) puts arg system arg unless $PRETEND end def ask( question, answers=nil ) print "#{question}" print " [#{answers}] " if answers until inp = $stdin.gets[0,1] ; sleep 1 ; end ; puts inp end def tell( statement ) puts statement end def provide_setup_rb return true if File.exists?( 'setup.rb') # copy from data dir to current directory (Won't work with Gem!) f = File.join( Config::CONFIG['datadir'], 'reap', 'setup_rb', 'setup.rb' ) if File.exists?(f) File.cp( f, '.' ) true else nil end end end #class Task end #module Reap