如何迭代ActiveRecord属性,包括attr_accessor方法

我到处寻找一个优雅的解决方案。 基本问题似乎是,在ActiveRecord :: Base中,映射到数据库列的ActiveRecord属性与attr_accessor方法完全不同。

我想做的事情如下:

model.attribute_names.each do |name| # do stuff end 

以包含attr_accessor字段的方式,但不包括任何其他实例方法。 我知道这不是内置的,但最优雅的方法是什么?

你无法解决这个问题。 你可以近似一个黑客,但它不是一个可以很好地工作的东西。

model.attribute_names应该可以获得所有ActiveRecord,但是attr_accessor字段不是字段。 它们只是普通的ruby方法,获取它们的唯一方法是使用model.instance_methods

想法1

你可以做model.attribute_names + model.instance_methods ,但是你必须过滤掉所有其他正常的ruby方法initialize, save等,这是不切实际的。

为了帮助过滤instance_methods你可以将它们与model.instance_variables匹配(你必须手动考虑实例变量中的@符号),但问题是实例变量实际上根本不存在,直到它们首先被分配。

想法2

在您的environment.rb ,在加载任何其他内容之前,在ActiveRecord::Base定义您自己的self.attr_accessor 。 然后,这可以包装底层的attr_accessor但也将属性名称保存到私有列表。 然后你就可以在以后退出这个列表了。 但是我建议不attr_accessor ……像attr_accessor这样的猴子修补核心语言设施attr_accessor会给你带来很大的痛苦。

想法3

ActiveRecord::Base定义自己的custom_attr_accessor ,它与Idea 2完全相同,并在您希望能够检索属性名称的代码中使用它。 这将是安全的,因为你不会再破坏内置的attr_accessor方法,但你必须更改所有代码以使用必要的custom_attr_accessor

attr_accessor ,你想要做什么,需要了解所有的attr_accessor字段? 如果可以的话,尝试从不同的角度看问题。

我来到这里寻找同样的事情,并且由于猎户座的回答,我发现这是完全错误的做法。

如果其他人的用例与我的相似,这是我的解决方案。 在使用ActiveRecord查询模型后,我正在使用attr_accessor为模型添加额外的属性。 然后我想将结果输出为JSON等。

更好的解决方案是首先将模型从ActiveRecord转换为常规哈希,然后将attr_accessor属性添加为常规哈希键。

例:

 model_hash = model_from_activerecord.attributes.to_options model_hash[:key] = value 

我为自己想出的解决方案建立在猎户座爱德华兹的回答之上。

代码:

klass_attributes = klass.column_names + klass.instance_methods(false). map(&:to_s).keep_if{|a| a.include?('=')}.map{|a| a.sub('=', '')}

细分:

klass.instance_methods(false)仅返回实例方法,而不返回inheritance的方法。

map(&:to_s)将符号数组转换为字符串数组,以便我们可以使用include? 方法。 还需要与column_names返回的字符串数组合并。

keep_if{|a| a.include?('=')} keep_if{|a| a.include?('=')}将删除数组中没有等号的所有字符串。 这很重要,因为我注意到attr_accessor属性都有’=’。 例如:’app_url’vs’login_url =’

map{|a| a.sub('=', '')} map{|a| a.sub('=', '')}是最后一个部分,然后从数组中的每个字符串中删除’=’。

最终得到一个表示属性的字符串数组,包括attr_accessor属性。