mongoid-eager-loading 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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