如何在ruby中进行attr_accessor_with_default?

我在rails模型中使用attr_accessor_with_default一些代码现在给我一个弃用警告,告诉我“使用Ruby代替!”

所以,想想也许在ruby 1.9.2中有一个新的位置让attr_accessor处理默认值,我用attr_accessor搜索它,但我没有看到。 我确实看到了一堆覆盖attr_accessor来处理默认值的方法。

当他们告诉我“使用Ruby?”时,这是什么意思? 或者我现在应该写完整的getter / setter? 还是有一些我找不到的新方法?

 attr_accessor :pancakes def after_initialize return unless new_record? self.pancakes = 11 end 

这可确保将值初始化为仅适用于新记录的某些默认值。

这个apidock页面建议在initialize方法中执行它。

 class Something attr_accessor :pancakes def initialize @pancakes = true super end end 

特别是在使用ActiveRecord或类似设备时,请不要忘记调用super

由于您可能非常了解您的数据,因此假设nil不是有效值是完全可以接受的。

这意味着您可以取消after_initialize ,因为这将针对您创建的每个对象执行。 正如几位人士指出的那样,这可能(可能)对性能造成灾难性后果。 此外,无论如何,在Rails 3.1中不推荐使用内联方法中的方法。

要’使用Ruby代替’,我会采用这种方法:

 attr_writer :pancakes def pancakes return 12 if @pancakes.nil? @pancakes end 

因此,稍微减少Ruby魔法并编写自己的getter。 毕竟这完全符合你想要完成的任务,并且它足够好和简单,任何人都可以将他/她的头包裹起来。

这是一个令人讨厌的问题,但一般问题仍然存在 – 我发现自己在这里。

其他答案是多种多样且有趣的,但是我在初始化数组时发现了所有这些问题(特别是因为我希望能够在实例上调用initialize之前在类级别使用它们)。 我成功了:

 attr_writer :pancakes def pancakes @pancakes ||= [] end 

如果使用=而不是|| =,您会发现<<运算符无法将第一个元素添加到数组中。 (创建一个匿名数组,为其分配一个值,但它永远不会被分配回@pancakes。)

例如:

 obj.pancakes #=> [] obj.pancakes << 'foo' #=> ['foo'] obj.pancakes #=> [] #???#!%$#@%FRAK!!! 

由于这是一个非常微妙的问题,可能会导致一些头部划痕,我认为这里值得一提。

对于bool,需要更改此模式,例如,如果要默认为false:

 attr_writer :pancakes def pancakes @pancakes.nil? ? @pancakes = false : @pancakes end 

虽然你可以争辩说在处理布尔时并不是绝对必要的。

1.9.2中没有什么可以用于初始化使用attr_accessor设置的实例变量。 但是有after_initialize回调:

只要实例化Active Record对象,就可以通过直接使用new或从数据库加载记录来调用after_initialize回调。 避免直接覆盖Active Record initialize方法可能很有用。

所以:

 attr_accessor :pancakes after_initialize :init protected def init @pancakes = 11 end 

这比这样的东西更安全:

 def pancakes @pancakes ||= 11 end 

因为nilfalse在初始化之后可能是完全有效的值并且假设它们不会导致一些有趣的错误。

我想知道如果只使用Rails实现对你有用:

http://apidock.com/rails/Module/attr_accessor_with_default

 def attr_accessor_with_default(sym, default = nil, &block) raise 'Default value or block required' unless !default.nil? || block define_method(sym, block_given? ? block : Proc.new { default }) module_eval( def #{sym}=(value) # def age=(value) class << self; attr_reader :#{sym} end # class << self; attr_reader :age end @#{sym} = value # @age = value end # end, __FILE__, __LINE__ + 1) end 

在将补丁应用于Module后,您可以为任何类的实例(不仅仅是ActiveRecords )指定默认值:

 class Zaloop attr_accessor var1: :default_value, var2: 2 def initialize self.initialize_default_values end end puts Zaloop.new.var1 # :default_value 

模块补丁:

 Module.module_eval do alias _original_attr_accessor attr_accessor def attr_accessor(*args) attr_names = extract_default_values args _original_attr_accessor *attr_names end alias _original_attr_reader attr_reader def attr_reader(*args) attr_names = extract_default_values args _original_attr_reader *attr_names end def extract_default_values(args) @default_values ||= {} attr_names = [] args.map do |arg| if arg.is_a? Hash arg.each do |key, value| define_default_initializer if @default_values.empty? @default_values[key] = value attr_names << key end else attr_names << arg end end attr_names end def define_default_initializer default_values = @default_values self.send :define_method, :initialize_default_values do default_values.each do |key, value| instance_variable_set("@#{key}".to_sym, value) end end end def initialize_default_values # Helper for autocomplete and syntax highlighters end end