Permalink
Browse files

copy from pull request http://github.com/mongoid/mongoid/pull/391

  • Loading branch information...
1 parent bd4b5db commit 0e5f563ed17fb36019fff0ac4cf70fd09743b0e2 @flyerhzm committed Oct 26, 2010
Showing with 1,006 additions and 13 deletions.
  1. +1 −0 .gitignore
  2. +2 −0 .rvmrc.example
  3. +24 −0 .watchr
  4. +54 −0 Gemfile.lock
  5. +5 −4 lib/mongoid-eager-loading.rb
  6. +18 −0 lib/mongoid-eager-loading/mongoid/criteria.rb
  7. +116 −0 lib/mongoid-eager-loading/mongoid/criterion/eager_loading.rb
  8. +5 −0 lib/mongoid-eager-loading/mongoid/finders.rb
  9. +2 −4 lib/mongoid-eager-loading/version.rb
  10. +11 −5 mongoid-eager-loading.gemspec
  11. +21 −0 spec/config/mongoid.yml
  12. +10 −0 spec/models/account.rb
  13. +52 −0 spec/models/address.rb
  14. +8 −0 spec/models/agent.rb
  15. +15 −0 spec/models/animal.rb
  16. +4 −0 spec/models/answer.rb
  17. +47 −0 spec/models/callbacks.rb
  18. +13 −0 spec/models/category.rb
  19. +10 −0 spec/models/comment.rb
  20. +6 −0 spec/models/country_code.rb
  21. +8 −0 spec/models/description.rb
  22. +5 −0 spec/models/employer.rb
  23. +8 −0 spec/models/favorite.rb
  24. +14 −0 spec/models/game.rb
  25. +72 −0 spec/models/inheritance.rb
  26. +5 −0 spec/models/location.rb
  27. +6 −0 spec/models/login.rb
  28. +4 −0 spec/models/mixed_drink.rb
  29. +13 −0 spec/models/name.rb
  30. +11 −0 spec/models/namespacing.rb
  31. +18 −0 spec/models/paranoid_post.rb
  32. +32 −0 spec/models/parents.rb
  33. +15 −0 spec/models/patient.rb
  34. +113 −0 spec/models/person.rb
  35. +7 −0 spec/models/pet.rb
  36. +6 −0 spec/models/pet_owner.rb
  37. +7 −0 spec/models/phone.rb
  38. +25 −0 spec/models/post.rb
  39. +7 −0 spec/models/preference.rb
  40. +8 −0 spec/models/question.rb
  41. +6 −0 spec/models/survey.rb
  42. +5 −0 spec/models/translation.rb
  43. +8 −0 spec/models/user.rb
  44. +9 −0 spec/models/user_account.rb
  45. +5 −0 spec/models/vet_visit.rb
  46. +5 −0 spec/models/video.rb
  47. +28 −0 spec/mongoid-eager-loading/mongoid/criteria_spec.rb
  48. +103 −0 spec/mongoid-eager-loading/mongoid/criterion/eager_loading_spec.rb
  49. +29 −0 spec/spec_helper.rb
View
@@ -1,3 +1,4 @@
pkg/*
*.gem
.bundle
+.rvmrc
View
@@ -0,0 +1,2 @@
+rvm_gemset_create_on_use_flag=1
+rvm gemset use mongoid-eager-loading
View
24 .watchr
@@ -0,0 +1,24 @@
+# vim:set filetype=ruby:
+def run(cmd)
+ puts cmd
+ system cmd
+end
+
+def spec(file)
+ if File.exists?(file)
+ run("rspec #{file}")
+ else
+ puts("Spec: #{file} does not exist.")
+ end
+end
+
+watch("spec/.*/*_spec\.rb") do |match|
+ puts(match[0])
+ spec(match[0])
+end
+
+watch("lib/(.*/.*)\.rb") do |match|
+ puts(match[1])
+ spec("spec/#{match[1]}_spec.rb")
+end
+
View
@@ -0,0 +1,54 @@
+PATH
+ remote: .
+ specs:
+ mongoid-eager-loading (0.0.1)
+ mongoid (~> 2.0.0.beta.19)
+
+GEM
+ remote: http://rubygems.org/
+ specs:
+ activemodel (3.0.1)
+ activesupport (= 3.0.1)
+ builder (~> 2.1.2)
+ i18n (~> 0.4.1)
+ activesupport (3.0.1)
+ bson (1.1.1)
+ bson_ext (1.1.1)
+ builder (2.1.2)
+ diff-lcs (1.1.2)
+ i18n (0.4.1)
+ mocha (0.9.9)
+ rake
+ mongo (1.0.9)
+ bson (>= 1.0.5)
+ mongoid (2.0.0.beta.19)
+ activemodel (~> 3.0)
+ mongo (= 1.0.9)
+ tzinfo (~> 0.3.22)
+ will_paginate (~> 3.0.pre)
+ rake (0.8.7)
+ rspec (2.0.1)
+ rspec-core (~> 2.0.1)
+ rspec-expectations (~> 2.0.1)
+ rspec-mocks (~> 2.0.1)
+ rspec-core (2.0.1)
+ rspec-expectations (2.0.1)
+ diff-lcs (>= 1.1.2)
+ rspec-mocks (2.0.1)
+ rspec-core (~> 2.0.1)
+ rspec-expectations (~> 2.0.1)
+ tzinfo (0.3.23)
+ watchr (0.7)
+ will_paginate (3.0.pre2)
+
+PLATFORMS
+ ruby
+
+DEPENDENCIES
+ bson_ext
+ bundler (>= 1.0.0)
+ mocha
+ mongoid (~> 2.0.0.beta.19)
+ mongoid-eager-loading!
+ rspec (~> 2.0.0)
+ watchr
@@ -1,7 +1,8 @@
+require 'mongoid-eager-loading/mongoid/criterion/eager_loading'
+require 'mongoid-eager-loading/mongoid/criteria'
+require 'mongoid-eager-loading/mongoid/finders'
+
module Mongoid
- module Eager
- module Loading
- # Your code goes here...
- end
+ module EagerLoading
end
end
@@ -0,0 +1,18 @@
+Mongoid::Criteria.class_eval do
+ include Mongoid::Criterion::EagerLoading
+
+ alias_method :origin_each, :each
+
+ def each(&block)
+ if @eager_loadings
+ # if eager loadings are used, preload the associations.
+ docs = []
+ context.iterate { |doc| docs << doc }
+ preload(docs)
+ docs.each(&block)
+ self
+ else
+ origin_each(&block)
+ end
+ end
+end
@@ -0,0 +1,116 @@
+module Mongoid
+ module Criterion
+ module EagerLoading
+ # EagerLoading criterion are used when eager loading the associations.
+ #
+ # Example:
+ #
+ # <tt>criteria.includes(:user)</tt>
+ #
+ # <tt>criteria.includes(:user, :post)</tt>
+ #
+ # Returns: <tt>self</tt>
+ attr_accessor :eager_loadings, :id_document_map, :id_associations_map
+
+ def includes(*options)
+ @eager_loadings = options
+ self
+ end
+
+ def preload(documents)
+ document_class = documents.first.class
+ @eager_loadings.each do |eager_loading|
+ setup_associations(documents, association_reflection(document_class, eager_loading))
+ end
+ end
+
+ private
+ def ignore_includes
+ @eager_loadings = nil
+ end
+
+ def association_reflection(document_class, eager_loading)
+ document_class.reflect_on_association(eager_loading)
+ end
+
+ def setup_associations(documents, reflection)
+ if reflection.association == Mongoid::Associations::ReferencesOne
+ setup_associations_with_ids(documents, reflection, true)
+ elsif reflection.association == Mongoid::Associations::ReferencesMany
+ setup_associations_with_ids(documents, reflection, false)
+ elsif reflection.association == Mongoid::Associations::ReferencesManyAsArray
+ setup_associations_with_foreign_keys(documents, reflection, false)
+ elsif reflection.association == Mongoid::Associations::ReferencedIn
+ setup_associations_with_foreign_keys(documents, reflection, true)
+ end
+ end
+
+ def setup_associations_with_ids(documents, reflection, one=true)
+ ids = []
+ documents.each do |document|
+ id_document_map[document.id] = document
+ ids << document.id
+ end
+
+ association_class = reflection.name.singularize.camelize.constantize
+ ignore_includes
+ eager_associations = association_class.where(reflection.foreign_key.to_sym.in => ids).to_a
+ eager_associations.each do |eager_association|
+ add_id_association(eager_association.send(reflection.foreign_key), eager_association)
+ end
+
+ id_document_map.each do |id, document|
+ document.send("#{reflection.name}=", one ? id_associations_map[id].first : id_associations_map[id])
+ end
+ end
+
+ def setup_associations_with_foreign_keys(documents, reflection, one)
+ ids = []
+ foreign_key_name = reflection.foreign_key
+ documents.each do |document|
+ foreign_key_value = document.send(foreign_key_name)
+ if one
+ id_document_map[foreign_key_value] = document
+ ids << foreign_key_value
+ elsif foreign_key_value
+ foreign_key_value.each do |fkv|
+ id_document_map[fkv] = document
+ ids << fkv
+ end
+ end
+ end
+
+ association_class = reflection.name.singularize.camelize.constantize
+ ignore_includes
+ eager_associations = association_class.find(ids).to_a
+ eager_associations.each do |eager_association|
+ add_id_association(eager_association.id, eager_association)
+ end
+
+ id_document_map.each do |id, document|
+ foreign_key_value = document.send(foreign_key_name)
+ associations = \
+ if one
+ id_associations_map[foreign_key_value].first
+ else
+ foreign_key_value.collect { |fkv| id_associations_map[fkv] }.flatten.uniq
+ end
+ document.send("#{reflection.name}=", associations)
+ end
+ end
+
+ def id_document_map
+ @id_doccument_map ||= {}
+ end
+
+ def id_associations_map
+ @id_associations_map ||= {}
+ end
+
+ def add_id_association(id, association)
+ id_associations_map[id] ||= []
+ id_associations_map[id] << association
+ end
+ end
+ end
+end
@@ -0,0 +1,5 @@
+Mongoid::Finders.class_eval do
+ def includes(*args)
+ criteria.send(:includes, *args)
+ end
+end
@@ -1,7 +1,5 @@
module Mongoid
- module Eager
- module Loading
- VERSION = "0.0.1"
- end
+ module EagerLoading
+ VERSION = "0.0.1"
end
end
@@ -3,18 +3,24 @@ require File.expand_path("../lib/mongoid-eager-loading/version", __FILE__)
Gem::Specification.new do |s|
s.name = "mongoid-eager-loading"
- s.version = Mongoid::Eager::Loading::VERSION
+ s.version = Mongoid::EagerLoading::VERSION
s.platform = Gem::Platform::RUBY
- s.authors = []
- s.email = []
+ s.authors = ["Richard Huang"]
+ s.email = ["flyerhzm@gmail.com"]
s.homepage = "http://rubygems.org/gems/mongoid-eager-loading"
- s.summary = "TODO: Write a gem summary"
- s.description = "TODO: Write a gem description"
+ s.summary = "eager loading for mongoid"
+ s.description = "eager loading for mongoid"
s.required_rubygems_version = ">= 1.3.6"
s.rubyforge_project = "mongoid-eager-loading"
+ s.add_dependency "mongoid", "~> 2.0.0.beta.18"
+
s.add_development_dependency "bundler", ">= 1.0.0"
+ s.add_development_dependency "rspec", "~> 2.0.0"
+ s.add_development_dependency "mocha"
+ s.add_development_dependency "watchr"
+ s.add_development_dependency "bson_ext"
s.files = `git ls-files`.split("\n")
s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
View
@@ -0,0 +1,21 @@
+defaults: &defaults
+ host: localhost
+ slaves:
+ # - host: localhost
+ # port: 27018
+ # - host: localhost
+ # port: 27019
+ allow_dynamic_fields: false
+ include_root_in_json: true
+ parameterize_keys: false
+ persist_in_safe_mode: false
+ raise_not_found_error: false
+ reconnect_time: 5
+ autocreate_indexes: false
+ persist_types: false
+ option_no_exist: false
+ skip_version_check: false
+
+test:
+ <<: *defaults
+ database: mongoid_config_test
View
@@ -0,0 +1,10 @@
+class Account
+ include Mongoid::Document
+ referenced_in :creator, :class_name => "User", :foreign_key => :creator_id
+ referenced_in :person
+ field :number
+ field :balance
+ field :nickname
+
+ attr_accessible :nickname
+end
View
@@ -0,0 +1,52 @@
+class Address
+ include Mongoid::Document
+
+ attr_accessor :mode
+
+ field :address_type
+ field :number, :type => Integer
+ field :street
+ field :city
+ field :state
+ field :post_code
+ field :parent_title
+ field :services, :type => Array
+ field :latlng, :type => Array
+ key :street
+ embeds_many :locations
+
+ embedded_in :addressable, :inverse_of => :addresses do
+ def extension
+ "Testing"
+ end
+ def doctor?
+ title == "Dr"
+ end
+ end
+
+ named_scope :rodeo, where(:street => "Rodeo Dr") do
+ def mansion?
+ all? { |address| address.street == "Rodeo Dr" }
+ end
+ end
+
+ validates_presence_of :street, :on => :update
+
+ def set_parent=(set = false)
+ self.parent_title = addressable.title if set
+ end
+
+ class << self
+ def california
+ where(:state => "CA")
+ end
+
+ def homes
+ where(:address_type => "Home")
+ end
+
+ def streets
+ all.map(&:street)
+ end
+ end
+end
View
@@ -0,0 +1,8 @@
+class Agent
+ include Mongoid::Document
+ field :title
+ field :number
+ embeds_many :names
+ references_many :posts, :foreign_key => :poster_id
+ accepts_nested_attributes_for :posts, :allow_destroy => true
+end
View
@@ -0,0 +1,15 @@
+class Animal
+ include Mongoid::Document
+ field :name
+ field :tags, :type => Array
+ key :name
+ embedded_in :person, :inverse_of => :pet
+
+ def tag_list
+ tags.join(", ")
+ end
+
+ def tag_list=(_tag_list)
+ self.tags = _tag_list.split(",").map(&:strip)
+ end
+end
View
@@ -0,0 +1,4 @@
+class Answer
+ include Mongoid::Document
+ embedded_in :question, :inverse_of => :answers
+end
Oops, something went wrong.

0 comments on commit 0e5f563

Please sign in to comment.