句法糖迫使我使用丑陋的陈述

时间:2014-01-08 13:14:49

标签: ruby

def foo(bar)
  'return value'
end

foo 'bar' # => "return value"

def foo=(bar)
  'return value'
end

foo = 'bar' # => "bar"

send :foo=, 'bar' # => "return value"

我希望foo = 'bar'返回"return value"但不要为此目的使用send。我怎么能这样做?

更新

我的宝石需要一个理想的行为。这是一个例子:

car = Car.new
car.gear # => :first
car.next_gear # => :second
car.gear # => :second

car.gear = :fourth # => false
car.gear # => :second

car.gear = :third # => :third
car.gear # => :third

4 个答案:

答案 0 :(得分:5)

作业始终会返回作业的右侧。

详细了解ruby documentation

  

以等号结尾的方法表示赋值方法。   对于赋值方法,返回值被忽略,参数是   而是返回。

话虽如此,foo = bar也分配给局部变量foo,而不是使用foo=方法。同样,这在the ruby docs

中定义
  

使用方法分配时,您必须始终拥有接收器。如果你   没有接收器Ruby假定您正在分配给本地   变量

您可以通过运行

进行测试
local_variables #=> []
def foo=(bar);end
foo = 42
local_variables #=> [:foo]

您看到已创建局部变量foo。更好地使用self.foo = 'bar'

解决您的宝石特定问题:遵循Neil的建议并使用change_gear之类的额外方法来完成您的工作。他在评论中给了你很好的理事会。

答案 1 :(得分:2)

这是一个Ruby问题:访问者方法的返回值被忽略。

此代码将更清楚地说明实际发生的事情:

#!/usr/bin/env ruby

def foo(bar)
  p "called :foo w/ #{bar.inspect}"
end

def foo=(bar)
  p "called :foo= with #{bar.inspect}"
end

ret = (foo :bar1)            # calls foo(bar)
p "ret: #{ret}"              # "ret: called :foo w/ :bar1"
ret = (foo = :bar2)          # assigns a local variable foo = 'bar2'
p "ret: #{ret}"              # "ret: bar2"
ret = (send :foo=, :bar3)    # calls foo=(bar), returns what p returns
p "ret: #{ret}"              # "ret: called :foo= with :bar3"
ret = (self.foo = :bar4)     # calls foo=(bar), returns ???
p "ret: #{ret}"              # "ret: bar4"

基本上,Ruby解析器(至少在2.1中)的行为就好像self.foo=正在调用一个访问器方法(即使它实际上没有分配任何东西),将始终返回传递给的值它与你发送的内容无关,而不是访问者的返回值

演示:

#!/usr/bin/env ruby

class << self
  attr_accessor :foo

  def foo=(bar)
    p "called :foo= with #{bar.inspect}"
    @foo = :baz
  end
end

ret = (self.foo = :bar)
p "ret: #{ret} vs @foo: #{@foo.inspect}"

输出:

"called :foo= with :bar"
"ret: bar vs @foo: :baz"

编辑:reference的@tessi:

  

以等号结尾的方法表示赋值方法。对于赋值方法,将忽略返回值,而是返回参数。

答案 2 :(得分:1)

我认为它失败的原因是局部变量名称在定义时优先于方法名称。

因此,您需要使用send,以便self知道它正在寻找方法而不是变量。

答案 3 :(得分:0)

你需要这样做:

 self.foo = 'bar'