从多个ActiveRecord查询创建Ruby哈希的有效方法

时间:2016-02-11 11:10:56

标签: ruby-on-rails ruby refactoring rails-activerecord

我正试图以优雅的方式解决问题。

我有一个班级:

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

它有效,但我觉得它很难看。 有没有更好的方法来编写这种方法?

3 个答案:

答案 0 :(得分:4)

我会将您的代码重构为以下内容,我认为这些代码具有相当的可读性:

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。只需在返回之后命名方法即可。 “page”也不需要在名称中,因为这是Pages上的方法。 (顺便说一下,ActiveRecord模型以单数形式而不是复数形式命名更常见。)
  • %w()比常规引用词语更好一些。
  • r不是读者友好的变量名称。我使用name,意思是“资源名称”,因为从上下文中可以明显看出它是资源的名称。
  • 使用mapeach_with_object创建可枚举的模式,通过迭代另一个可枚举,然后返回它来构建它,通常可以更清晰,更短。
  • 将数组转换为哈希值时,map[key, value]对数组通常很方便,然后将其转换为.to_h哈希值。
  • try(r)没有做任何事情,因为关联方法总是返回一个真值。我删除了它。
  • 在调用除赋值方法之外的方法时,
  • self.不是必需的。
  • merge!只能替换为哈希文字。
  • return在方法结束时是不必要的,而不是惯用的。

重构很有趣,所以我按照说明回答了你的问题,但我同意Nermin看起来你可能想要查看序列化框架。

答案 1 :(得分:2)

如何使用序列化程序,它们就是为了做这种工作。

检查以下链接:

答案 2 :(得分:1)

你可以通过将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