ActiveModel属性

12

我如何获得ActiveRecord的属性方法功能?我有这个类:

class PurchaseForm
  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend ActiveModel::Naming

  attr_accessor :name,
                :surname,
                :email

  validates_presence_of :name

  validates_format_of :email, :with => /^[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i

  def initialize(attributes = {}, shop_name)
    if not attributes.nil?
      attributes.each do |name, value|
      send("#{name}=", value)
    end
  end

  def persisted?
    false
  end
end

我需要做什么才能使得PurchaseForm对象拥有一个attributes方法,以列出所有名称和值?


鉴于现有答案的hacky性质,正确的答案似乎是“没有适用于Rails的模块”。个人认为最好避免模仿属性API。 - deprecated
也许ActiveModel::Attributes 模块是你正在寻找的东西? - Andrea Salicetti
5个回答

17

这里是重构后的版本:

class PurchaseForm
  include ActiveModel::Model

  def self.attributes
    [:name, :surname, :email]
  end

  attr_accessor *self.attributes

  # your validations

  def to_hash
    self.class.attributes.inject({}) do |hash, key|
      hash.merge({ key => self.send(key) })
    end
  end  
end

现在你可以轻松地使用这个类:

irb(main):001:0> a = PurchaseForm.new({ name: 'Name' })
=> #<PurchaseForm:0x00000002606b50 @name="Name">
irb(main):002:0> a.to_hash
=> {:name=>"Name", :surname=>nil, :email=>nil}
irb(main):003:0> a.email = 'user@example.com'
=> "user@example.com"
irb(main):004:0> a
=> #<PurchaseForm:0x00000002606b50 @name="Name", @email="user@example.com">
irb(main):005:0> a.to_hash
=> {:name=>"Name", :surname=>nil, :email=>"user@example.com"}

更进一步,如果您希望使此行为可重用,请考虑将.attributes#to_hash方法提取到单独的模块中:

module AttributesHash
  extend ActiveSupport::Concern

  class_methods do
    def attr_accessor(*args)
      @attributes = args
      super(*args)
    end

    def attributes
      @attributes
    end
  end

  included do
    def to_hash
      self.class.attributes.inject({}) do |hash, key|
        hash.merge({ key => self.send(key) })
      end
    end
  end
end

现在,只需将其包含到您的模型中,您就完成了:

class PurchaseForm
  include ActiveModel::Model
  include AttributesHash

  attr_accessor :name, :surname, :email

  # your validations
end

我可以建议在 concern 中使用 @attributes ? @attributes += args : @attributes = args 而不是仅仅使用 @attributes = args 吗?这将允许在类中多次调用 attr_accessor - Barry

15

#instance_values 可以完成这个任务:

class PurchaseForm
  attr_accessor :name, :email

  def attributes
    instance_values
  end
end

输出示例:

purchase.attributes #=> {"name"=>"John", "email"=>"john@example.com"}

文档在这里:https://api.rubyonrails.org/classes/Object.html#method-i-instance_values - Paul Odeon

7

我已经成功解决了这段代码的问题:

class PurchaseForm
  include ActiveModel::Validations
  include ActiveModel::Conversion
  extend ActiveModel::Naming

  attr_accessor :attributes,
                :name,
                :surname,
                :email

  validates_presence_of :name

  validates_format_of :email, :with => /^[-a-z0-9_+\.]+\@([-a-z0-9]+\.)+[a-z0-9]{2,4}$/i

  def initialize(attributes = {})
    @attributes = attributes
  end

  def persisted?
    false
  end
end

你为什么在attr_accessor调用中添加了:attributes? - Craig
所以我可以从视图中调用属性方法...我在Rails方面还是个新手,如果你有更好的解决方案,请分享 :) - zigomir
1
此解决方案有缺陷。如果你不将所有属性传递给 .new.attributes 将返回具有仅指定属性的不正确哈希值。其次,如果你在初始化后尝试更改实例,比如说 @purchase_form.name = '其他名字',然后调用 @purchase_form.attributes,哈希表将包含提供给 .new 的旧 :name - Rustam Gasanov

6

让我们试试这个

self.as_json

=> {:name=>"名称", :surname=>nil, :email=>"user@example.com"}


-3

使用起来不是更好吗?

include ActiveModel::Serialization

def attributes
  JSON.parse(self.to_json)
end

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接