Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

Config tasks proposal #24

Closed
wants to merge 5 commits into from
@thinkerbot

Developers often use rake to build project-specific command line tools, even with alternatives like Thor out there. For these it would be nice to provide task configurations without ENV variables, and to be able to use a more standard 'task arg arg' syntax rather than 'task[arg,arg]'.

To do so I propose commits in this pull request, which adds a new tasc syntax for ConfigTasks. For example here is a command line tool to write a greeting. It uses args, configs, and illustrates that simple config types are automatically converted to appropriate objects; String, Integer, Float, and true/false (for flags and switches) are supported.

require 'rake'

desc "welcome a thing"
tasc :welcome, :thing, %{
  -m,--message [hello]  : A welcome message
  -n [1]                : Number of times to repeat
} do |config, args|
  config.n.times do 
    puts "#{config.message} #{args.thing}"
  end
end

These tasks may be invoked using the current syntax.

% rake welcome[world]
hello world
% rake welcome[--message,goodnight,-n,3,moon]
goodnight moon
goodnight moon
goodnight moon

Alternatively, the first commit in this request adds a new command line syntax whereby args after a '--' are re-written into the current syntax. For example:

% rake -- welcome world
hello world
% rake -- welcome --message goodnight -n 3 moon
goodnight moon
goodnight moon
goodnight moon
% rake -- welcome --help
Usage: rake -- welcome [options] object
    -m, --message [hello]            A welcome message
    -n [1]                           Number of times to repeat
    -h, --help                       Display this help message.

The new syntax and ConfigTask class do not interfere with any of the current functionality AFAIK, and does not add dependencies. If you want I can clean up these commits into two separate pulls -- one for the 'option' syntax and one for ConfigTask. In addition, I recognize ConfigTask could use a little more internal documentation which I will add if you intend to pull in these changes.

Thanks! Hope you like the proposal!

@qrush

This is awesome. Please merge!

@sikachu

I really wish that this got merged into the core. Way more superior way than having to do rake welcome[--message,goodnight,-n,3,moon] or using the environment variable.

@veryhappythings

As a rather casual user of rake, I think that new users would find the -- in rake -- welcome world pretty difficult to grasp. Much better than the current syntax though!

@qrush

@veryhappythings: The daemons gem has a similar pattern if you want to pass custom arguments:

daemons run -- -s blah

And "-s blah" would get passed along. I think it works pretty well. In any case I think this is better than doing environment variables or the "bracket" syntax which doesn't work in many shells.

@lukaszkorecki

Not entirely convinced about -- but still - huge improvement comparing to what we have now.

+1

@veryhappythings

@qrush: Yeah, I've seen it used in several command line tools in the format rake welcome -- world (as opposed to rake -- welcome world as originally written) - I think that would make sense.

@thinkerbot

A nice thing about the '--' syntax is that it allows multiple tasks to be specified without breaking the existing syntax. These are all the same:

rake welcome[a] welcome[b]
rake welcome[a] -- welcome b
rake -- welcome a -- welcome b

The tasks split along double-dashes. @veryhappythings I think in your variation the args would only be able to apply to the last task mentioned. You would need multiple constructs to specify multiple tasks.

rake welcome[a] welcome -- b

That's not necessarily bad, it's just a bit harder IMO than:

rake OLD_SYNTAX -- NEW_SYNTAX
@jimweirich
Owner

Interesting proposal, and you seem to have a bit of support.

I do like the -- syntax above (but not so wild about the tasc thing).

I want to take this slow, we've gone through several iterations of task arguments and I want to make sure what ever we introduce will wear well with age.

So, I guess I'm considering it.

@kronn

I like the idea in general but I would prefer calling it config_task or something similar in the Rakefile. tasc looks like a typo too me, though I can see the intention there.

Is it possible to have the standard task accept this third argument with options? That would be ideal I think.

@thinkerbot

I can agree with that. Sometime after I made this proposal I started thinking cmd might also be better. I have no objections to renaming everything. @jimweirich - I will happily rework the patches if you have a preference... and glad to hear you are considering this!

@kronn adding the options string is possible with a constraint such that string and symbol args are treated differently, or something like 'if the last non-hash arg is a string then define options'. IMO it's more straightforward to make a new method :)

@kronn

@thinkerbot I would be happy with a new method. The name should be clearly different, then. cmd also is closer to the shell. :)

@richmeyers

The brackets syntax is ugly, brackets are special for the shell and must be escaped, using commas to separate arguments will wreak havoc with arguments that contain commas necessitating yet another escaping scheme.

The second syntax is pretty good. -- should be optional if there are no arguments so that rake task continues to work. After a transition period of 1-2 years rake arguments may be required to appear before the first task name if there is no --, such that -- would not need to be specified at all.

However, I wonder what the ultimate utility is of this proposal. It is already possible to say rake task arg=value with both arg and value in lower case. Is there anything this proposal permits that cannot already be done?

@thinkerbot

@richmeyers for the most part I agree with your opinion but I should clarify that the '--' syntax does nothing to fix the escape issues. Commas cannot be specified using either syntax. Here are some examples - see this gist to run these yourself:

# [Rakefile]
# task :standard_echo, :a, :b do |t, args|
#   message = ENV['MESSAGE'] || 'got'
#   puts "#{message}: #{args.a} #{args.b}"
# end
# 
# cmd  :cmd_echo, :a, :b, %{
#   -m,--message [got]  : A message
# } do |config, args|
#   puts "#{config.message}: #{args.a} #{args.b}"
# end

rake standard_echo[a,b]
# got: a b
rake standard_echo['a,b',c]
# got: a b

rake -- cmd_echo a b
# got: a b
rake -- cmd_echo 'a,b' c
# got: a b

To answer your question, one of the benefits of per-task options is that you can specify different options for different tasks. This is not possible with the ENV syntax, or at least opens you up to collisions because ENV is global. Sometimes you do want global options; this proposal provides per-task options when you don't.

# [Rakefile (continued)]
# task :standard_one do 
#   puts(ENV['MESSAGE'] || 'got')
# end
# task :standard_two do 
#   puts(ENV['MESSAGE'] || 'got')
# end
# 
# cmd :cmd_one, %{
#   -m,--message [got] : A message
# } do |config, args|
#   puts config.message
# end
# cmd :cmd_two, %{
#   -m,--message [got] : A message
# } do |config, args|
#   puts config.message
# end

rake standard_one MESSAGE=hello standard_two MESSAGE=goodbye
# goodbye
# goodbye

rake -- cmd_one -m hello -- cmd_two -m goodbye
# hello
# goodbye
@thinkerbot

Thought of something a bit theoretical in nature but important to point out.

Command tasks are currently constructed to reenable after each invocation so that, unlike standard tasks, they can be treated like shell commands and executed multiple times with different args, ie:

rake -- cmd_echo a b -- cmd_echo x y
# got: a b
# got: x y

Vs

rake standard_echo[a,b] standard_echo[x,y]
# got: a b

This behavior makes pretty good sense when you just use it. However it also breaks the model of dependency-based workflows because command tasks can execute multiple times. If necessary commands can be made to run just once, however I like it for the following reasons:

First, the behavior is what I expect for commands and flows nicely when shifting into the '--' syntax.

Second, and this is the theoretical bit, I think using commands as prerequisites (ie tasks) convolutes two distinct workflows that are best kept separate anyhow.

Consider a command pipeline first, one kind of workflow. Commands can be declared with context (ie args/options) because each command acts like an instance of a class. For instance you can write something like this where each grep has separate context:

grep -e 'one' somefile.txt | grep -e 'two'

By contrast a dependency-based workflow treats each task like a singleton. You declare how tasks depend on one another, perhaps multiple times, and perhaps in multiple places. Because they're singletons it's problematic to add anything but global (ie ENV) context to them.

For instance look how the 'x,y' args in the standard_echo example are lost. There is no way to handle both sets of arguments because two sets implies two instances, and for tasks, there is only one.

I like the different behavior for commands and tasks because sometimes I want a multiple-instance workflow and sometimes I want a singleton workflow. As far as I know, the only issue is that commands can't be used as prerequisites for tasks (but not vice-versa... tasks can be used as prerequisites for commands). Admittedly this is a gotcha, although code could be added to detect and notify users when they try to use commands as tasks.

Food for thought.

@tapajos

Please, merge it.

@twe4ked

The new syntax and ConfigTask class do not interfere with any of the current functionality AFAIK, and does not add dependencies.

I don't see a reason this shouldn't be merged.

@jimweirich
Owner

There seems to be a real desire on some to be able to write procedural-like tasks, and this desire manifests in calls for better arguments to tasks and tasks that can be invoked multiple times.

It seems to me that we already have a pretty good tool for writing these and its called methods. Perhaps all we need is a place to put regular methods that can be invoked from the command line (like a task), arrange for it to easily pick up arguments and then decide how it ties into the task dependency system.

I'm not going to approve this pull request as it stands because I feel it only partially addresses the issues I see being requested. But I'll leave it open for the moment so that the discussion can continue.

@ixti

Just a humble proposal to have this feature closer to DSL we have now:

desc "Task description"
opts :count, "-n", :required => true, :desc => "Arg description"
opts :message, "-m", "--message", :default => "hi", :desc => "More description"
taks :foobar do |t, args|
  puts "#{args[:message}" * args[:count]
end
@jimweirich
Owner

No progress on this ... closing.

@jimweirich jimweirich closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
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
31 lib/rake/application.rb
@@ -122,6 +122,10 @@ def parse_task_string(string)
end
[name, args]
end
+
+ def format_task_string(args)
+ "#{args.shift}[#{args.join(',')}]"
+ end
# Provide standard exception handling for the given block.
def standard_exception_handling
@@ -421,6 +425,12 @@ def handle_options
end
standard_rake_options.each { |args| opts.on(*args) }
+
+ opts.on_tail("--", "Turn on arg syntax ('-- a b c' => 'a[b,c]')") do
+ # restores option break to ARGV if found, allowing arg syntax
+ throw :terminate, "--"
+ end
+
opts.environment('RAKEOPT')
end.parse!
@@ -532,13 +542,30 @@ def standard_system_dir #:nodoc:
# Environmental assignments are processed at this time as well.
def collect_tasks
@top_level_tasks = []
+
+ current = nil
ARGV.each do |arg|
- if arg =~ /^(\w+)=(.*)$/
+ case arg
+ when '--'
+ if current && !current.empty?
+ @top_level_tasks << format_task_string(current)
+ end
+ current = []
+ when /^(\w+)=(.*)$/
ENV[$1] = $2
else
- @top_level_tasks << arg unless arg =~ /^-/
+ if current
+ current << arg
+ else
+ @top_level_tasks << arg unless arg =~ /^-/
+ end
end
end
+
+ if current && !current.empty?
+ @top_level_tasks << format_task_string(current)
+ end
+
@top_level_tasks.push("default") if @top_level_tasks.size == 0
end
View
255 lib/rake/config_task.rb
@@ -0,0 +1,255 @@
+require 'rake/task.rb'
+require 'optparse'
+
+module Rake
+ # #########################################################################
+ # = ConfigTask
+ #
+ # Config tasks allow the creation of tasks that can recieve simple
+ # configurations using command line options. The intention is to allow
+ # command-like tasks to be defined and used in a natural way (ie using a 'name
+ # --flag arg' syntax). For clarity ConfigTasks are declared using 'tasc' and
+ # referred to as 'tascs'.
+ #
+ # Tasc configs are declared using an options string and accessed on the tasc
+ # itself. Numeric types are cast to appropriate values.
+ #
+ # require 'rake'
+ #
+ # desc "welcome a thing"
+ # tasc :welcome, :thing, %{
+ # -m,--message [hello] : A welcome message
+ # -n [1] : Number of times to repeat
+ # } do |config, args|
+ # config.n.times do
+ # puts "#{config.message} #{args.thing}"
+ # end
+ # end
+ #
+ # Then from the command line, invoke after '--':
+ #
+ # % rake -- welcome world
+ # hello world
+ # % rake -- welcome --message goodnight -n 3 moon
+ # goodnight moon
+ # goodnight moon
+ # goodnight moon
+ # % rake -- welcome --help
+ # Usage: rake -- welcome [options] object
+ # -m, --message [hello] A welcome message
+ # -n [1] Number of times to repeat
+ # -h, --help Display this help message.
+ #
+ # Unlike typical tasks which only run once, tascs are reset after each run, so
+ # that they can be invoked multiple times:
+ #
+ # % rake -- welcome world -- welcome moon -m goodnight
+ # hello world
+ # goodnight moon
+ #
+ # Tascs may participate in dependency workflows, although it gets a little
+ # peculiar when other tasks depend upon the tasc. Below is an explanation.
+ # TL;DR; -- tascs may have dependencies, but other tasks/tascs should not depend
+ # upon a tasc.
+ #
+ # == Dependency Discussion
+ #
+ # Normally tasks are designed to be unaware of context (for lack of a better
+ # word). Once you introduce arguments/configs, then suddenly it matters when the
+ # arguments/configs 'get to' a task. For example:
+ #
+ # require 'rake'
+ #
+ # task(:a) { puts 'a' }
+ # task(:b => :a) { puts 'b' }
+ #
+ # tasc(:x, [:letter, 'x']) {|t| puts t.letter }
+ # tasc(:y, [:letter, 'y'], :needs => :x) {|t| puts t.letter }
+ #
+ # There is no order issue for the tasks, for which there is no context and
+ # therefore they can align into a one-true execution order regardless of
+ # declaration order.
+ #
+ # % rake a b
+ # a
+ # b
+ # % rake b a
+ # a
+ # b
+ #
+ # A problem arises, however with tascs that do have a context. Now it matters
+ # what order things get declared in. For example:
+ #
+ # % rake -- x --letter a -- y --letter b
+ # a
+ # a
+ # b
+ # % rake -- y --letter b -- x --letter a
+ # x
+ # b
+ # a
+ #
+ # You can see that declaration order matters for tascs in a way it does not for
+ # tasks. The problem is not caused directly by the decision to make tascs run
+ # multiple times; it's caused by the context which gets interwoven to all
+ # tasks/tascs via dependencies. For example, pretend tascs only executed once...
+ # which arguments/configurations should win in this case?
+ #
+ # % rake -- welcome world -- welcome -m goodnight
+ # # hello world ?
+ # # goodnight ?
+ # # goodnight world ?
+ #
+ # All of this can be avoided by only using tascs as end-points for dependency
+ # workflows and never as prerequisites. This is fine:
+ #
+ # require 'rake'
+ #
+ # task(:a) { print 'a' }
+ # task(:b => :a) { print 'b' }
+ # tasc(:x, [:letter, 'x'], :needs => [:b, :a]) {|t| puts t.letter }
+ #
+ # Now:
+ #
+ # % rake -- x --letter c
+ # abc
+ # % rake a b -- x --letter c
+ # abc
+ # % rake b a -- x --letter c
+ # abc
+ #
+ class ConfigTask < Task
+
+ def parser
+ @parser ||= OptionParser.new do |opts|
+ 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 invoke_with_call_chain(*args)
+ super
+ reenable
+ end
+
+ def reenable
+ @config = nil
+ super
+ end
+
+ def config
+ @configs ||= default_config.dup
+ end
+
+ def default_config
+ @default_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 }
+ parser.banner = "Usage: rake -- #{name} [options] #{@arg_names.join(' ')}"
+ @arg_names
+ end
+
+ def parse_options(obj)
+ case obj
+ when Array then [obj]
+ when String then parse_options_string(obj)
+ else nil
+ end
+ end
+
+ def parse_options_string(string)
+ string = string.strip
+ return nil unless string[0] == ?-
+
+ string.split(/\s*\n\s*/).collect do |str|
+ flags, desc = str.split(':', 2)
+ flags = flags.split(',').collect! {|arg| arg.strip }
+
+ key = guess_key(flags)
+ default = flags.last =~ /\s+\[(.*)\]/ ? guess_default($1) : guess_bool_default(flags)
+
+ [key, default] + flags + [desc.to_s.strip]
+ end
+ end
+
+ def guess_key(flags)
+ keys = flags.collect do |flag|
+ case flag.split(' ').first
+ when /\A-([^-])\z/ then $1
+ when /\A--\[no-\](.*)\z/ then $1
+ when /\A--(.*)\z/ then $1
+ else nil
+ end
+ end
+ keys.compact.sort_by {|key| key.length }.last
+ end
+
+ def guess_default(str)
+ case str
+ when /\A(\d+)\z/ then str.to_i
+ when /\A(\d+\.\d+)\z/ then str.to_f
+ else str
+ end
+ end
+
+ def guess_bool_default(flags)
+ flags.any? {|flag| flag =~ /\A--\[no-\]/ ? true : false }
+ end
+
+ def set_options(options)
+ options.each do |(key, default, *option)|
+ default = false if default.nil?
+ option = guess_option(key, default) if option.empty?
+
+ default_config[key.to_sym] = default
+ parser.on(*option) do |value|
+ config[key.to_sym] = parse_config_value(default, value)
+ end
+ end
+ end
+
+ def guess_option(key, default)
+ n = key.to_s.length
+
+ case default
+ when false
+ n == 1 ? ["-#{key}"] : ["--#{key}"]
+ when true
+ ["--[no-]#{key}"]
+ else
+ n == 1 ? ["-#{key} [#{key.to_s.upcase}]"] : ["--#{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
56 lib/rake/dsl_definition.rb
@@ -23,6 +23,62 @@ def task(*args, &block)
Rake::Task.define_task(*args, &block)
end
+ # Declare a config task. Configs are defined as arrays of [key, default]
+ # pairs that follow the argument list. The default value may be a String,
+ # Integer, Float, true, or false; these values will be cast appropriately.
+ #
+ # Example:
+ #
+ # tasc :welcome, :object, [:msg, 'hello'], [:n, 1] do |config, args|
+ # config.n.times do
+ # puts "#{config.msg} #{args.object}"
+ # end
+ # end
+ #
+ # OptionParser arguments may be specified after the default, if desired:
+ #
+ # Example:
+ #
+ # tasc(:welcome, :object,
+ # [:msg, 'hello', '-m', '--message', 'A welcome message']
+ # [:n, 1, '-n', 'Number of times to repeat']
+ # ) do |config, args|
+ # config.n.times do
+ # puts "#{config.msg} #{args.object}"
+ # end
+ # end
+ #
+ # Configs may also be declared as a string, which allows documentation and
+ # compact specification of shorts. In this syntax the config key will be
+ # equal to the long, if available, or the short if not. The config type
+ # will be guessed from the default string.
+ #
+ # Example:
+ #
+ # tasc :welcome, :object, %{
+ # -m,--message [hello] : A welcome message
+ # -n [1] : Number of times to repeat
+ # } do |config, args|
+ # config.n.times do
+ # puts "#{config.message} #{args.object}"
+ # end
+ # 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
10 test/lib/application_test.rb
@@ -658,6 +658,16 @@ def test_task_collection
assert_equal ["a", "b"], @tasks.sort
end
+ def test_task_collection_of_arg_syntax
+ command_line("a", "--", "b", "c", "d", "--", "x", "y", "z")
+ assert_equal ["a", "b[c,d]", "x[y,z]"], @tasks.sort
+ end
+
+ def test_task_collection_ignores_empty_arg_syntax
+ command_line("a", "--", "--")
+ assert_equal ["a"], @tasks.sort
+ end
+
def test_default_task_collection
command_line()
assert_equal ["default"], @tasks
View
226 test/lib/config_task_test.rb
@@ -0,0 +1,226 @@
+#!/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_configs_are_parsed_from_a_string
+ t = tasc :t, %{
+ -o,--one [ONE] : One doc
+ --two [TWO] : Two doc
+ -t [THREE] : Three doc
+ }
+ assert_equal [], t.arg_names
+ assert_equal({
+ :one => 'ONE',
+ :two => 'TWO',
+ :t => 'THREE'
+ }, t.config)
+
+ assert_equal %q{
+Usage: rake -- t [options]
+ -o, --one [ONE] One doc
+ --two [TWO] Two doc
+ -t [THREE] Three doc
+ -h, --help Display this help message.
+}, "\n" + t.parser.to_s
+ 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_tascs_reenable_after_each_invoke
+ runlist = []
+ t = tasc :t, [:key, 'a'] do |t|
+ runlist << t.key
+ end
+ assert_equal 'a', t.key
+ t.invoke('--key', 'b')
+ assert_equal 'b', t.key
+ t.invoke('--key', 'c')
+ assert_equal 'c', t.key
+ assert_equal ['b', 'c'], runlist
+ 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_treads_single_letter_keys_as_shorts
+ t = tasc :t, [:k, 'value']
+ t.invoke('-k', 'VALUE')
+ assert_equal 'VALUE', t.k
+ 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.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
+
+ def test_tasc_gueses_string_configs
+ t = tasc :t, '--key [vaLue]'
+ assert_equal 'vaLue', t.key
+ t.invoke('--key', '8')
+ assert_equal '8', t.key
+ end
+
+ def test_tasc_allows_empty_string_defaults
+ t = tasc :t, '--key []'
+ assert_equal '', t.key
+ end
+
+ def test_tasc_gueses_integer_configs
+ t = tasc :t, '--key [1]'
+ assert_equal 1, t.key
+ t.invoke('--key', '8')
+ assert_equal 8, t.key
+ end
+
+ def test_tasc_gueses_float_configs
+ t = tasc :t, '--key [1.1]'
+ assert_equal 1.1, t.key
+ t.invoke('--key', '8.8')
+ assert_equal 8.8, t.key
+ end
+
+ def test_tasc_gueses_flags
+ t = tasc :t, '--key'
+ assert_equal false, t.key
+ t.invoke('--key')
+ assert_equal true, t.key
+ end
+
+ def test_tasc_gueses_switches
+ t = tasc :t, '--[no-]key'
+ assert_equal true, t.key
+ t.invoke('--no-key')
+ assert_equal false, t.key
+ end
+end
Something went wrong with that request. Please try again.