使用可选的默认值创建属性访问器

时间:2017-05-22 18:48:11

标签: ruby-on-rails ruby default

我有一个Rails应用程序,需要重复设置默认属性。有时,用户将提供将被尊重的属性的值,但在其他情况下,模型或用户可能希望使用默认值覆盖这些属性而忽略原始值。

我猜这个问题需要一个用于设置默认值的banged(!)和非banged方法,允许用户和程序切换到适当的状态。非撞击的setter只会在它们不为nil时设置默认值,而banged版本将始终使用默认值覆盖属性。差别很小:

class BangDiBang
  attr_accessor :value

  def set_default
    self.value ||= do_some_suff_to_determine_default_value
  end

  def set_default!
    self.value = do_some_suff_to_determine_default_value
  end

  ...
end

这段代码的问题是如果我要设置一堆变量,我最终会为每个变量重复两次相同的代码。

我的问题是如何分解这段代码?将逻辑保存在一个方法中,并使用两个方法set_valueset_value!使用不同的赋值运算符调用中央逻辑。

我想到了一个解决方案:将中央逻辑写为文本,从setter方法替换赋值操作并进行评估(但这感觉不对)。我怎么不重复自己?

2 个答案:

答案 0 :(得分:0)

由于您正在调用set_default但未指定哪个变量,因此您对此进行此操作的方式不适用于多个参数。理想情况下,您需要为每个变量定义行为。有些库可以处理这类事情,但你可以很容易地自己动手:

class Example
  def self.default_param(name, default_value)
    define_method("default_#{name}") { default_value }

    define_method(name) do 
      instance_variable_get("@#{name}") || default_value
    end

    attr_writer name
  end

  default_param :foo, 'foo default'
end

ex = Example.new
ex.foo #=> "foo default" 
ex.foo = 'bar'
ex.foo #=> "bar"
ex.default_foo #=> "foo default" 

我已将set_defaultset_default!重命名为更清晰:对于每个具有默认值的变量,都会创建三个方法(例如使用foo作为变量名称):< / p>

  1. foo - 如果它是真实的,则返回@foo的值,否则返回default_foo
  2. foo= - 设置@foo
  3. 的值
  4. default_foo - 返回指定的默认值
  5. 您可以进一步划分和干掉上面的一些代码,创建一个default_params(复数)方​​法来获取哈希值,将类宏提取到关注点:

    module DefaultParams
      def default_param(name, default_value)
        define_method("default_#{name}") { default_value }
    
        define_method(name) do 
          instance_variable_get("@#{name}") || default_value
        end
    
        attr_writer name
      end
    
      def default_params(params)
        params.each { |default|  default_param(*default) }
      end
    end
    
    class Example
      extend DefaultParams
      default_params foo: 'default foo', bar: 'my favorite bar'
    end
    

答案 1 :(得分:0)

我已经实施了一个灵感来自coreyward的解决方案。我一开始并没有意识到,我需要将默认值分离为单个方法,而不是使用两种方法来设置默认值数组。这为应用程序的设计提供了极大的灵活性。非常感谢答案。

对于我已添加到application_record.rb

的导轨设置
def set_attr_from_defaults
  default_attrs.each do |atr|
    eval("self.#{atr[0..atr.length-9]} ||= self.#{atr}")
  end
end

def set_attr_from_defaults!
  default_attrs.each do |atr|
    eval("self.#{atr[0..atr.length-9]} = self.#{atr}")
  end
end

def set_default_attr params
  params.each { |key, value| self.define_singleton_method(key){value} }
end

def default_attrs
  self.attributes.keys.map{|i| (i+'_default').to_sym} & self.methods
end

default_attrs生成默认属性的符号列表。 set_default_attr定义了单例方法,打算在像attrname_default: 'default_value' ...这样的散列中使用解析。当属性具有set_attr_from_defaults对时,var: var_default会根据默认值设置属性。数字9_default + 1的长度。