Ruby 用十几行代码阐述了一个非常小的类 ActiveRecord::Relation 的实现思路

ad583255925 · March 10, 2017 · Last by clarkyi replied at March 13, 2017 · 1740 hits

首先用 scope 把这些查询语句转发到 relation scoped 这个方法先把 Relation 绑到自己的 class 上,再用这个新的 class 去实例化 Relation,达到的效果是,返回的 relation 对象是 SubCat::Relation,就跟 AR 一样

然后把数组常用的方法全部绑定到 to_a 这个方法上,to_a 这个方法里面去查询和返回,就能达到 AR 的那种懒加载了,假如有一天不用 sql 数据库了,就用这种思路写就好了

贴代码,不要截图

Reply to huacnlee

能用 markdown?

Reply to ad583255925

必须啊... github flavored markdown

relation.rb

class Relation
  delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join, to: :to_a

  def initialize options = {}
    raise 'class is not initialize' if options[:model].blank?
    @class_name = options[:model]
    @where = options[:where] || []
    @limit = options[:limit] || []
    @order = options[:order] || []
    @page  = options[:page] || []
    @per_page = []
    @records = []
  end

  def inspect
    to_a
    "#<#{self.class}: records: #{self.instance_variable_get(:@records)} attrs: #{self.instance_values}>"
  end

  %w/where limit order page per_page/.each do |m|
    define_method m do |args=nil|
      self.instance_variable_get("@#{m}".to_sym) << args if args.present?
      self
    end
  end

  def to_a
    load_records
    @records
  end

  def load_records

    #查询集中在这里
    @records = ['a', 'b', 'c']
  end
end

cat.rb

class Cat
  delegate :select, :group, :order, :reorder, :limit, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, to: :scoped

  def scoped
    @model ||= self.class.const_set 'Relation', Class.new(Relation)
    @model.new(model: self.class)
  end

end

subcat.rb

class SubCat < Cat
end

 c = SubCat.new
#<SubCat:0x007fe078c21658>

c.where.class
SubCat::Relation

c.where('1=1').order('test asc').limit(1)
#<SubCat::Relation: records: ["a", "b", "c"] attrs: {"class_name"=>SubCat, "where"=>["1=1"], "limit"=>[1], "order"=>["test asc"], "page"=>[], "per_page"=>[], "records"=>["a", "b", "c"]}>

c.where('1=1').order('test asc').limit(1).each{|c| p c }
"a"
"b"
"c"
["a", "b", "c"]
Reply to ad583255925

你更新到原文上嘛...

You need to Sign in before reply, if you don't have an account, please Sign up first.