Rub*_*uby 1 ruby ruby-on-rails
我刚开始学习Rails.所以我创建了一个模型Bookmark,打开了rails控制台,并输入了:
Bookmark.all
结果:
加载书签(1.0ms)选择"书签".*FROM"书签"等
然后我输入了这个:
Bookmark.all.count
结果:
SELECT COUNT(*)FROM"bookmarks"
这种优化的性能提升是显而易见的,所以我理解他们为什么这么做.我不明白的是,怎么样?我对Ruby太新了,无法轻松搜索源代码.如果你要实现这个,你会如何处理它?
这是一个简单的示例,说明如何在需要数据之前推迟操作(请注意,这比ActiveRecord简单得多,不费力地提高效率,并且只是描述概念的最快方式).
首先,我们有一个简单的数据库,由1到100的数字组成.这将是我们查询的数据集:
class Database
def numbers
(1..100).to_a
end
end
Run Code Online (Sandbox Code Playgroud)
Query对象将维护对数据库对象的引用以及一组指令.使用该where方法将指令添加到数组中.
class Query
attr_accessor :lambdas, :database
def initialize
@database = Database.new
@lambdas = []
end
def where(condition)
lambdas << condition
# after an instruction is added, return the object to allow more chaining.
self
end
end
Run Code Online (Sandbox Code Playgroud)
一旦建立了查询的参数,您仍然只有一个查询对象没有向数据库询问任何内容,直到您调用execute:
class Query
def execute
lambdas.inject(database.numbers) do |set, lambda|
set.select(&lambda)
end
end
end
# the set of numbers divisible by 2 and 3:
q = Query.new.where(->(x){ x.even? }).where(->(x){ x % 3 == 0 })
q.execute #=> [6, 12, 18, 24, 30, 36, 42, 48, 54, 60, 66, 72, 78, 84, 90, 96]
Run Code Online (Sandbox Code Playgroud)
ActiveRecord为您做了一些不错的事情,包括必要时的自动数组转换.但是,我们可以简单地定义一个method_missing来捕获尚未为Query类定义的所有消息,而不是实现所有数组/可枚举方法.我们只是假设应该在一个数组上调用它们,这很方便,就是从它返回的数据execute.
class Query
def method_missing(name, *args, &block)
execute.send(name, *args, &block)
end
end
Run Code Online (Sandbox Code Playgroud)
所以现在我们可以从之前获取我们的查询对象,并直接与它进行交互,就好像它是一个数组:
q.map { |n| n * 2 }
#=> [12, 24, 36, 48, 60, 72, 84, 96, 108, 120, 132, 144, 156, 168, 180, 192]
q.count #=> 16
Run Code Online (Sandbox Code Playgroud)
但是,您不需要让阵列处理此问题.在ActiveRecord的情况下,它可以轻松地让转换的数组响应count,但通过在内部定义该方法,可以以更有效的方式构造SQL查询.调用不同的方法会ActiveRecord::Relation更改对象的状态,并且该状态最终会确定在检索数据时弹出的查询.
让我们扩展QueryAPI以响应Ruby的显式和隐式数组转换方法:
class Query
def to_a
execute
end
def to_ary
to_a
end
end
Run Code Online (Sandbox Code Playgroud)
所以,现在to_a,to_ary和execute所有做同样的事情,但是这两个额外的方法给我们几个优点.首先,显式数组转换方法(to_a)符合Ruby核心和标准库中已有的约定.我们实际上可以重命名execute为to_a,因为没有必要同时重命名.
to_ary但是,给了我们一些不同的东西.它允许我们将查询对象视为隐式数组,因此如果我们尝试将其添加到另一个数组,或者Array()在其上调用转换方法,它就会按预期运行:
my_array = [1,2,3]
q = Query.new.where(->(x){ x > 95 })
my_array + q # => [1,2,3,96,97,98,99,100]
Run Code Online (Sandbox Code Playgroud)
同样,execute从未显式调用,但该to_ary方法定义了当您尝试将数组与非数组对象连接时应该发生的事情Query.我们所做的是创建一个"类似数组"的对象.