Skip to content

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
qrush commented May 3, 2011

This is awesome. Please merge!

@sikachu
sikachu commented May 3, 2011

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
qrush commented May 3, 2011

@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
kronn commented Jun 14, 2011

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
kronn commented Jun 14, 2011

@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
tapajos commented Feb 23, 2012

Please, merge it.

@twe4ked
twe4ked commented Feb 23, 2012

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
ixti commented Mar 19, 2013

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 Apr 28, 2013
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.