From: Trans Date: 2006-08-18T05:20:08+09:00 Subject: Re: using a module at the toplevel doesn't work Pit, Pit Capitain wrote: > Trans schrieb: > > Unfortuately when I use this to make a Rake command-line emulator I > > want to use the module at the toplevel and can't. > > Tom, I think I still don't get what your problem is. If you want to use > a module at the toplevel just include it in Object. The module is for module/class level. It uses #define_method and #ancestors. Including it into Object just gives: ./taskable.rb:184:in `task': undefined method `define_method' for main:Object (NoMethodError) Using extend makes the methods not available at the toplevel. I pasted the code base I'm working on below. To try it, create a task and call it: task :foo do puts "foo" end foo T. # = taskable.rb # # == Copyright (c) 2006 Thomas Sawyer # # Ruby License # # This module is free software. You may use, modify, and/or redistribute this # software under the same terms as Ruby. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. # # == Auhtor(s) # # * Thomas Sawyer # Author:: Thomas Sawyer # Copyright:: Copyright (c) 2006 Thomas Sawyer # License:: Ruby License require 'tsort' # = TSort # # TSort is used to determine which prerequisites to run # for a task. # # Here we add a convenience method --if such a long method # name can be said to be convenient ;) NOTE: Although I ended # up not using this after all --it still may proved useful and # may eventually be added to Facets or submitted as a patch. module TSort def strongly_connected_components_from(node) result = [] each_strongly_connected_component_from(node) do |component| result << component end result end end # = Task # # A Task is a Method with prerequisite dependencies. # # NOTE Can't subclass Method b/c Method has no initiator. # Not sure that would be a good idea anyway. class Task attr_accessor :description def initialize( name, container, preq, desc=nil, &action ) @name = name @container = container @prerequisite = preq || [] @description = desc @action = action end def prerequisite @prerequisite ||= [] end # #-- # # TODO Problem is 'a' can't take any parameters, # # and there's no way to sort the tasks # # from here. FIX if possible. # #++ # def to_proc # r = @prerequisite # n = @name # a = @action # lambda { # r.each{ |d| send(d) unless n == d } # instance_eval &a if a # } # end def complete # ensure no cycles in the graph and extract call graph #own = (class << self; self; end) # in case there are singletons? t = Graph.new( container, name ) t.each_strongly_connected_component_from( name ) do |c| #@container.instance_task(c).call @container.send("->#{d}") end end def call @action.call end def each(&blk) @prerequisite.each(&blk) end # Task::Graph is a Tarjan-sorted hash. # It is used to generate the proper dependency chains. # TODO Make Multiton off of base? class Graph < Hash include TSort def initialize( base, name ) @base = base.class @name = name instance_map( name ) end def instance_map( name ) t = @base.instance_task(name) raise "task does not exist -- #{name} in #{@base}" unless t self[name] = t.prerequisite || [] self[name].each { |r| instance_map( r ) } end alias_method :tsort_each_node, :each_key def tsort_each_child(node, &block) fetch(node).each(&block) end end end # = Taskable # # The Taskable module provides a generic task system # patterned after Rake, but useable in any # code context --not just with the Rake tool. In other # words one can create methods with dependencies. # # NOTE Unlike methods, tasks can't take parameters # if they are to be used as prerequisites. They can # pass on the parameters of the original call though. module Taskable # Define description for subsequent task. def desc(line=nil) return @last_description unless line @last_description = line.gsub("\n",'') end # Use up the description for subsequent task. def desc! l = @last_description @last_description = nil l end # Define a task. def task(args, &action) if Hash === args raise ArgumentError, "#{args.size} for 1" if args.size != 1 name, *deps = *(args.to_a.flatten) name = name.to_sym deps = deps.compact.collect{ |e| e.to_sym } else name, deps = args.to_sym, [] end # create task by creating core and callable methods. # We do it this way b/c otherwise we'd have to use # instance_eval and then can't pass paramaters (TODO fix w/Ruby 1.9). if action define_method( "->#{name}", &action ) # core of the task define_method( name ) do |*args| # ensure no cycles in the graph and extract call graph #own = (class << self; self; end) # in case there are singletons? todolist = Task::Graph.new( self, name ) todolist.each_strongly_connected_component_from( name ) do |c| send("->#{c}", *args) end end instance_task_table[name] = Task.new( name, self, deps, desc!, &action ) elsif m = instance_task(name) #m.description = desc! # TODO should this apply only if there is an action? m.prerequisite.concat deps unless deps.empty? m else #define_method( "->#{name}", lambda{} ) # empty action define_method( name ) do |*args| # ensure no cycles in the graph and extract call graph #own = (class << self; self; end) # in case there are singletons? todolist = Task::Graph.new( self, name ) todolist.each_strongly_connected_component_from( name ) do |c| send("->#{c}", *args) unless "#{c}" == "#{name}" end end instance_task_table[name] = Task.new( name, self, deps, desc!, &action ) end end # Access a task. def instance_task( name ) name = name.to_sym if instance_task_table and r = instance_task_table[name] return r end ancestors.each do |anc| itt = anc.instance_task_table #instance_variable_get("@instance_task_table") if itt and r = itt[name] return r end end nil end # List of task names. def instance_tasks( include_ancestors=true ) if include_ancestors and ancestors and ancestors[1] instance_task_table.keys | ancestors[1].instance_tasks else instance_task_table.keys end end protected def instance_task_table @instance_task_table ||= {} end end class Module include Taskable end include Taskable