Creating a setter method that takes extra arguments in Ruby
It doesn't work because the parser doesn't allow it. An equals sign is allowed in expressions of the form identifier = expression
, expression.identifier = expression
(where identifier is \w+
), expression[arguments] = expression
and expression.[]= arguments
and as part of a string or symbol or character literal (?=
). That's it.
gen.send(:allow=, 'a', 1, false)
would work, but at that point you could as well just give the method a name that doesn't include a =
.
Setter method (assignment) with multiple arguments
Due to the syntax sugar of methods whose names end in=
, the only way that you can actually pass multiple parameters to the method is to bypass the syntax sugar and use send
…
h.send(:strategy=, :mystrategy, :backward )
…in which case you might as well just use a normal method with better names:
h.set_strategy :mystrategy, :backward
However, you could rewrite your method to automatically un-array the values if you knew that an array is never legal for the parameter:
def strategy=( value )
if value.is_a?( Array )
@strategy << value.first
@strategy.direction = value.last
else
@strategy = value
end
end
This seems like a gross hack to me, however. I would use a non-assigment method name with multiple arguments if you need them.
An alternative suggestion: if the only directions are :forward
and :backward
what about:
def forward_strategy=( name )
@strategy << name
@strategy.direction = :forward
end
def reverse_strategy=( name )
@strategy << name
@strategy.direction = :backward
end
What can I do with a setter method that takes no arguments
You can't use it as a setter, and it's not even very useful as a method in general. The only way to even make that method run is to use send
or create a Method instance, because the normal message-send syntax doesn't allow the message to include a "=" character.
So you technically could have code like this:
class Foo
def bar=()
puts "Hi, mom!"
end
end
foo = Foo.new
foo.send(:bar=)
and it would print "Hi, mom!" as expected.
But can you do anything with it? Well, I guess you could brag to all your friends about your nigh-uncallable setter that doesn't set anything. Beyond that, no, it's really not useful for anything.
Alias a bracket setter with extra parameter (index and value)
Given this class
class GameVariables
def []=(key, value)
# do something
end
end
vars = GameVariables.new
you can call the setter in different, equivalent ways. The most common and idiomatic one is this
vars[:abc] = 123
This however is actually using syntactic sugar built into Ruby. In fact, this call actually resolves to this:
vars.[]=(:abc, 123)
This works similar for all the operators like +
, -
, ==
and other types of array/hash accessors. In Ruby, the whole concept of operators is just syntactic sugar that makes the Ruby parser automatically resolve certain calling constructs (like the hash setter) to their actual method calls. This however only works for some specifically named method names, one of which being []=
.
When you want to use the functionality of your original []=
method using another name, you can`t rely on the parser to resolve the syntactic sugar anymore. Thus, your call
foo[variable_id] = value
actually resolves to this:
foo()[variable_id] = value
which obviously passes to few arguments to the method. To actually be able to properly call the foo method, you can use this instead:
foo(variable_id, value)
This uses the plain old method calling syntax without any need for the parser applying any syntactic sugar.
Ruby, how to overwrite the setter (=) method to allow 2 parameters?
As @eugen correctly says, you cannot pass two arguments to a setter like
self.my_vector = 1, 2
The nearest thing you can achieve is to unpack the argument using pattern matching:
def myvector=(arg)
arg => [x, y]
@my_vector = Vector2d.new(x, y)
end
foo.my_vector = [1, 2]
An alternative is to define a simple helper method v
so you can write
foo.my_vector = v 1, 2
Assigning multiple values to setter at once: self.x = (y, z) results in a syntax error
You indeed can't pass more than one argument to a method that ends in =
. A setter method doesn't need to end in =
, though, naturally: you can just do set_title_and_author(title, author)
.
Another alternative would be to have the method take an array:
def set_title_and_author= (title_and_author)
@title, @author = title_and_author
end
#...
book.set_title_and_author= ["Ender's Game", "Orson Scott Card"]
If you do the latter, stylistically I'd recommend removing the set
and just calling the method title_and_author=
. set
is redundant with =
.
Ruby get and set in one method
The trickier case is if you want to set duration to nil. I can think of two ways of doing this
def duration(*args)
@duration = args.first unless args.empty?
@duration
end
Allow people to pass any number of args and decide what to do based on the number. You could also raise an exception if more than one argument is passed.
Another way is
def duration(value = (getter=true;nil))
@duration = value unless getter
@duration
end
This exploits default arguments a little: they can be pretty much any expression.
When called with no arguments getter
is set to true, but when an argument is supplied (even if it is nil) the default value is not evaluated. Because of how local variable scope works getter
ends up nil.
Possibly a little too clever, but the method body itself is cleaner.
How can I create a setter method for a new object with default values?
Make use of default keyword parameters:
def default_product(
title: "default title",
description: "default description",
price: 1,
image_url: 'default_url.png')
Product.new(
title: title,
description: description,
price: price,
image_url: image_url)
end
And call it like: default_product(title: "Another title")
.
Sidenote: the proper approach would be probably to use FactoryBot
.
Related Topics
How to Solve the 'Object Doesn't Support #Inspect' Error
How to Enable Chromedriver Logging in Ruby Capybara with Selenium
How to Ensure Ruby Gems Are Installed in Right Place to Be Executed by Bundler
Capybara Synchronize with Has_No_Css
Rails G Migration Doesn't Work
Ruby on Rails 3 and Webrick Issue
How to Convert JSON to a Hash, Search for and Change a Value
Combine 2 Objects and Sort Rails 5
Multiple Applications Using a Single Code Base in Ruby
Ruby Equivalent of PHP's ".=" (Dot Equals) Operator
How to Use Private Submit to Hide from Profile
Is There a Ruby Method That Just Returns the Value of a Block
Is There a Best Directory to Place Image Uploads on Heroku
Does the Order of Gems in Your Gemfile Make a Difference
Trying to Get Content Inside Cdata Tags in Xml File Using Nokogiri
Heroku Rails 3.1 App - Compiling Assets Locally VS Compiling Assets During Slug Compilation