Ruby on Rails如何做到这一点?

Rub*_*uby 1 ruby ruby-on-rails

我刚开始学习Rails.所以我创建了一个模型Bookmark,打开了rails控制台,并输入了:

Bookmark.all

结果:

加载书签(1.0ms)选择"书签".*FROM"书签"等

然后我输入了这个:

Bookmark.all.count

结果:

SELECT COUNT(*)FROM"bookmarks"

这种优化的性能提升是显而易见的,所以我理解他们为什么这么做.我不明白的是,怎么样?我对Ruby太新了,无法轻松搜索源代码.如果你要实现这个,你会如何处理它?

Zac*_*emp 5

这是一个简单的示例,说明如何在需要数据之前推迟操作(请注意,这比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_aryexecute所有做同样的事情,但是这两个额外的方法给我们几个优点.首先,显式数组转换方法(to_a)符合Ruby核心和标准库中已有的约定.我们实际上可以重命名executeto_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.我们所做的是创建一个"类似数组"的对象.