Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Defining method in Ruby with equals

Tags:

syntax

ruby

Being new to Ruby, I'm having trouble explaining to myself the behavior around method definitions within Ruby.

The example is noted below...

class Foo
  def do_something(action)
    action.inspect
  end
  def do_something_else=action 
    action.inspect
  end
end

?> f.do_something("drive")
=> "\"drive\""

?> f.do_something_else=("drive")
=> "drive"

The first example is self explanatory. What Im trying to understand is the behavior of the second example. Other than what looks to be one producing a string literal and the other is not, what is actually happening? Why would I use one over the other?

like image 452
Aaron McIver Avatar asked Oct 10 '12 04:10

Aaron McIver


3 Answers

Generally, do_something is a getter, and do_something= is a setter.

class Foo
  attr_accessor :bar
end

is equivalent to

class Foo
  def bar
    @bar
  end

  def bar=(value)
    @bar = value
  end
end

To answer your question about the difference in behavior, methods that end in = always return the right hand side of the expression. In this case returning action, not action.inspect.

class Foo
  def do_something=(action)
    "stop"
  end
end

?> f = Foo.new
?> f.do_something=("drive")
=> "drive"
like image 163
Erik Peterson Avatar answered Oct 29 '22 20:10

Erik Peterson


Both of your methods are actually being defined and called as methods. Quite a lot of things in Ruby can be defined as methods, even the operators such as +, -, * and /. Ruby allows methods to have three special notational suffixes. I made that phrase up all by myself. What I mean by notational suffixes is that the thing on the end of the method will indicate how that method is supposed to work.

Bang!

The first notational suffix is !. This indicates that the method is supposed to be destructive, meaning that it modifies the object that it's called on. Compare the output of these two scripts:

a = [1, 2, 3]
a.map { |x| x * x }
a

And:

a = [1, 2, 3]
a.map! { |x| x * x }
a

There's a one character difference between the two scripts, but they operate differently! The first one will still go through each element in the array and perform the operation inside the block, but the object in a will still be the same [1,2,3] that you started with.

In the second example, however, the a at the end will instead be [1, 4, 9] because map! modified the object in place!

Query

The second notational suffix is ?, and that indicates that a method is used to query an object about something, and means that the method is supposed to return true, false or in some extreme circumstances, nil.

Now, note that the method doesn't have to return true or false... it's just that it'd be very nice if it did that!

Proof:

def a?
  true
end

def b?
  "moo"
end

Calling a? will return true, and calling b? will return "moo". So there, that's query methods. The methods that should return true or false but sometimes can return other things because some developers don't like other developers.

Setters!

NOW we get to the meat of your (paraphrased) question: what does = mean on the end of a method?

That usually indicates that a method is going to set a particular value, as Erik already outlined before I finished typing this essay of an answer.

However, it may not set one, just like the query methods may not return true or false. It's just convention.

You can call that setter method like this also:

foo.something_else="value"

Or (my favourite):

foo.something_else = "value"

In theory, you can actually ignore the passed in value, just like you can completely ignore any arguments passed into any method:

def foo?(*args)
  "moo"
end

>> foo?(:please, :oh, :please, :why, :"won't", :you, :use, :these, :arguments, :i, :got, :just, :for, :you, :question_mark?)
=> "moo"

Ruby supports all three syntaxes for setter methods, although it's very rare to see the one you used!

Well, I hope this answer's been roughly educational and that you understand more things about Ruby now. Enjoy!

like image 44
Ryan Bigg Avatar answered Oct 29 '22 21:10

Ryan Bigg


You cannot define a return value for assignment methods. The return value is always the same as the value passed in, so that assignment chains (x = y = z = 3) will always work.

Typically, you would omit the brackets when you invoke the method, so that it behaves like a property:

my_value = f.do_something= "drive"
like image 45
meagar Avatar answered Oct 29 '22 20:10

meagar