Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way for Ruby accessors to return something other than the set variable?

I want to do some checking in a writer accessor. My first idea was returning a boolean.

class MyClass
  def var=(var)
    @var = var
    # some checking
    return true
  end
end

m = MyClass.new
retval = (m.var = 'foo')

=> "foo"

Can I set a return value in a writer accessor? If yes, how can I get this value?

like image 982
Christian Lescuyer Avatar asked Dec 05 '22 07:12

Christian Lescuyer


2 Answers

I would use set_var(var) instead of what you are trying to do, an attribute writer is assumed to just work. What you are trying to do is nonstandard and non-obvious to the next poor person to use your code. (It may just be yourself) I would throw an exception if bad input is sent or something rather exceptional happens.

You want this behavior

Correct
>>temp = object.var = 7
=> 7 

Wrong
>>temp = object.var = 7
=> false

The = operator should always return the value that was passed to it. Ruby uses implicit returns which is uncommon in programming languages. Double check the returns when you use method=().

like image 186
epochwolf Avatar answered Apr 27 '23 09:04

epochwolf


class Test
  def var=(var)
    @var = var
    return true
  end
end

t1, t2 = Test.new, Test.new

t1.var = 123 # evaluates to 123

# Why is it impossible to return something else:
t1.var = t2.var = 456

As stated in the comment: I believe it's impossible to change the return value in order to allow chained assignments. Changing the return value would probably be unexpected by the majority of Ruby programmers anyway.

Disclaimer: I tested the code above but I've found no explicit references to verify my statement.

Update

class Test
  def method_missing(method, *args)
    if method == :var=
      # check something
      @var = args[0]
      return true
    else
      super(method, *args)
    end
  end

  def var
    @var
  end
end

t = Test.new
t.var = 123 # evaluates to 123
t.does_not_exists # NoMethodError

It really seems to impossible! The above sample suggests that the return value isn't related to the var= method at all. You cannot change this behavior - it's the fundamental way how Ruby assignments work.

Update 2: Solution found

You could raise an exception!

class Test
  def var=(var)
    raise ArgumentError if var < 100 # or some other condition
    @var = var
  end
  def var
    @var
  end
end

t = Test.new
t.var = 123 # 123
t.var = 1 # ArgumentError raised
t.var # 123
like image 32
Christoph Schiessl Avatar answered Apr 27 '23 10:04

Christoph Schiessl