在我的项目中,大量的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
它有效,但我不认为这是一个很好的解决方案:
where
方法可能会变得非常长且容易出错。find
,find_by
,pluck
,order by
以及其他许多人。即使我可以全部实施,我如何"链"多个查询?Person.where("age > ?", 5)
和其他灵活查询?我错过了什么吗?我觉得我正在重新发明轮子。
我已经检查了ActiveModel,但不幸的是它没有查询系统。
那里有没有可以提供帮助的宝石?
答案 0 :(得分:1)
您最好使用对象映射器,例如来自https://www.ruby-toolbox.com/categories/orm的对象映射器。 http://rom-rb.org/learn/repositories/reading-simple-objects/显示了一个示例实现,但这对您正在做的事情可能有点过分。
Hashie支持单个对象的deep_find
和对象集合的deep_locate
等方法。 deep_locate
可能适用于您正在做的事情,但请记住,Hashie对象将占用比标准哈希更多的内存。
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}]