Too many default argument values!

T

Tim Hunter

Looking for coding style advice...

I'm trying to write a method that describes a rectangle. A rectangle is
defined by the x,y coordinates of its upper-left corner and its width and
height. These values are required. Optionally, the rectangle can have
"styles," like the fill color and the stroke color. Also optionally, the
rectangle can have rounded corners, if you specify how much rounding you
want in the x and y directions. (The default is square corners.)

The method I started with is:

def rect(x, y, width, height, rx=0, ry=0, styles=nil)
# blah, blah, blah
end

Where the styles argument (if present) is a Hash formed by the usual
trailing key=>value pairs. All very standard Ruby.

I was thinking that you'd create a square-corner rectangle like this:

canvas.rect(10, 10, 20, 30, :fill=>'black', :stroke='red')

But of course that doesn't work, because the styles hash
"{:fill=>'black', :stroke=>'red'}" gets assigned to rx, not to styles.

Of course, within rect I could test the class of rx and/or ry and if it's a
Hash, assign it to styles, but that seems kludgy and insufficiently
Rubyish. I could divide rect into two methods, rect and rounded_rect, but
that means that the user has to remember an extra method name. I could make
rx and ry required arguments. Blech.

Thoughts?
 
F

Florian Gross

Tim said:
[snip]
The method I started with is:

def rect(x, y, width, height, rx=0, ry=0, styles=nil)
# blah, blah, blah
end

Where the styles argument (if present) is a Hash formed by the usual
trailing key=>value pairs. All very standard Ruby.

I was thinking that you'd create a square-corner rectangle like this:

canvas.rect(10, 10, 20, 30, :fill=>'black', :stroke='red')

But of course that doesn't work, because the styles hash
"{:fill=>'black', :stroke=>'red'}" gets assigned to rx, not to styles.

Of course, within rect I could test the class of rx and/or ry and if it's a
Hash, assign it to styles, but that seems kludgy and insufficiently
Rubyish. I could divide rect into two methods, rect and rounded_rect, but
that means that the user has to remember an extra method name. I could make
rx and ry required arguments. Blech.

Thoughts?

def rect(x, y, width, height, *more)
styles = more.pop || {}
rx = more.shift || 0
ry = more.shift || 0
end

This will however force you to specify the styles when you want to use a
custom rx/ry.

In general I guess it would be a good idea to just move rx/ry inside the
styles hash.

Regards,
Florian Gross
 
T

trans. (T. Onoma)

My word I'm looking at almost the same problem! I wish we had real named
parameters. But in the mean time try:

def rect(x, y, width, height)
# ...
yield(self)
# ...
end

rect(10,10,100,100){ |r| r.rx=0; r.ry=0; r.fill='red' }

At least I think that's how it's done. For even more "inner" control:

def rect(x, y, width, height, &yld)
# ...
instance_eval &yld
# ...
end

rect(10,10,100,100){ @rx=0; @ry=0; @fill='red' }

These aren't tested so sorry if I made any mistakes, but you get the idea at
least.

T.


|
| I'm trying to write a method that describes a rectangle. A rectangle is
| defined by the x,y coordinates of its upper-left corner and its width and
| height. These values are required. Optionally, the rectangle can have
| "styles," like the fill color and the stroke color. Also optionally, the
| rectangle can have rounded corners, if you specify how much rounding you
| want in the x and y directions. (The default is square corners.)
|
| The method I started with is:
|
| def rect(x, y, width, height, rx=0, ry=0, styles=nil)
| # blah, blah, blah
| end
|
| Where the styles argument (if present) is a Hash formed by the usual
| trailing key=>value pairs. All very standard Ruby.
|
| I was thinking that you'd create a square-corner rectangle like this:
|
| canvas.rect(10, 10, 20, 30, :fill=>'black', :stroke='red')
|
| But of course that doesn't work, because the styles hash
| "{:fill=>'black', :stroke=>'red'}" gets assigned to rx, not to styles.
|
| Of course, within rect I could test the class of rx and/or ry and if it's a
| Hash, assign it to styles, but that seems kludgy and insufficiently
| Rubyish. I could divide rect into two methods, rect and rounded_rect, but
| that means that the user has to remember an extra method name. I could make
| rx and ry required arguments. Blech.
|
| Thoughts?

--
( o _ カラãƒ
// trans.
/ \ (e-mail address removed)

I don't give a damn for a man that can only spell a word one way.
-Mark Twain
 
T

trans. (T. Onoma)

BTW: you could have different constructors too.

def self.quick_new(x,y,width,height,styles=nil)
self.new(x,y,width,height,0,0,styles)
end

|
| I'm trying to write a method that describes a rectangle. A rectangle is
| defined by the x,y coordinates of its upper-left corner and its width and
| height. These values are required. Optionally, the rectangle can have
| "styles," like the fill color and the stroke color. Also optionally, the
| rectangle can have rounded corners, if you specify how much rounding you
| want in the x and y directions. (The default is square corners.)
|
| The method I started with is:
|
| def rect(x, y, width, height, rx=0, ry=0, styles=nil)
| # blah, blah, blah
| end
|
| Where the styles argument (if present) is a Hash formed by the usual
| trailing key=>value pairs. All very standard Ruby.
|
| I was thinking that you'd create a square-corner rectangle like this:
|
| canvas.rect(10, 10, 20, 30, :fill=>'black', :stroke='red')
|
| But of course that doesn't work, because the styles hash
| "{:fill=>'black', :stroke=>'red'}" gets assigned to rx, not to styles.
|
| Of course, within rect I could test the class of rx and/or ry and if it's a
| Hash, assign it to styles, but that seems kludgy and insufficiently
| Rubyish. I could divide rect into two methods, rect and rounded_rect, but
| that means that the user has to remember an extra method name. I could make
| rx and ry required arguments. Blech.
|
| Thoughts?

--
( o _ カラãƒ
// trans.
/ \ (e-mail address removed)

I don't give a damn for a man that can only spell a word one way.
-Mark Twain
 
R

Robert Klemme

Tim Hunter said:
Looking for coding style advice...

I'm trying to write a method that describes a rectangle. A rectangle is
defined by the x,y coordinates of its upper-left corner and its width and
height. These values are required. Optionally, the rectangle can have
"styles," like the fill color and the stroke color. Also optionally, the
rectangle can have rounded corners, if you specify how much rounding you
want in the x and y directions. (The default is square corners.)

The method I started with is:

def rect(x, y, width, height, rx=0, ry=0, styles=nil)
# blah, blah, blah
end

Where the styles argument (if present) is a Hash formed by the usual
trailing key=>value pairs. All very standard Ruby.

I was thinking that you'd create a square-corner rectangle like this:

canvas.rect(10, 10, 20, 30, :fill=>'black', :stroke='red')

But of course that doesn't work, because the styles hash
"{:fill=>'black', :stroke=>'red'}" gets assigned to rx, not to styles.

Of course, within rect I could test the class of rx and/or ry and if it's a
Hash, assign it to styles, but that seems kludgy and insufficiently
Rubyish. I could divide rect into two methods, rect and rounded_rect, but
that means that the user has to remember an extra method name. I could make
rx and ry required arguments. Blech.

Thoughts?
 
R

Robert Klemme

Tim Hunter said:
Looking for coding style advice...

I'm trying to write a method that describes a rectangle. A rectangle is
defined by the x,y coordinates of its upper-left corner and its width and
height. These values are required. Optionally, the rectangle can have
"styles," like the fill color and the stroke color. Also optionally, the
rectangle can have rounded corners, if you specify how much rounding you
want in the x and y directions. (The default is square corners.)

The method I started with is:

def rect(x, y, width, height, rx=0, ry=0, styles=nil)
# blah, blah, blah
end

Where the styles argument (if present) is a Hash formed by the usual
trailing key=>value pairs. All very standard Ruby.

I was thinking that you'd create a square-corner rectangle like this:

canvas.rect(10, 10, 20, 30, :fill=>'black', :stroke='red')

But of course that doesn't work, because the styles hash
"{:fill=>'black', :stroke=>'red'}" gets assigned to rx, not to styles.

Of course, within rect I could test the class of rx and/or ry and if it's a
Hash, assign it to styles, but that seems kludgy and insufficiently
Rubyish. I could divide rect into two methods, rect and rounded_rect, but
that means that the user has to remember an extra method name. I could make
rx and ry required arguments. Blech.

Thoughts?

Put all (optional) args into the hash and use a default hash

def rect(x, y, width, height, args={:rx=>0, :ry=>0})
# blah, blah, blah
p x
p y
p width
p height
p args
end

Alternative with slightly better performance:

DEFAULT_RECT_ARGS = {
:rx => 0,
:ry => 0,
}.freeze

def rect(x, y, width, height, args=DEFAULT_RECT_ARGS)
....
10
10
20
30
{:stroke=>"red", :fill=>"black"}

or even

def rect(args=DEFAULT_RECT_ARGS)
....

Kind regards

robert
 
R

Robert Klemme

Matt Maycock said:
Putting everything in a default hash is sort of a bad idea.

------------------------- code -------------------------
[ummaycoc@localhost ummaycoc]$ ruby -e '
DEFAULT_ARGS = {:foo => :meow, :santa => :love}.freeze
puts("-" * 50)
p DEFAULT_ARGS
def test(arg1, opt_args=DEFAULT_ARGS)
puts "Test: #{opt_args.inspect}"
end

puts
test:)hey)
puts
test:)two, :foo => 1, :ruby => :language)'
--------------------------------------------------
{:santa=>:love, :foo=>:meow}

Test: {:santa=>:love, :foo=>:meow}

Test: {:ruby=>:language, :foo=>1}
------------------------- end code -------------------------

Actually using it, you lose the fact that santa maps to love (thus
everything in the `optional' hash is `mandatory')

a much better idea would be:

DEFAULT_HASH = { ... }
def myFunc(arg1, ..., argN, optHash={})
DEFAULT_HASH.keys.each {|k|
optHash[k] = DEFAULT_HASH[k] unless optHash.include?(k)
}
....
end


This gives you what you actually want. I hope.

True. I forgot that. Alternatives:

DEFAULT_HASH = { ... }.freeze
def myFunc(a, b, args={})
@a = a
@b = b
@foo = args[:foo] || DEFAULT_HASH[:foo]
....
end

or

DEFAULT_HASH = { ... }.freeze

def rect(a, b, args = DEFAULT_HASH)
tmp = DEFAULT_HASH.dup.update( args )
...
@a = a
@b = b
@foo = tmp[:foo]
...
end

Kind regards

robert
 
J

James Edward Gray II

a much better idea would be:

DEFAULT_HASH = { ... }
def myFunc(arg1, ..., argN, optHash={})
DEFAULT_HASH.keys.each {|k|
optHash[k] = DEFAULT_HASH[k] unless optHash.include?(k)
}
....
end

Or slightly simpler:

DEFAULT_OPTIONS = { ... }
def my_func( arg1, ... argN, options = { } )
options = DEFAULT_OPTIONS.merge(options)

# ...
end

James Edward Gray II
 
R

Robert Klemme

James Edward Gray II said:
a much better idea would be:

DEFAULT_HASH = { ... }
def myFunc(arg1, ..., argN, optHash={})
DEFAULT_HASH.keys.each {|k|
optHash[k] = DEFAULT_HASH[k] unless optHash.include?(k)
}
....
end

Or slightly simpler:

DEFAULT_OPTIONS = { ... }
def my_func( arg1, ... argN, options = { } )
options = DEFAULT_OPTIONS.merge(options)

# ...
end

James Edward Gray II
 
R

Robert Klemme

Sorry for the empty post.


James Edward Gray II said:
a much better idea would be:

DEFAULT_HASH = { ... }
def myFunc(arg1, ..., argN, optHash={})
DEFAULT_HASH.keys.each {|k|
optHash[k] = DEFAULT_HASH[k] unless optHash.include?(k)
}
....
end

Or slightly simpler:

DEFAULT_OPTIONS = { ... }
def my_func( arg1, ... argN, options = { } )
options = DEFAULT_OPTIONS.merge(options)

Thanks for that hint! I didn't know Hash#merge yet.

robert
 
M

Mohammad Khan

I had same issue,
I had couple of ideas, of course one was using Hash.

I tried to accomplish it another way,

class Canvas

attr_accessor :args

def rect
# Draw canvus using
# args.width
# args.length
# args.fill-color
end

def args
if @args.nil?
@args = Arguments.new
end
return @args
end
end

class Arguments < Canvas
attr_accessor :width, :length, :fill-color, :border-color

def initialize
return self
end
end

canvas = Canvas.new
canvas.args.width = 5
canvas.args.height = 10
..
..
..
canvas.args.fill-color = 'Red'
canvas.args.border-color = 'Black'

canvas.rect


Is it too complicated ? !!

Mohammad
 

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

Forum statistics

Threads
473,995
Messages
2,570,236
Members
46,823
Latest member
Nadia88

Latest Threads

Top