gpt4 book ai didi

ruby-on-rails - Ruby on Rails 如何做到这一点?

转载 作者:太空宇宙 更新时间:2023-11-03 17:07:30 25 4
gpt4 key购买 nike

我刚刚开始学习 Rails。所以我创建了一个模型书签,打开 Rails 控制台,然后输入:

Bookmark.all

结果:

Bookmark Load (1.0ms) SELECT "bookmarks".* FROM "bookmarks" etc.

然后我输入了这个:

Bookmark.all.count

结果:

SELECT COUNT(*) FROM "bookmarks"

这种优化的性能提升是显而易见的,所以我理解他们这样做的原因。我不明白的是,如何?我对 Ruby 太陌生,无法轻松搜索源代码。如果您要实现这一点,您会如何处理?

最佳答案

这是一个简单的示例,说明如何将操作推迟到需要数据时执行(请注意,这比 ActiveRecord 简单得多,没有努力提高效率,并且只是描述该概念的最快方式)。

首先,我们有一个简单的数据库,其中包含从 1 到 100 的数字。这将是我们要查询的数据集:

class Database
def numbers
(1..100).to_a
end
end

查询对象将维护对数据库对象的引用,以及一组指令。使用 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

一旦建立了查询的参数,您仍然只有一个查询对象,它还没有向数据库请求任何东西,直到您调用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]

ActiveRecord 为您做了一些好事,包括在必要时自动转换数组。然而,我们可以简单地定义一个 method_missing 来捕获所有尚未为我们的 Query 类定义的消息,而不是实现所有数组/可枚举方法。我们假设它们应该在一个数组上调用,这很方便,就是从 execute 返回的内容。

class Query
def method_missing(name, *args, &block)
execute.send(name, *args, &block)
end
end

现在我们可以获取之前的查询对象,并直接与它交互,就像它是一个数组一样:

q.map { |n| n * 2 }
#=> [12, 24, 36, 48, 60, 72, 84, 96, 108, 120, 132, 144, 156, 168, 180, 192]

q.count #=> 16

但是,您不需要让数组来处理这个。在 ActiveRecord 的情况下,它可以很容易地让转换后的数组响应 count,但是通过在内部定义该方法,可以以更有效的方式构造 SQL 查询。在 ActiveRecord::Relation 上调用不同的方法会更改对象的状态,而该状态最终决定了在检索数据时弹出的查询。

我们还扩展Query API 以响应Ruby 的显式和隐式数组转换方法:

class Query
def to_a
execute
end

def to_ary
to_a
end
end

现在,to_ato_aryexecute 都做同样的事情,但这两个额外的方法给我们带来了一些好处。首先,显式数组转换方法 (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]

同样,execute 从未被显式调用,但 to_ary 方法定义了当您尝试将数组与非数组对象(如 )连接时应该发生的情况查询。我们所做的是创建一个“类数组”对象。

关于ruby-on-rails - Ruby on Rails 如何做到这一点?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21771306/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com