从多个 ActiveRecord 查询创建 Ruby 哈希的有效方法
Efficient way to create a Ruby hash from multiple ActiveRecord queries
我正在尝试以优雅的方式解决问题。
我有一个 class:
class Pages < ActiveRecord::Base
# Relations
has_many :contents
has_many :videos
has_many :galleries
has_many :surveys
has_many :documents
end
我想创建这样的散列
{"videos"=>{:resource_type=>"Video", :resource_id=>2, :resource_name=>"Video di prova"}, "documents"=>{}, "contents"=>{}, "surveys"=>{}, "galleries"=>{}}
正在收集我协会中的记录。
我写了一个方法
def get_page_resources
result = {}
['videos','galleries','documents','surveys','contents'].each do |r|
if self.try(r)
res_collection = {}
self.send(r).each do |resource|
res_collection.merge!(resource_type: resource.class.name)
res_collection.merge!(resource_id: resource.id)
res_collection.merge!(resource_name: resource.name)
end
result[r] = res_collection
end
end
return result
end
它有效,但我认为它很丑陋。
这个方法有没有更好的写法?
您可以通过将 res_collection 东西提取到这样的方法中来清理它:
def get_page_resources
{
videos: res_collection(:videos),
galleries: res_collection(:galleries),
documents: res_collection(:documents),
surveys: res_collection(:surveys),
contents: res_collection(:contents)
}
end
private
# this probably doesn't do what it was supposed to, but
# just to give an impression without digging further into your
# code..
def resource_collection(resource)
res_collection = {}
self.send(resource).each do |resource|
res_collection.merge!(resource_type: resource.class.name)
res_collection.merge!(resource_id: resource.id)
res_collection.merge!(resource_name: resource.name)
end
res_collection
end
我会将您的代码重构为以下代码,我认为它相当可读:
def resources
%w(videos galleries documents surveys contents).map do |name|
[
name, send(name).map do |resource|
{
resource_type: resource.class.name,
resource_id: resource.id,
resource_name: resource.name
}
end
]
end.to_h
end
- 以
get_
开始一个方法是不惯用的 Ruby。只需在 return 之后命名该方法即可。 "page" 也不需要在名称中,因为这是 Pages
上的一个方法。 (顺便说一句,ActiveRecord 模型通常以单数命名而不是复数命名。)
%w()
比常规的引用词数组好一点。
r
不是 reader 友好的变量名。我使用 name
,意思是 "resource name",因为从上下文中可以明显看出它是资源的名称。
- 创建可枚举的模式,通过迭代另一个可枚举来构建它,然后 returning 它,通常可以使用
map
或 each_with_object
变得更清晰和更短。
- 将数组转换为散列时,通常方便的做法是
map
将其转换为 [key, value]
对的数组,然后将其转换为带有 .to_h
的散列。
try(r)
什么都不做,因为关联方法总是 return 一个真值。我删除了它。
self.
调用赋值方法以外的方法时不需要
merge!
可以用散列文字替换。
return
在方法的末尾是不必要的,而且不是惯用的。
重构很有趣,所以我按照说明回答了你的问题,但我同意 Nermin 的观点,你可能想研究一下序列化框架。
我正在尝试以优雅的方式解决问题。
我有一个 class:
class Pages < ActiveRecord::Base
# Relations
has_many :contents
has_many :videos
has_many :galleries
has_many :surveys
has_many :documents
end
我想创建这样的散列
{"videos"=>{:resource_type=>"Video", :resource_id=>2, :resource_name=>"Video di prova"}, "documents"=>{}, "contents"=>{}, "surveys"=>{}, "galleries"=>{}}
正在收集我协会中的记录。
我写了一个方法
def get_page_resources
result = {}
['videos','galleries','documents','surveys','contents'].each do |r|
if self.try(r)
res_collection = {}
self.send(r).each do |resource|
res_collection.merge!(resource_type: resource.class.name)
res_collection.merge!(resource_id: resource.id)
res_collection.merge!(resource_name: resource.name)
end
result[r] = res_collection
end
end
return result
end
它有效,但我认为它很丑陋。 这个方法有没有更好的写法?
您可以通过将 res_collection 东西提取到这样的方法中来清理它:
def get_page_resources
{
videos: res_collection(:videos),
galleries: res_collection(:galleries),
documents: res_collection(:documents),
surveys: res_collection(:surveys),
contents: res_collection(:contents)
}
end
private
# this probably doesn't do what it was supposed to, but
# just to give an impression without digging further into your
# code..
def resource_collection(resource)
res_collection = {}
self.send(resource).each do |resource|
res_collection.merge!(resource_type: resource.class.name)
res_collection.merge!(resource_id: resource.id)
res_collection.merge!(resource_name: resource.name)
end
res_collection
end
我会将您的代码重构为以下代码,我认为它相当可读:
def resources
%w(videos galleries documents surveys contents).map do |name|
[
name, send(name).map do |resource|
{
resource_type: resource.class.name,
resource_id: resource.id,
resource_name: resource.name
}
end
]
end.to_h
end
- 以
get_
开始一个方法是不惯用的 Ruby。只需在 return 之后命名该方法即可。 "page" 也不需要在名称中,因为这是Pages
上的一个方法。 (顺便说一句,ActiveRecord 模型通常以单数命名而不是复数命名。) %w()
比常规的引用词数组好一点。r
不是 reader 友好的变量名。我使用name
,意思是 "resource name",因为从上下文中可以明显看出它是资源的名称。- 创建可枚举的模式,通过迭代另一个可枚举来构建它,然后 returning 它,通常可以使用
map
或each_with_object
变得更清晰和更短。 - 将数组转换为散列时,通常方便的做法是
map
将其转换为[key, value]
对的数组,然后将其转换为带有.to_h
的散列。 try(r)
什么都不做,因为关联方法总是 return 一个真值。我删除了它。self.
调用赋值方法以外的方法时不需要merge!
可以用散列文字替换。return
在方法的末尾是不必要的,而且不是惯用的。
重构很有趣,所以我按照说明回答了你的问题,但我同意 Nermin 的观点,你可能想研究一下序列化框架。