What Is the Most Efficient Way to Initialize a Class in Ruby with Different Parameters and Default Values

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



Leave a reply



Submit