Hash auto-true ({:a} == {:a => true})

W

Woody Peterson

I have a unix-inspired desire to pass arguments to a method where some
are toggle flags and some are values, aka "bash$ method --flag
--config=/etc/method". I can think of a few ways to do it that are
close, but if I had a hash that initiated value-less keys to true, I
would have it.

Example:

method:)flag, :another, :config => "/etc/whatever")

For some of the functionality I colud use *args and check
args.include?:)foo), but then I couldn't pass in key-value pairs easily.

The closest way would be to use *args to pass in things of the form
method:)flag, [:config, "/etc/whatever"]), then use args.include?:)flag)
for flags and args.assoc:)config)[1] for value-based options.

Example:

method:)flag, [:config, "/etc/whatever"])

ex. code:

if args.include?:)flag) do; stuff; end
if args.assoc:)config) do; @config = args.assoc:)config)[1]; end

I'd have to make the args.assoc logic prettier with a helper method to
set up default values, but that's the basics of it. Much easier/prettier
if I could modify Hash to auto-assign empty keys to true. Note that
there's a way to set the default value for empty keys from nil to
another value (and even a proc), but this wouldn't be what I want. I
want empty keys to be nil, and specified lone keys set to true.

Thoughts? Anybody else think this would be really handy, or am I missing
something?
 
J

James Coglan

[Note: parts of this message were removed to make it a legal post.]

2008/9/8 Woody Peterson said:
I have a unix-inspired desire to pass arguments to a method where some
are toggle flags and some are values, aka "bash$ method --flag
--config=/etc/method". I can think of a few ways to do it that are
close, but if I had a hash that initiated value-less keys to true, I
would have it.



If you make the restriction that flags always come first you could do
something like this:


def my_method(arg1, arg2, *args)
options = (Hash === args.last) ? args.pop : {}
puts args.inspect
puts options.inspect
end

my_method('foo', 'bar', :flag, :value => 'something')
[:flag]
{:value=>"something"}

my_method('foo', 'bar', :flag)
[:flag]
{}

my_method('foo', 'bar', :value => 'something')
[]
{:value=>"something"}


Then you can check for flags using args.include?:)foo), or you could loop
over them and set them to true in the hash:

args.each { |key| options[key] = true }
 
J

James Coglan

[Note: parts of this message were removed to make it a legal post.]
Now I'm wondering what it would take to define a hash explode (sidenote:
is that what * on *args is called? explode?). How would one go about
defining something like {}args that explodes args into a
true-for-value-less hash (even using the code you posted)? I'm not sure
exactly how *args works internally, and it's pretty hard to find info
about it, actually. Any idea what kind of method it is, and how would
you define another one?



Far as I know, the *args is part of Ruby's syntax, it's not a method that
you can change. Even if you could change it, I've no idea which class the
method would be in. The best you can do is use *args to mean "the remaining
args" and pop the last one off if it's a hash.

You could add a method in Hash to set one value to many keys:

class Hash
def set_many(value, *keys)
keys.each { |key| self[key] = value }
end
end

Then do the following:

def my_method(arg1, arg2, *args)
options = (Hash === args.last) ? args.pop : {}
options.set_many(true, *args)
# ...
end
 
J

James Coglan

[Note: parts of this message were removed to make it a legal post.]
You could add a method in Hash to set one value to many keys:

class Hash
def set_many(value, *keys)
keys.each { |key| self[key] = value }
end
end

Then do the following:

def my_method(arg1, arg2, *args)
options = (Hash === args.last) ? args.pop : {}
options.set_many(true, *args)
# ...
end


Or this, to build the hash from the args array:

module Enumerable
def to_option_hash(value = true)
ary = entries
options = (Hash === ary.last) ? ary.pop : {}
ary.each { |key| options[key] = value }
options
end
end

Then you can do:

def my_method(arg1, arg2, *options)
options = options.to_option_hash
# ...
end
 
W

Woody Peterson

def my_method(arg1, arg2, *options)
options = options.to_option_hash
# ...
end

That's pretty cool. I was thinking something similar, but got stuck on
naming the method :p

The advantage of this strategy over args.include?:)flag) is that it's
backwards compatible with my_method:)flag => true, :config =>
"whatever"). Also, although I'm sure it's negligible, after you hash it
you have a seek time of O(1), whereas I assume you have a seek time of
O(n) every time you call include?(). But mostly the backwards
compatibility.
 
W

Woody Peterson

backwards compatible with my_method:)flag => true, :config =>
"whatever")

err, following your examlpe, my_method("arg1", "arg2", :flag => true,
:config => "whatever").
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,767
Messages
2,569,570
Members
45,045
Latest member
DRCM

Latest Threads

Top