Created
November 21, 2013 21:10
-
-
Save boy-jer/7589684 to your computer and use it in GitHub Desktop.
mongoid-activerecord-eagerloadable
source https://rubygems.org/gems/mongoid-activerecord-eagerloadable
This file contains hidden or 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
module Mongoid | |
module ActiveRecord | |
module EagerLoadable | |
module Criteria | |
extend ActiveSupport::Concern | |
def includes_active_record(*relations) | |
relations.each do |relation| | |
metadata = RelationMetadata.new(relation) | |
active_record_inclusions.push(metadata) unless active_record_inclusions.include?(metadata) | |
end | |
clone | |
end | |
def active_record_inclusions | |
@active_record_inclusions ||= [] | |
end | |
end | |
end | |
end | |
end |
This file contains hidden or 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
module Mongoid | |
module ActiveRecord | |
module EagerLoadable | |
module Eager | |
extend ActiveSupport::Concern | |
included do | |
alias_method :original_eager_load, :eager_load | |
alias_method :original_eager_loadable?, :eager_loadable? | |
def eager_load(docs) | |
load_active_record_inclusions(docs) | |
original_eager_load(docs) | |
end | |
def eager_loadable?(document = nil) | |
return false if criteria.inclusions.empty? && criteria.active_record_inclusions.empty? | |
document ? !inclusions_loaded?(document) : !eager_loaded | |
end | |
end | |
def load_active_record_inclusions(docs) | |
criteria.active_record_inclusions.each do |metadata| | |
metadata.eager_load(eager_loaded_ids(docs, metadata)) if !docs.empty? | |
end | |
end | |
end | |
end | |
end | |
end |
This file contains hidden or 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 'active_support/core_ext/module/delegation' | |
require 'mongoid/active_record/eager_loadable/version' | |
require 'mongoid/active_record/eager_loadable/eager' | |
require 'mongoid/active_record/eager_loadable/relation_metadata' | |
require 'mongoid/active_record/eager_loadable/criteria' | |
module Mongoid | |
module ActiveRecord | |
module EagerLoadable | |
extend ActiveSupport::Concern | |
included do | |
Mongoid::Contextual::Mongo.send(:include, Eager) | |
Mongoid::Contextual::Memory.send(:include, Eager) | |
Mongoid::Criteria.send(:include, Criteria) | |
@active_record_relations = {} | |
end | |
module ClassMethods | |
# todo: put this in Accessors similar to mongoid | |
def active_record_getter(name, metadata) | |
re_define_method name do | |
record_id = self.send(metadata.foreign_key) | |
IdentityMap.get(metadata.relation_class, record_id) || metadata.relation_class.find(record_id) | |
end | |
self | |
end | |
def active_record_relate(name, metadata) | |
@active_record_relations[name] = metadata | |
active_record_getter(name, metadata) | |
end | |
def belongs_to_active_record(association_id) | |
metadata = RelationMetadata.new(association_id) | |
active_record_relate(association_id, metadata) | |
end | |
end | |
end | |
end | |
end |
This file contains hidden or 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 'active_record' | |
require 'test/unit' | |
require 'mongoid' | |
require './lib/mongoid/active_record/eager_loadable.rb' | |
ENV['MONGOID_ENV'] = 'test' | |
Mongoid.load!('./config/mongoid.yml', :test) | |
ActiveRecord::Base.establish_connection( | |
adapter: 'sqlite3', | |
database: ':memory:' | |
) | |
require './test/support/models' | |
require './test/support/active_record/sql_counter.rb' | |
ActiveRecord::Base.logger = Logger.new(File.join(File.dirname(__FILE__), "../log/debug.log")) | |
ActiveSupport::Notifications.subscribe('sql.active_record', ActiveRecord::SQLCounter.new) | |
class EagerLoadableTest < ActiveRecord::TestCase | |
def setup | |
@jim = Person.create!(name: 'Jim') | |
@bill = Person.create!(name: 'Bill') | |
@bob = Person.create!(name: 'Bob') | |
@joe = Person.create!(name: 'Joe') | |
@jack = Person.create!(name: 'Jack') | |
@fletch = Pet.create!(name: 'Fletch', person_id: @jim.id) | |
@wilson = Pet.create!(name: 'Wilson', person_id: @bill.id) | |
@mocha = Pet.create!(name: 'Mocha', person_id: @bob.id) | |
@tanner = Pet.create!(name: 'Tanner', person_id: @joe.id) | |
@freckles = Pet.create!(name: 'Freckles', person_id: @jack.id) | |
end | |
def teardown | |
Person.delete_all | |
Pet.delete_all | |
end | |
def test_respond_to_belongs_to_active_record | |
assert_respond_to Pet, :belongs_to_active_record | |
end | |
def test_respond_to_includes_active_record | |
assert_respond_to Pet.scoped, :includes_active_record | |
end | |
def test_mongoid_model_responds_to_association_method | |
assert_respond_to Pet.first, :person | |
end | |
def test_mongoid_model_without_includes_active_record_finds_associated_model_instance | |
assert_kind_of Person, Pet.first.person | |
end | |
def test_mongoid_model_with_includes_active_record_finds_associated_model_instance | |
assert_kind_of Person, Pet.scoped.includes_active_record(:person).first.person | |
end | |
def test_getter_method_returns_proper_associatied_object_without_eager_loading | |
assert_equal Pet.first.person, Person.find(Pet.first.person_id) | |
end | |
def test_getter_method_returns_proper_associatied_object_with_eager_loading | |
assert_equal Pet.scoped.includes_active_record(:person).first.person, | |
Person.find(Pet.scoped.includes_active_record(:person).first.person_id) | |
end | |
def test_multiple_queries_execute_without_eager_loading | |
assert_queries(5) do | |
Pet.all.map(&:person) | |
end | |
end | |
def test_one_query_executes_with_eager_loading | |
assert_queries(1) do | |
Pet.scoped.includes_active_record(:person).map(&:person) | |
end | |
end | |
end | |
This file contains hidden or 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
class Pet | |
include Mongoid::Document | |
include Mongoid::ActiveRecord::EagerLoadable | |
belongs_to_active_record :person | |
end | |
class Person < ActiveRecord::Base | |
attr_accessible :name | |
end | |
#The `belongs_to_active_record :person` statement enables the following | |
#to execute a single query against your mongodb instance and a single | |
#query against your ActiveRecord connection: | |
# Pet.scoped.includes_active_record(:person).all.map(&:person) | |
#Also, the `person` method is added to each instance of `Pet` regardless | |
#of whether or not the `includes_active_record` method is invoked |
This file contains hidden or 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
module Mongoid | |
module ActiveRecord | |
module EagerLoadable | |
class RelationMetadata | |
attr_reader :relation | |
def initialize(relation) | |
@relation = relation | |
relation_class.define_singleton_method(:collection_name) { table_name } | |
end | |
def eager_load(ids) | |
relation_class.where(id: ids.uniq).each do |record| | |
IdentityMap.set(record) | |
end | |
end | |
def relation_class | |
relation.to_s.classify.constantize | |
end | |
def foreign_key | |
"#{relation}_id" | |
end | |
def stores_foreign_key? | |
true | |
end | |
end | |
end | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment