26
Возможности, особенности и проблемы AR::Relation Тимофей Цветков, Evil Martians 2011

Возможности, особенности и проблемы AR::Relation

  • Upload
    -

  • View
    125

  • Download
    1

Embed Size (px)

Citation preview

Возможности, особенности и

проблемы AR::RelationТимофей Цветков, Evil Martians

2011

Мотивации слайд

Я сказал убей!

Look at them!

SO HAPPY OMG

OMG LOL

ActiveRecord — не черный ящик

Not only Arel

• Queries are not only Arel

• AR ≠ Arel

3.x (x ≤1)

• It’s not only Arel! AR::Relation was released

• Identity Map (WHORRAAAA)

ActiveRecord

module ActiveRecord #:nodoc: class << self # Class methods delegate :find, :first, :first!, :last, :last!, :all, :exists?, :any?, :many?, :to => :scoped delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :scoped delegate :find_each, :find_in_batches, :to => :scoped delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins, :where, :preload, :eager_load, :includes, :from, :lock, :readonly, :having, :create_with, :to => :scoped delegate :count, :average, :minimum, :maximum, :sum, :calculate, :to => :scoped endend

Scoped

def scoped(options = nil) if options scoped.apply_finder_options(options) else if current_scope current_scope.clone else scope = relation.clone scope.default_scoped = true scope end end end

Finder methods

• where

• having

• select

• order

• limit

• offset

All finder methods work through AR::Relation

• joins

• includes

• lock

• readonly

• from

Example. Where

def where(opts, *rest) return self if opts.blank?

relation = clone relation.where_values += build_where(opts, rest) relationend

ActiveRecord::Relation

def to_a @records = if @readonly_value.nil? && [email protected]_enabled? eager_loading? ? find_with_associations : @klass.find_by_sql(arel.to_sql, @bind_values) else IdentityMap.without do eager_loading? ? find_with_associations : @klass.find_by_sql(arel.to_sql, @bind_values) end endend

Real quering from the AR::Relation

AR::Relation problems

• merging default_scope’s with labmda (solved):

default_scope where(:name => "qwerty")default_scope lambda { order("id DESC") }

• except (I’ve wrote a patch):

scope :xxx, order("id DESC")scope :yyy, except(:order).order("id ASC")

@surveys = Survey.xxx.yyy

Survey Load (0.1ms) SELECT "surveys".* FROM "surveys" ORDER BY id DESC, id ASC

IdentityMap

Identity map was written by Emilio Tagua

Emilio Tagua

LEHAIM!

Without IdentityMap

user1 = User.find(1) # => #<User id: 1, name: "Josh">user2 = User.find(1) # => #<User id: 1, name: "Josh">

user1 == user2 # => true, b/c AR::Base recognizes that # they have the same primary key

user1.object_id == user2.object_id # => false, b/c these are two # different in-memory objects

User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 1]]CACHE (0.0ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 1]]

Code example from: http://edgerails.info/articles/what-s-new-in-edge-rails/2011/04/21/activerecord-identity-map/index.html

With IdentityMap

user1 = User.find(1) # => #<User id: 1, name: "Josh">user2 = User.find(1) # => #<User id: 1, name: "Josh">

user1 == user2 # => true

user1.object_id == user2.object_id # => true, b/c these really are # the same in-memory objects

User Load (2.2ms) SELECT "users".* FROM "users" LIMIT 1User with ID = 1 loaded from Identity Map

Code example from: http://edgerails.info/articles/what-s-new-in-edge-rails/2011/04/21/activerecord-identity-map/index.html

Even more...

post = Post.find(1)

same_post = post.comments.first.post

post.object_id == same_post.object_id # => true

Code example from: http://miloops.com/post/3391477665/identity-map-and-active-record

Thread safe

• Creates for each request

• Uses Thread.current

def repository Thread.current[:identity_map] ||= Hash.new { |h,k| h[k] = {} }end

Interesting...def update @survey = Survey.find(params[:id]) @surveys = Survey.all

if @survey.update_attributes(params[:survey]) redirect_to(@survey, :notice => 'Survey was successfully updated.') else render :action => "edit" end end

def update @survey = Survey.find(params[:id])

if @survey.update_attributes(params[:survey]) redirect_to(@survey, :notice => 'Survey was successfully updated.') else @surveys = Survey.all

render :action => "edit" endend

How to use IdentityMap

• It will be in Rails 3.1

• To enable write in your application.rb:

config.active_record.identity_map = true

Contribute!!!112233

Троллинг