mongoid-eager-loading 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/.gitignore +4 -0
  2. data/.rvmrc.example +2 -0
  3. data/.watchr +24 -0
  4. data/Gemfile +4 -0
  5. data/Gemfile.lock +54 -0
  6. data/LICENSE +20 -0
  7. data/README.md +35 -0
  8. data/Rakefile +45 -0
  9. data/lib/mongoid-eager-loading.rb +8 -0
  10. data/lib/mongoid-eager-loading/mongoid/criteria.rb +18 -0
  11. data/lib/mongoid-eager-loading/mongoid/criterion/eager_loading.rb +116 -0
  12. data/lib/mongoid-eager-loading/mongoid/finders.rb +5 -0
  13. data/lib/mongoid-eager-loading/version.rb +5 -0
  14. data/mongoid-eager-loading.gemspec +28 -0
  15. data/spec/config/mongoid.yml +21 -0
  16. data/spec/models/account.rb +10 -0
  17. data/spec/models/address.rb +52 -0
  18. data/spec/models/agent.rb +8 -0
  19. data/spec/models/animal.rb +15 -0
  20. data/spec/models/answer.rb +4 -0
  21. data/spec/models/callbacks.rb +47 -0
  22. data/spec/models/category.rb +13 -0
  23. data/spec/models/comment.rb +10 -0
  24. data/spec/models/country_code.rb +6 -0
  25. data/spec/models/description.rb +8 -0
  26. data/spec/models/employer.rb +5 -0
  27. data/spec/models/favorite.rb +8 -0
  28. data/spec/models/game.rb +14 -0
  29. data/spec/models/inheritance.rb +72 -0
  30. data/spec/models/location.rb +5 -0
  31. data/spec/models/login.rb +6 -0
  32. data/spec/models/mixed_drink.rb +4 -0
  33. data/spec/models/name.rb +13 -0
  34. data/spec/models/namespacing.rb +11 -0
  35. data/spec/models/paranoid_post.rb +18 -0
  36. data/spec/models/parents.rb +32 -0
  37. data/spec/models/patient.rb +15 -0
  38. data/spec/models/person.rb +113 -0
  39. data/spec/models/pet.rb +7 -0
  40. data/spec/models/pet_owner.rb +6 -0
  41. data/spec/models/phone.rb +7 -0
  42. data/spec/models/post.rb +25 -0
  43. data/spec/models/preference.rb +7 -0
  44. data/spec/models/question.rb +8 -0
  45. data/spec/models/survey.rb +6 -0
  46. data/spec/models/translation.rb +5 -0
  47. data/spec/models/user.rb +8 -0
  48. data/spec/models/user_account.rb +9 -0
  49. data/spec/models/vet_visit.rb +5 -0
  50. data/spec/models/video.rb +5 -0
  51. data/spec/mongoid-eager-loading/mongoid/criteria_spec.rb +26 -0
  52. data/spec/mongoid-eager-loading/mongoid/criterion/eager_loading_spec.rb +103 -0
  53. data/spec/spec_helper.rb +29 -0
  54. metadata +213 -0
@@ -0,0 +1,4 @@
1
+ pkg/*
2
+ *.gem
3
+ .bundle
4
+ .rvmrc
@@ -0,0 +1,2 @@
1
+ rvm_gemset_create_on_use_flag=1
2
+ rvm gemset use mongoid-eager-loading
data/.watchr ADDED
@@ -0,0 +1,24 @@
1
+ # vim:set filetype=ruby:
2
+ def run(cmd)
3
+ puts cmd
4
+ system cmd
5
+ end
6
+
7
+ def spec(file)
8
+ if File.exists?(file)
9
+ run("rspec #{file}")
10
+ else
11
+ puts("Spec: #{file} does not exist.")
12
+ end
13
+ end
14
+
15
+ watch("spec/.*/*_spec\.rb") do |match|
16
+ puts(match[0])
17
+ spec(match[0])
18
+ end
19
+
20
+ watch("lib/(.*/.*)\.rb") do |match|
21
+ puts(match[1])
22
+ spec("spec/#{match[1]}_spec.rb")
23
+ end
24
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :gemcutter
2
+
3
+ # Specify your gem's dependencies in mongoid-eager-loading.gemspec
4
+ gemspec
@@ -0,0 +1,54 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ mongoid-eager-loading (0.1.0)
5
+ mongoid (~> 2.0.0.beta.18)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ activemodel (3.0.1)
11
+ activesupport (= 3.0.1)
12
+ builder (~> 2.1.2)
13
+ i18n (~> 0.4.1)
14
+ activesupport (3.0.1)
15
+ bson (1.1.1)
16
+ bson_ext (1.1.1)
17
+ builder (2.1.2)
18
+ diff-lcs (1.1.2)
19
+ i18n (0.4.1)
20
+ mocha (0.9.9)
21
+ rake
22
+ mongo (1.0.9)
23
+ bson (>= 1.0.5)
24
+ mongoid (2.0.0.beta.19)
25
+ activemodel (~> 3.0)
26
+ mongo (= 1.0.9)
27
+ tzinfo (~> 0.3.22)
28
+ will_paginate (~> 3.0.pre)
29
+ rake (0.8.7)
30
+ rspec (2.0.1)
31
+ rspec-core (~> 2.0.1)
32
+ rspec-expectations (~> 2.0.1)
33
+ rspec-mocks (~> 2.0.1)
34
+ rspec-core (2.0.1)
35
+ rspec-expectations (2.0.1)
36
+ diff-lcs (>= 1.1.2)
37
+ rspec-mocks (2.0.1)
38
+ rspec-core (~> 2.0.1)
39
+ rspec-expectations (~> 2.0.1)
40
+ tzinfo (0.3.23)
41
+ watchr (0.7)
42
+ will_paginate (3.0.pre2)
43
+
44
+ PLATFORMS
45
+ ruby
46
+
47
+ DEPENDENCIES
48
+ bson_ext
49
+ bundler (>= 1.0.0)
50
+ mocha
51
+ mongoid (~> 2.0.0.beta.18)
52
+ mongoid-eager-loading!
53
+ rspec (~> 2.0.0)
54
+ watchr
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Richard Huang (flyerhzm@gmail.com)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,35 @@
1
+ mongoid-eager-loading
2
+ =====================
3
+
4
+ mongoid-eager-loading adds the eager loading feature for mongoid.
5
+
6
+ Originally it is my [pull request][0] for mongoid, but it is not accepted yet, so I created this gem to get the eager loading benefits easily for my projects.
7
+
8
+ Usage
9
+ -----
10
+
11
+ define it in your Gemfile
12
+
13
+ gem "mongoid-eager-loading"
14
+
15
+ include the module after Mongoid::Document
16
+
17
+ class User
18
+ include Mongoid::Document
19
+ include Mongoid::EagerLoading
20
+ end
21
+
22
+ then you can use the eager loading like
23
+
24
+ Post.includes(:user)
25
+ Post.includes(:user, :comment)
26
+
27
+ Author
28
+ ------
29
+ Richard Huang :: flyerhzm@gmail.com :: @flyerhzm
30
+
31
+ Copyright
32
+ ---------
33
+ Copyright (c) 2010 Richard Huang. See LICENSE for details.
34
+
35
+ [0]: https://github.com/mongoid/mongoid/pull/391
@@ -0,0 +1,45 @@
1
+ require 'bundler'
2
+ Bundler.setup
3
+
4
+ require "rake"
5
+ require "rake/rdoctask"
6
+ require "rspec"
7
+ require "rspec/core/rake_task"
8
+
9
+ $LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
10
+
11
+ task :build do
12
+ system "gem build mongoid-eager-loading.gemspec"
13
+ end
14
+
15
+ task :install => :build do
16
+ system "sudo gem install mongoid-eager-loading-#{Mongoid::EagerLoading::VERSION}.gem"
17
+ end
18
+
19
+ task :release => :build do
20
+ puts "Tagging #{Mongoid::EagerLoading::VERSION}..."
21
+ system "git tag -a #{Mongoid::EagerLoading::VERSION} -m 'Tagging #{Mongoid::EagerLoading::VERSION}'"
22
+ puts "Pushing to Github..."
23
+ system "git push --tags"
24
+ puts "Pushing to rubygems.org..."
25
+ system "gem push mongoid-eager-loading-#{Mongoid::EagerLoading::VERSION}.gem"
26
+ end
27
+
28
+ Rspec::Core::RakeTask.new(:spec) do |spec|
29
+ spec.pattern = "spec/**/*_spec.rb"
30
+ end
31
+
32
+ Rspec::Core::RakeTask.new('spec:progress') do |spec|
33
+ spec.rspec_opts = %w(--format progress)
34
+ spec.pattern = "spec/**/*_spec.rb"
35
+ end
36
+
37
+ Rake::RDocTask.new do |rdoc|
38
+ rdoc.rdoc_dir = "rdoc"
39
+ rdoc.title = "mongoid-eager-loading #{Mongoid::EagerLoading::VERSION}"
40
+ rdoc.rdoc_files.include("README*")
41
+ rdoc.rdoc_files.include("lib/**/*.rb")
42
+ end
43
+
44
+ task :default => :spec
45
+
@@ -0,0 +1,8 @@
1
+ require 'mongoid-eager-loading/mongoid/criterion/eager_loading'
2
+ require 'mongoid-eager-loading/mongoid/criteria'
3
+ require 'mongoid-eager-loading/mongoid/finders'
4
+
5
+ module Mongoid
6
+ module EagerLoading
7
+ end
8
+ end
@@ -0,0 +1,18 @@
1
+ Mongoid::Criteria.class_eval do
2
+ include Mongoid::Criterion::EagerLoading
3
+
4
+ alias_method :origin_each, :each
5
+
6
+ def each(&block)
7
+ if @eager_loadings
8
+ # if eager loadings are used, preload the associations.
9
+ docs = []
10
+ context.iterate { |doc| docs << doc }
11
+ preload(docs)
12
+ docs.each(&block)
13
+ self
14
+ else
15
+ origin_each(&block)
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,116 @@
1
+ module Mongoid
2
+ module Criterion
3
+ module EagerLoading
4
+ # EagerLoading criterion are used when eager loading the associations.
5
+ #
6
+ # Example:
7
+ #
8
+ # <tt>criteria.includes(:user)</tt>
9
+ #
10
+ # <tt>criteria.includes(:user, :post)</tt>
11
+ #
12
+ # Returns: <tt>self</tt>
13
+ attr_accessor :eager_loadings, :id_document_map, :id_associations_map
14
+
15
+ def includes(*options)
16
+ @eager_loadings = options
17
+ self
18
+ end
19
+
20
+ def preload(documents)
21
+ document_class = documents.first.class
22
+ @eager_loadings.each do |eager_loading|
23
+ setup_associations(documents, association_reflection(document_class, eager_loading))
24
+ end
25
+ end
26
+
27
+ private
28
+ def ignore_includes
29
+ @eager_loadings = nil
30
+ end
31
+
32
+ def association_reflection(document_class, eager_loading)
33
+ document_class.reflect_on_association(eager_loading)
34
+ end
35
+
36
+ def setup_associations(documents, reflection)
37
+ if reflection.association == Mongoid::Associations::ReferencesOne
38
+ setup_associations_with_ids(documents, reflection, true)
39
+ elsif reflection.association == Mongoid::Associations::ReferencesMany
40
+ setup_associations_with_ids(documents, reflection, false)
41
+ elsif reflection.association == Mongoid::Associations::ReferencesManyAsArray
42
+ setup_associations_with_foreign_keys(documents, reflection, false)
43
+ elsif reflection.association == Mongoid::Associations::ReferencedIn
44
+ setup_associations_with_foreign_keys(documents, reflection, true)
45
+ end
46
+ end
47
+
48
+ def setup_associations_with_ids(documents, reflection, one=true)
49
+ ids = []
50
+ documents.each do |document|
51
+ id_document_map[document.id] = document
52
+ ids << document.id if document.id
53
+ end
54
+
55
+ association_class = reflection.name.singularize.camelize.constantize
56
+ ignore_includes
57
+ eager_associations = association_class.where(reflection.foreign_key.to_sym.in => ids).to_a
58
+ eager_associations.each do |eager_association|
59
+ add_id_association(eager_association.send(reflection.foreign_key), eager_association)
60
+ end
61
+
62
+ id_document_map.each do |id, document|
63
+ document.send("#{reflection.name}=", one ? id_associations_map[id].first : id_associations_map[id])
64
+ end
65
+ end
66
+
67
+ def setup_associations_with_foreign_keys(documents, reflection, one)
68
+ ids = []
69
+ foreign_key_name = reflection.foreign_key
70
+ documents.each do |document|
71
+ foreign_key_value = document.send(foreign_key_name)
72
+ if one
73
+ id_document_map[foreign_key_value] = document
74
+ ids << foreign_key_value if foreign_key_value
75
+ elsif foreign_key_value
76
+ foreign_key_value.each do |fkv|
77
+ id_document_map[fkv] = document
78
+ ids << fkv if fkv
79
+ end
80
+ end
81
+ end
82
+
83
+ association_class = reflection.name.singularize.camelize.constantize
84
+ ignore_includes
85
+ eager_associations = association_class.find(ids).to_a
86
+ eager_associations.each do |eager_association|
87
+ add_id_association(eager_association.id, eager_association)
88
+ end
89
+
90
+ id_document_map.each do |id, document|
91
+ foreign_key_value = document.send(foreign_key_name)
92
+ associations = \
93
+ if one
94
+ id_associations_map[foreign_key_value].first
95
+ else
96
+ foreign_key_value.collect { |fkv| id_associations_map[fkv] }.flatten.uniq
97
+ end
98
+ document.send("#{reflection.name}=", associations)
99
+ end
100
+ end
101
+
102
+ def id_document_map
103
+ @id_doccument_map ||= {}
104
+ end
105
+
106
+ def id_associations_map
107
+ @id_associations_map ||= {}
108
+ end
109
+
110
+ def add_id_association(id, association)
111
+ id_associations_map[id] ||= []
112
+ id_associations_map[id] << association
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,5 @@
1
+ Mongoid::Finders.class_eval do
2
+ def includes(*args)
3
+ criteria.send(:includes, *args)
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ module Mongoid
2
+ module EagerLoading
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path("../lib/mongoid-eager-loading/version", __FILE__)
3
+
4
+ Gem::Specification.new do |s|
5
+ s.name = "mongoid-eager-loading"
6
+ s.version = Mongoid::EagerLoading::VERSION
7
+ s.platform = Gem::Platform::RUBY
8
+ s.authors = ["Richard Huang"]
9
+ s.email = ["flyerhzm@gmail.com"]
10
+ s.homepage = "http://rubygems.org/gems/mongoid-eager-loading"
11
+ s.summary = "eager loading for mongoid"
12
+ s.description = "eager loading for mongoid"
13
+
14
+ s.required_rubygems_version = ">= 1.3.6"
15
+ s.rubyforge_project = "mongoid-eager-loading"
16
+
17
+ s.add_dependency "mongoid", "~> 2.0.0.beta.18"
18
+
19
+ s.add_development_dependency "bundler", ">= 1.0.0"
20
+ s.add_development_dependency "rspec", "~> 2.0.0"
21
+ s.add_development_dependency "mocha"
22
+ s.add_development_dependency "watchr"
23
+ s.add_development_dependency "bson_ext"
24
+
25
+ s.files = `git ls-files`.split("\n")
26
+ s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
27
+ s.require_path = 'lib'
28
+ end
@@ -0,0 +1,21 @@
1
+ defaults: &defaults
2
+ host: localhost
3
+ slaves:
4
+ # - host: localhost
5
+ # port: 27018
6
+ # - host: localhost
7
+ # port: 27019
8
+ allow_dynamic_fields: false
9
+ include_root_in_json: true
10
+ parameterize_keys: false
11
+ persist_in_safe_mode: false
12
+ raise_not_found_error: false
13
+ reconnect_time: 5
14
+ autocreate_indexes: false
15
+ persist_types: false
16
+ option_no_exist: false
17
+ skip_version_check: false
18
+
19
+ test:
20
+ <<: *defaults
21
+ database: mongoid_config_test
@@ -0,0 +1,10 @@
1
+ class Account
2
+ include Mongoid::Document
3
+ referenced_in :creator, :class_name => "User", :foreign_key => :creator_id
4
+ referenced_in :person
5
+ field :number
6
+ field :balance
7
+ field :nickname
8
+
9
+ attr_accessible :nickname
10
+ end
@@ -0,0 +1,52 @@
1
+ class Address
2
+ include Mongoid::Document
3
+
4
+ attr_accessor :mode
5
+
6
+ field :address_type
7
+ field :number, :type => Integer
8
+ field :street
9
+ field :city
10
+ field :state
11
+ field :post_code
12
+ field :parent_title
13
+ field :services, :type => Array
14
+ field :latlng, :type => Array
15
+ key :street
16
+ embeds_many :locations
17
+
18
+ embedded_in :addressable, :inverse_of => :addresses do
19
+ def extension
20
+ "Testing"
21
+ end
22
+ def doctor?
23
+ title == "Dr"
24
+ end
25
+ end
26
+
27
+ named_scope :rodeo, where(:street => "Rodeo Dr") do
28
+ def mansion?
29
+ all? { |address| address.street == "Rodeo Dr" }
30
+ end
31
+ end
32
+
33
+ validates_presence_of :street, :on => :update
34
+
35
+ def set_parent=(set = false)
36
+ self.parent_title = addressable.title if set
37
+ end
38
+
39
+ class << self
40
+ def california
41
+ where(:state => "CA")
42
+ end
43
+
44
+ def homes
45
+ where(:address_type => "Home")
46
+ end
47
+
48
+ def streets
49
+ all.map(&:street)
50
+ end
51
+ end
52
+ end