Ruby紧凑赋值语法

时间:2016-03-29 09:50:25

标签: ruby

我想在ruby中进行紧凑的错误检查分配。

class User
  attr_accessor :x
end

user = User.new
user.x = 5
a = b || user.x

我想弄清楚哪些是第一个有效属性并分配它,类似于javascript如何处理不同的API,即:

var AudioContext = window.AudioContext||window.webkitAudioContext;
audioContext = new AudioContext();

并找出哪些是有效的。

但是,对于ruby,当我引用一个未定义的变量时,类似的语法会出错。即:

a = 10
b = 7
c = a || b
c # => 10

VS

a = 10
c = b || a # => Error: b is undefined

有干净的方法吗?或者至少,最好的方法是什么?

我正在使用我尚未创建的大型代码,我不允许更改它。

更新: 我认为真正的用例与这个问题有关,所以我会解释它。

我有一个模块,每次更新rails中的模型时都会向DB保存一些内容,此更新需要一个id字段,此id字段位于包含我的模块的模型中,但并非每个模型都保持相同的命名约定为了这个id。三元运算符相当于我想要做的是

id = defined?(self.id) ? self.id : defined?(self.game_id) ? self.game_id : defined?(self.app_id) ? self.app_id : nil

与js等效

相比,难以读写

2 个答案:

答案 0 :(得分:3)

您可以使用defined?来测试名称是否指当前范围内可识别的内容(方法,局部变量等):

c = defined?(b) ? b : a # of course this assumes that 'a' is defined

虽然你从未定义的局部变量中分配是非常不确定的 - 这是怎么出现的?

或者您是否总是在测试物业?在这种情况下,respond_do?可能是更好的选择。

修改

我使用三元运算符作为示例,当然可以使用if / elsif / else块。

由于您只是测试方法,respond_to?defined?更方便,因为它可以使用符号而不是表达式来应用您想要的任何逻辑:

def invoke_first *names
  names.each do |name|
    if respond_to? name
      return send name
    end
  end
  return nil # or, more likely, raise something
end

或更简洁:

def invoke_first *names
  send names.find(lambda {raise 'no method'}){|n| respond_to?(n)}
end

包含在您的模型中并用作:

invoke_first(:foo_id, :bar_id, :baz_id)

答案 1 :(得分:0)

好的,有一个更简洁的方法来做到这一点,但它有一个副作用,分配给undefined var的东西。

这打破了

a = 1
c = b || a # => b is undefined

这有效

a = 1
c = b ||= a # => c == 1, b == 1

如果b有效,上面将b分配给c,然后再回到a。如果b未定义/无效,则仅将a分配给b(和c)。