Ruby Rails 上:对非持久化对象的类似 ActiveRecord 的查询?

Ruby on Rails: ActiveRecord-like queries on non-persisted objects?

在我的项目中,大量的PORO是由外部API等各种数据源组成的。对象看起来像:

{id: 1, name: 'Peter', age: 8}, {id: 2, name: 'Jack', age: 4}, {id: 3, name: 'Tom', age: 12}

我想要一个类似 ActiveRecord 的界面来查询这些对象。比如Person.where(name: 'Jack')Person.where("age > ?", 5)

我的尝试是这样的:

class Query
    def initialize(objs)
      @objs = objs
    end

    def where(name: nil, age: nil)
      result = @objs 
      result = result.select{|x| x.name == name} if name
      result = result.select{|x| x.age == age}   if age
      result
    end
end

它有效,但我认为这不是一个好的解决方案:

  1. 如果有20个属性呢? where 方法会变得很长而且容易出错。
  2. 其他有用的 ActiveRecord 查询呢?例如findfind_bypluckorder by 等等。即使我可以实现它们,我如何 "chain" 多个查询?
  3. 效率:如何像 sql 查询规划器一样优化查询?
  4. 最重要的是,我如何实现 Person.where("age > ?", 5) 和其他灵活的查询?

我错过了什么吗?我觉得我在重新发明轮子。

我检查过 ActiveModel,但遗憾的是它没有查询系统。

是否有任何可以提供帮助的 gem?

您可能最好使用对象映射器,例如来自 https://www.ruby-toolbox.com/categories/orm. An example implementation is shown at http://rom-rb.org/learn/repositories/reading-simple-objects/ 的对象映射器,但这对于您正在做的事情来说可能有点矫枉过正。

Hashie 支持单个对象的 deep_find 和对象集合的 deep_locate 等方法。 deep_locate 可能适用于您正在做的事情,但请记住,哈希对象将比标准哈希占用更多内存。

deep_locate 的示例代码:

books = [
  {
    title: "Ruby for beginners",
    pages: 120
  },
  {
    title: "CSS for intermediates",
    pages: 80
  },
  {
    title: "Collection of ruby books",
    books: [
      {
        title: "Ruby for the rest of us",
        pages: 576
      }
    ]
  }
]

books.extend(Hashie::Extensions::DeepLocate)

# for ruby 1.9 leave *no* space between the lambda rocket and the braces
# http://ruby-journal.com/becareful-with-space-in-lambda-hash-rocket-syntax-between-ruby-1-dot-9-and-2-dot-0/

books.deep_locate -> (key, value, object) { key == :title && value.include?("Ruby") }
# => [{:title=>"Ruby for beginners", :pages=>120}, {:title=>"Ruby for the rest of us", :pages=>576}]

books.deep_locate -> (key, value, object) { key == :pages && value <= 120 }
# => [{:title=>"Ruby for beginners", :pages=>120}, {:title=>"CSS for intermediates", :pages=>80}]