+4  A: 

Ok, here's some metaprogramming that will do what you want (in ruby18 or ruby19)

def create_task(taskname, &execute_body)
  taskclass = :"#{taskname}Task"
  taskmethod = taskname.to_s.downcase.to_sym
  # open up the metaclass for main
  (class << self; self; end).class_eval do
    # can't pass a default to a block parameter in ruby18
    define_method(taskmethod) do |*args, &block|
      # set default name if none given
      args << taskmethod if args.empty?
      Albacore.const_get(taskclass).new(*args, &block)
    end
  end
  Albacore.const_set(taskclass, Class.new(Albacore::AlbacoreTask) do
    define_method(:execute, &execute_body)
  end)
end

create_task :AssemblyInfo do |name|
  asm = AssemblyInfo.new
  asm.load_config_by_task_name(name)
  call_task_block(asm)
  asm.write
  fail if asm.failed
end

The key tools in the metaprogrammers tool box are:

  • class<<self;self;end - to get at the metaclass for any object, so you can define methods on that object
  • define_method - so you can define methods using current local variables

Also useful are

  • const_set, const_get: allow you to set/get constants
  • class_eval : allows you to define methods using def as if you were in a class <Classname> ... end region
rampion
If you want the ability to auto-capitalize etc, you can find that functionality in ActiveSupport.
Justice
i'm using Ruby 1.8.6... i think this solution is for 1.9+
Derick Bailey
ActiveSupport can only autocapitalize when you use underscore delimiters, which Derick didn't seem to want.
rampion
@Derick Bailey: The code as is now is 1.8.6 compliant.
rampion
Derick Bailey
+1  A: 

Something like this, tested on ruby 1.8.6:

class String
  def camelize
    self.split(/[^a-z0-9]/i).map{|w| w.capitalize}.join
  end
end

class AlbacoreTask; end

def create_task(name, &block)
  klass = Class.new AlbacoreTask
  klass.send :define_method, :execute, &block
  Object.const_set "#{name.to_s.camelize}Task", klass
end

create_task :test do |name|
  puts "test: #{name}"
end

testing = TestTask.new
testing.execute 'me'

The core piece is the "create_task" method, it:

  • Creates new class
  • adds execute method
  • Names the class and exposes it
nightshade427