What is the most efficient way to initialize a Class in Ruby with different parameters and default values?
The typical way to solve this problem is with a hash that has a default value. Ruby has a nice syntax for passing hash values, if the hash is the last parameter to a method.
class Fruit
attr_accessor :color, :type
def initialize(params = {})
@color = params.fetch(:color, 'green')
@type = params.fetch(:type, 'pear')
end
def to_s
"#{color} #{type}"
end
end
puts(Fruit.new) # prints: green pear
puts(Fruit.new(:color => 'red', :type => 'grape')) # prints: red grape
puts(Fruit.new(:type => 'pomegranate')) # prints: green pomegranate
A good overview is here: http://deepfall.blogspot.com/2008/08/named-parameters-in-ruby.html
ruby initialize method - purpose of initialize arguments
You can set an instance variable in any method in your class.
initialize
is a method that is executed immediately after calling Person.new
.
All external data for new object is passed through the arguments of .new(args)
.
Your line @age = age
- it's the same that @age = nil
.
This is due to the fact that age
is absent in the arguments of initialize
.
Also you have attr_accessor :age
.
It's equal, that you have methods:
def age
@age
end
def age=(age)
@age = age
end
So you can set instance variable like this:
john = Person.new('John')
p john.age #=> nil
john.age = 5
p john.age #=> 5
Ruby - initialize inheritance, super with only certain arguments?
Yes, you are committing a Cardinal Sin (obviously, you are aware of it, since you are asking about it). :)
You are breaking Liskov substitution principle (and probably some other named or unnamed rules).
You should probably extract another class as a common superclass, which does not contain occupation
. That will make everything much clearer and cleaner.
Manipulating Ruby class default values
If you write
class Die
def initialize(min=1, max=6)
@min, @max = min, max
end
end
and create a new instance by passing a single argument, such as:
die = Die.new(3)
#=> #<Die:0x007fcc6902a700 @min=3, @max=6>
we can see from the return value that the argument 3
has been assigned to @min
and @max
gets its default value. In short, to pass a value to @max
you must also pass one to @min
(unless, of course, you reverse the order of the arguments).
You can do what you want by using named arguments (or named parameters), introduced in Ruby v2.0.
class Die
def initialize(min: 1, max: 6)
@min, @max = min, max
end
end
die = Die.new(max: 3)
#=> #<Die:0x007fcc698ccc00 @min=1, @max=3>
(or die = Die.new(:max=>3
). As you see, @min
equals its default value and @max
equals the argument that is passed, 3
.
Default values were required for keyword arguments in Ruby v2.0, but v2.1 extended their functionality to permit required named arguments as well. See, for example, this article.
Lastly, consider the following two cases (the second being the more interesting).
class Die
def initialize(min=1, max: 6)
@min, @max = min, max
end
end
die = Die.new(max: 3)
#=> #<Die:0x007fcc69954448 @min=1, @max=3>
class Die
def initialize(min, max: 6)
@min, @max = min, max
end
end
die = Die.new(max: 3)
#=> #<Die:0x007fa01b900930 @min={:max=>3}, @max=6>
ruby: how to initialize class method with parameters?
You could do this.
class MyClass
singleton_class.send(:attr_accessor, :string_3)
end
MyClass.string_3 = "It's a fine day."
MyClass.string_3 #=> "It's a fine day."
@string_3
is a class instance variable.
Ruby class initialize method with default hash
Keyword arguments were introduced with Ruby 2.0.
The documentation gives details, and the Ruby Rogues podcast back then had an interesting discussion about it.
Note that this applies to any method, not only to initialize
.
Initialize ruby object with a hash with default values?
In Ruby, the conditional expression is, well, an expression, which means it evaluates to a value. (In Ruby, everything is an expression, there are no statements, ergo, everything evaluates to a value.) The value of a conditional expression is the value of the branch that was taken.
So, everywhere where you see something like this:
if foo
bar(baz)
else
bar(quux)
end
You can always replace that with
bar(
if foo
baz
else
quux
end
)
# more conventionally written as
bar(if foo then baz else quux end)
And everywhere where you see something like this:
if foo
bar = baz
else
bar = quux
end
You can always replace that with
bar = if foo
baz
else
quux
end
So, let's do that here:
def get_date(details)
@admission_date = if details[:date].nil?
Date.today.strftime("%d%m%y")
else
details[:date]
end
end
Let's look at this further: your get_date
method is really strange. It performs a side-effect (it assigns to @admission_date
) but it also returns a value. Normally, a method should either perform a side-effect (and return nothing, i.e. in Ruby nil
) or return something but not both.
Also, the fact that a method named get
something is actually setting something is incredibly confusing and misleading, and confusing and misleading method names are very dangerous because they lead to bugs where the programmer thinks the method is doing one thing but it actually does another.
I, personally, would not at all expect that it is unsafe to call a method named get
something. But this method is unsafe: if I call it just to check what the current date is (after all, it is called get_date
, so what could it possibly do other than, you know, get the date), I will actually overwrite the value of @admission_date
!
What is even weirder, though, is the way that the method is used: even though the method already assigns the instance variable @admission_date
when it is called, the return value of the method (which is just the value that was assigned to @admission_date
) is used to assign @admission_date
again, so that it immediately gets overwritten with the exact same value that it already has.
It seems obvious that even the author of the code was confused and mislead by the name of the method! The method name is so misleading that the author didn't even see the double assignment despite the fact that the two assignments are literally within 5 lines of each other.
So, let's remove one of the redundant assignments. I would prefer to remove the one in the get_date
method to bring its behavior further in line with its name:
def get_date(details)
if details[:date].nil?
Date.today.strftime("%d%m%y")
else
details[:date]
end
end
Furthermore, it looks like details[:date]
can be either nil
or a Date
but it can never be false
and nil
is not a valid value. So, we can use the well-known ||
idiom here instead:
def get_date(details)
details[:date] || Date.today.strftime("%d%m%y")
end
Or, probably even better, we can use Hash#fetch
:
def get_date(details)
details.fetch(:date, Date.today.strftime("%d%m%y"))
end
Since your helper method isn't really doing anything complex at this point, and is only called from one single place in your code, we can just inline it:
def initialize(details)
@name = details[:name]
@age = details[:age]
@admission_date = details.fetch(:date, Date.today.strftime("%d%m%y"))
end
Note that since Ruby 2.0 (released on Ruby's 20th birthday in 2013), Ruby supports keyword parameters, both mandatory keyword parameters and optional keyword parameters with default argument values, so an even better way to write this might be:
def initialize(name:, age:, date: Date.today.strftime("%d%m%y"))
@name, @age, @date = name, age, date
end
But that depends on your API design and your callers.
In Ruby, what is the benefit initializing a class instance using def initialize ( value = '')
First case:
def initialize (value = "")
@value = value
end
will set the @value to be empty string if no parameter is passed to initialize. If a parameter is passed to initialize, value will be set to that parameter.
Second case:
def initialize
@value = ""
end
will always set @value to be the empty string and will not accept parameters.
Example:
If we have
module TicTacToe
class Cell
attr_accessor :value
def initialize (value = "")
@value = value
end
end
end
c = TicTacToe::Cell.new("hello")
puts c.value
the code would print hello.
Using the same code above but changing the last two lines to
c = TicTacToe::Cell.new
puts c.value
the code prints nothing (well except the empty string).
Now if we change our code to the second way:
module TicTacToe
class Cell
attr_accessor :value
def initialize
@value = ""
end
end
end
c = TicTacToe::Cell.new
puts c.value
this will output the empty string again. However, this time if we try changing the last 2 lines to:
c = TicTacToe::Cell.new("hello")
puts c.value
we get an error because the initializer is not expecting an argument. Thus, we cannot instantiate it with any @value besides the empty string.
In regards to your edit:
Yes, you can still change @value. If you would like to prevent this, make it only readable by changing
attr_accessor :value
to
attr_reader :value
How to cleanly initialize attributes in Ruby with new?
def initialize(params)
params.each do |key, value|
instance_variable_set("@#{key}", value)
end
end
Related Topics
Bundler VS Rvm VS Gems VS Rubygems VS Gemsets VS System Ruby
Passing a Hash to a Function ( *Args ) and Its Meaning
Mongodb with Mongoid in Rails - Geospatial Indexing
Can't Install Ffi -V '1.9.18' on MACos Catalina
Convert String with Hex Ascii Codes to Characters
Calling Method in Parent Class from Subclass Methods in Ruby
Ruby Way to Check for String Palindrome
Thread.Join Blocks the Main Thread
How to Add Custom Filter to Active Admin
Why Does This Nokogiri Xpath Have a Null Return
Rmagick Installation: Can't Find Magickwand.H
Factory Girl - What's the Purpose
Read Input from Console in Ruby
How to Convert Array of Activerecord Models to CSV
Installed Rails But the Rails Command Says It's Not Installed