Created
August 23, 2012 09:05
-
-
Save denyago/3434469 to your computer and use it in GitHub Desktop.
Add belongs_to_remote to preload collections of ActiveResources without N+1 requests
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
source :rubygems | |
gem 'pg' | |
gem "hashie" | |
gem 'activerecord', require: 'active_record' | |
gem 'activesupport' | |
gem 'activeresource', :require => 'active_resource' | |
gem "debugger" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'bundler/setup' | |
Bundler.require :default | |
require 'debugger' | |
require 'active_record' | |
require 'active_support' | |
require 'active_resource' | |
ActiveRecord::Base.establish_connection( | |
{ adapter: 'postgresql', | |
encoding: 'unicode', | |
database: 'ar_dm2', | |
host: 'localhost', | |
port: 5432, | |
pool: 5, | |
username: 'rails', | |
password: 'rails'} | |
) | |
ActiveRecord::Base.connection.execute(<<SQL | |
DROP TABLE IF EXISTS "public"."users"; | |
DROP SEQUENCE IF EXISTS "users_id_seq"; | |
CREATE SEQUENCE "users_id_seq" INCREMENT 1 START 1 MAXVALUE 9223372036854775807 MINVALUE 1 CACHE 1; | |
ALTER TABLE "users_id_seq" OWNER TO "rails"; | |
CREATE TABLE "public"."users" ( | |
"id" int4 NOT NULL DEFAULT nextval('users_id_seq'::regclass), | |
"name" varchar(255) NOT NULL, | |
"age" int4, | |
CONSTRAINT "users_pkey" PRIMARY KEY ("id") NOT DEFERRABLE INITIALLY IMMEDIATE | |
) | |
WITH (OIDS=FALSE); | |
ALTER TABLE "public"."users" OWNER TO "rails"; | |
insert into "public"."users" ( "name", "age") values ( 'octocat', 18); | |
insert into "public"."users" ( "name", "age") values ( 'dhh', 26); | |
SQL | |
) | |
class Profile < ActiveResource::Base | |
self.site = "http://127.0.0.1:3000" | |
end | |
class Facebook < ActiveResource::Base | |
self.site = "http://127.0.0.1:3000" | |
end | |
module RemoteBelonger | |
def self.included(base) | |
base.extend ClassMethods | |
base.send :include, InstanceMethods | |
base.class_eval do | |
end | |
end | |
module ClassMethods | |
def belongs_to_remote(remote_rel, options ={}) | |
rel = activeresource_relations | |
rel[remote_rel.to_sym] = { | |
klass: ( options[:class_name] ? options[:class_name].constantize : remote_rel.to_s.classify.constantize), | |
join_key: ( options[:foreign_key] ? options[:foreign_key] : self.model_name.to_s.foreign_key ) | |
} | |
class_eval <<-RUBY, __FILE__, __LINE__+1 | |
attr_accessor :#{remote_rel} | |
def #{remote_rel} | |
if @remote_resources_prefetched == true | |
@#{remote_rel} ? @#{remote_rel}.first : nil | |
else | |
@#{remote_rel} ||= #{rel[remote_rel.to_sym][:klass].to_s}.find(:first, params: { #{rel[remote_rel.to_sym][:join_key]}: [self.id]}) | |
end | |
end | |
RUBY | |
@activeresource_relations = rel | |
end | |
def has_many_remote(remote_rel, options ={}) | |
end | |
def activeresource_relations | |
@activeresource_relations ||= {} | |
end | |
end | |
module InstanceMethods | |
end | |
end | |
class User < ActiveRecord::Base | |
include RemoteBelonger | |
validates :name, presence: true | |
belongs_to_remote :profile, class_name: "Profile", foreign_key: 'user_id' | |
belongs_to_remote :facebook | |
delegate :likes, :to => :profile, :prefix => true, :allow_nil => true | |
delegate :hates, :to => :facebook, :prefix => true, :allow_nil => true | |
end | |
module ActiveRecord | |
class Relation | |
def prefetch_remote_resources | |
keys = self.map(&:id) | |
klass.activeresource_relations.each do |k,d| | |
join_key = d[:join_key] | |
activeresource_accessor = k.to_s | |
activeresource_klass = d[:klass] | |
set = activeresource_klass.find(:all, :params => { join_key => keys }) | |
self.each do |u| | |
u.send("#{activeresource_accessor}=", set.select {|s| s.send(join_key) == u.id }) | |
u.instance_variable_set(:@remote_resources_prefetched, true) | |
end | |
end | |
self.all | |
end | |
end | |
end | |
puts User.scoped.prefetch_remote_resources.to_json(methods: [:profile_likes, :facebook_hates]) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment