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
它有效,但我认为这不是一个好的解决方案:
- 如果有20个属性呢?
where
方法会变得很长而且容易出错。
- 其他有用的 ActiveRecord 查询呢?例如
find
、find_by
、pluck
、order by
等等。即使我可以实现它们,我如何 "chain" 多个查询?
- 效率:如何像 sql 查询规划器一样优化查询?
- 最重要的是,我如何实现
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}]
在我的项目中,大量的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
它有效,但我认为这不是一个好的解决方案:
- 如果有20个属性呢?
where
方法会变得很长而且容易出错。 - 其他有用的 ActiveRecord 查询呢?例如
find
、find_by
、pluck
、order by
等等。即使我可以实现它们,我如何 "chain" 多个查询? - 效率:如何像 sql 查询规划器一样优化查询?
- 最重要的是,我如何实现
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}]