Skip to content
Browse files

added ConfigTask and tasc declaration syntax

  • Loading branch information...
1 parent 4a19133 commit eb3dfbfe1b7416c296568165129ddba3517920c8 @thinkerbot committed Feb 24, 2011
Showing with 269 additions and 3 deletions.
  1. +1 −0 lib/rake.rb
  2. +1 −3 lib/rake/application.rb
  3. +95 −0 lib/rake/config_task.rb
  4. +25 −0 lib/rake/dsl_definition.rb
  5. +147 −0 test/lib/config_task_test.rb
View
1 lib/rake.rb
@@ -48,6 +48,7 @@
require 'rake/file_task'
require 'rake/file_creation_task'
require 'rake/multi_task'
+require 'rake/config_task'
require 'rake/dsl_definition'
require 'rake/file_utils_ext'
require 'rake/file_list'
View
4 lib/rake/application.rb
@@ -553,13 +553,11 @@ def collect_tasks
current = []
when /^(\w+)=(.*)$/
ENV[$1] = $2
- when /^-/
- next
else
if current
current << arg
else
- @top_level_tasks << arg
+ @top_level_tasks << arg unless arg =~ /^-/
end
end
end
View
95 lib/rake/config_task.rb
@@ -0,0 +1,95 @@
+require 'rake/task.rb'
+require 'optparse'
+
+module Rake
+ # #########################################################################
+ # A ConfigTask is a task that allows simple command line configurations to
+ # be passed to it. Configurations are parsed as options from the task args.
+ #
+ class ConfigTask < Task
+
+ # An OptionParser containing the configs for self.
+ def parser
+ @parser ||= OptionParser.new do |opts|
+ opts.banner = "Usage: rake -- #{name} [options] #{arg_names.join(' ')}"
+ opts.on_tail("-h", "--help", "Display this help message.") do
+ puts opts
+ exit
+ end
+ end
+ end
+
+ def invoke(*args)
+ parser.parse!(args)
+ super(*args)
+ end
+
+ def config
+ @config ||= {}
+ end
+
+ def [](key)
+ config[key.to_sym]
+ end
+
+ def method_missing(sym, *args, &block)
+ sym = sym.to_sym
+ config.has_key?(sym) ? config[sym] : super
+ end
+
+ def set_arg_names(args)
+ while options = parse_options(args.last)
+ set_options(options)
+ args.pop
+ end
+ @arg_names = args.map { |a| a.to_sym }
+ end
+
+ def parse_options(obj)
+ case obj
+ when Array then [obj]
+ else nil
+ end
+ end
+
+ def set_options(options)
+ options.each do |(key, default, *option)|
+ default = false if default.nil?
+ option = guess_option(key, default) if option.empty?
+
+ config[key.to_sym] = default
+ parser.on(*option) do |value|
+ config[key.to_sym] = parse_config_value(default, value)
+ end
+ end
+ end
+
+ # Guess the option declaration for a config given a key and default
+ # value, according to the following logic:
+ #
+ # default opt
+ # false ['--key']
+ # true ['--[no-]key']
+ # (obj) ['--key KEY']
+ #
+ def guess_option(key, default)
+ case default
+ when false
+ ["--#{key}"]
+ when true
+ ["--[no-]#{key}"]
+ else
+ ["--#{key} [#{key.to_s.upcase}]"]
+ end
+ end
+
+ def parse_config_value(default, value)
+ case default
+ when String then value.to_s
+ when Integer then value.to_i
+ when Float then value.to_f
+ else value
+ end
+ end
+ end
+end
View
25 lib/rake/dsl_definition.rb
@@ -23,6 +23,31 @@ def task(*args, &block)
Rake::Task.define_task(*args, &block)
end
+ # Declare a config task. Configs are defined as arrays of [key, default,
+ # *optargs] that follow the argument list.
+ #
+ # Example:
+ #
+ # tasc :say, :object, [:msg, 'hello'] do |config, args|
+ # puts "#{config.msg} #{args.object}"
+ # end
+ #
+ # Prerequisites may still be declared using a trailing hash in the form
+ # {:needs => [prequisites]}.
+ #
+ def tasc(*args, &block)
+ # adding an explicit needs hash ensures args will be equivalent to one
+ # of these patterns (thereby making argument resolution predictable)
+ #
+ # tasc :name => :need
+ # tasc :name => [:needs]
+ # tasc :name, args_or_configs..., {:needs => [:needs]}
+ #
+ unless args.last.kind_of?(Hash)
+ args << {:needs => []}
+ end
+ Rake::ConfigTask.define_task(*args, &block)
+ end
# Declare a file task.
#
View
147 test/lib/config_task_test.rb
@@ -0,0 +1,147 @@
+#!/usr/bin/env ruby
+
+require 'test/unit'
+require 'rake'
+
+######################################################################
+# Note that this class is named oddly to fix an order issue in the tests.
+# When named without the 'Z', a TestClean test fails like so:
+#
+# test_clean(TestClean):
+# RuntimeError: Don't know how to build task 'clean'
+# ./lib/rake/task_manager.rb:49:in `[]'
+# ./lib/rake/task.rb:298:in `[]'
+# ./test/lib/clean_test.rb:10:in `test_clean'
+#
+class ZConfigFileTask < Test::Unit::TestCase
+ include Rake
+
+ def setup
+ super
+ Task.clear
+ Rake::TaskManager.record_task_metadata = true
+ end
+
+ def teardown
+ Rake::TaskManager.record_task_metadata = false
+ super
+ end
+
+ def test_no_args_given
+ t = tasc :t
+ assert_equal [], t.arg_names
+ assert_equal({}, t.config)
+ end
+
+ def test_args_given
+ t = tasc :t, :a, :b
+ assert_equal [:a, :b], t.arg_names
+ assert_equal({}, t.config)
+ end
+
+ def test_configs_given
+ t = tasc :t, [:one, 'one'], [:two, 'two']
+ assert_equal [], t.arg_names
+ assert_equal({:one => 'one', :two => 'two'}, t.config)
+ end
+
+ def test_args_and_configs_given
+ t = tasc :t, :a, :b, [:one, 'one'], [:two, 'two']
+ assert_equal [:a, :b], t.arg_names
+ assert_equal({:one => 'one', :two => 'two'}, t.config)
+ end
+
+ def test_name_and_needs
+ t = tasc(:t => [:pre])
+ assert_equal "t", t.name
+ assert_equal [], t.arg_names
+ assert_equal({}, t.config)
+ assert_equal ["pre"], t.prerequisites
+ end
+
+ def test_name_and_explicit_needs
+ t = tasc(:t, :needs => [:pre])
+ assert_equal "t", t.name
+ assert_equal [], t.arg_names
+ assert_equal({}, t.config)
+ assert_equal ["pre"], t.prerequisites
+ end
+
+ def test_name_args_and_explicit_needs
+ t = tasc(:t, :x, :y, :needs => [:pre])
+ assert_equal "t", t.name
+ assert_equal [:x, :y], t.arg_names
+ assert_equal({}, t.config)
+ assert_equal ["pre"], t.prerequisites
+ end
+
+ def test_name_configs_and_explicit_needs
+ t = tasc(:t, [:one, 'one'], [:two, 'two'], :needs => [:pre])
+ assert_equal "t", t.name
+ assert_equal [], t.arg_names
+ assert_equal({:one => 'one', :two => 'two'}, t.config)
+ assert_equal ["pre"], t.prerequisites
+ end
+
+ def test_name_args_configs_and_explicit_needs
+ t = tasc(:t, :x, :y, [:one, 'one'], [:two, 'two'], :needs => [:pre])
+ assert_equal "t", t.name
+ assert_equal [:x, :y], t.arg_names
+ assert_equal({:one => 'one', :two => 'two'}, t.config)
+ assert_equal ["pre"], t.prerequisites
+ end
+
+ def test_tasc_can_access_arguments_and_configs
+ t = tasc(:t, :a, :b,
+ [:one, 'one'],
+ [:two, 'two']
+ ) do |tt, args|
+ assert_equal({:one => 'ONE', :two => 'two'}, t.config)
+ assert_equal 'ONE', tt[:one]
+ assert_equal 'two', tt[:two]
+ assert_equal 'ONE', t.one
+ assert_equal 'two', t.two
+
+ assert_equal({:a => 1, :b => 2}, args.to_hash)
+ assert_equal 1, args[:a]
+ assert_equal 2, args[:b]
+ assert_equal 1, args.a
+ assert_equal 2, args.b
+ end
+ t.invoke(1, '--one', 'ONE', 2)
+ end
+
+ def test_tasc_handles_integers
+ t = tasc :t, [:key, 1]
+ t.invoke('--key', '8')
+ assert_equal 8, t.key
+ end
+
+ def test_tasc_hanldes_floats
+ t = tasc :t, [:key, 1.1]
+ t.invoke('--key', '8.8')
+ assert_equal 8.8, t.key
+ end
+
+ def test_tasc_handles_flags
+ t = tasc :t, [:key, false]
+ t.invoke('--key')
+ assert_equal true, t.key
+ end
+
+ def test_tasc_handles_switches
+ t = tasc :t, [:key, true]
+ t.invoke('--key')
+ assert_equal true, t.key
+
+ t = tasc :t, [:key, true]
+ t.invoke('--no-key')
+ assert_equal false, t.key
+ end
+
+ def test_tasc_can_handle_optparse_patterns
+ t = tasc :t, [:key, [], "--key x,y,z", Array]
+ t.invoke('--key', 'a,b')
+ assert_equal ['a', 'b'], t.key
+ end
+end

0 comments on commit eb3dfbf

Please sign in to comment.
Something went wrong with that request. Please try again.