mongo_mapper 0.5.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 (69) hide show
  1. data/.gitignore +7 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +39 -0
  4. data/Rakefile +87 -0
  5. data/VERSION +1 -0
  6. data/bin/mmconsole +55 -0
  7. data/lib/mongo_mapper.rb +92 -0
  8. data/lib/mongo_mapper/associations.rb +86 -0
  9. data/lib/mongo_mapper/associations/base.rb +83 -0
  10. data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +34 -0
  11. data/lib/mongo_mapper/associations/belongs_to_proxy.rb +22 -0
  12. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +27 -0
  13. data/lib/mongo_mapper/associations/many_documents_proxy.rb +116 -0
  14. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
  15. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +67 -0
  16. data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +11 -0
  17. data/lib/mongo_mapper/associations/many_proxy.rb +6 -0
  18. data/lib/mongo_mapper/associations/proxy.rb +64 -0
  19. data/lib/mongo_mapper/callbacks.rb +106 -0
  20. data/lib/mongo_mapper/document.rb +317 -0
  21. data/lib/mongo_mapper/dynamic_finder.rb +35 -0
  22. data/lib/mongo_mapper/embedded_document.rb +354 -0
  23. data/lib/mongo_mapper/finder_options.rb +94 -0
  24. data/lib/mongo_mapper/key.rb +32 -0
  25. data/lib/mongo_mapper/observing.rb +50 -0
  26. data/lib/mongo_mapper/pagination.rb +51 -0
  27. data/lib/mongo_mapper/rails_compatibility/document.rb +15 -0
  28. data/lib/mongo_mapper/rails_compatibility/embedded_document.rb +27 -0
  29. data/lib/mongo_mapper/save_with_validation.rb +19 -0
  30. data/lib/mongo_mapper/serialization.rb +55 -0
  31. data/lib/mongo_mapper/serializers/json_serializer.rb +92 -0
  32. data/lib/mongo_mapper/support.rb +157 -0
  33. data/lib/mongo_mapper/validations.rb +69 -0
  34. data/mongo_mapper.gemspec +156 -0
  35. data/test/NOTE_ON_TESTING +1 -0
  36. data/test/custom_matchers.rb +48 -0
  37. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +54 -0
  38. data/test/functional/associations/test_belongs_to_proxy.rb +46 -0
  39. data/test/functional/associations/test_many_documents_as_proxy.rb +244 -0
  40. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +132 -0
  41. data/test/functional/associations/test_many_embedded_proxy.rb +174 -0
  42. data/test/functional/associations/test_many_polymorphic_proxy.rb +297 -0
  43. data/test/functional/associations/test_many_proxy.rb +331 -0
  44. data/test/functional/test_associations.rb +48 -0
  45. data/test/functional/test_binary.rb +18 -0
  46. data/test/functional/test_callbacks.rb +85 -0
  47. data/test/functional/test_document.rb +951 -0
  48. data/test/functional/test_embedded_document.rb +97 -0
  49. data/test/functional/test_pagination.rb +87 -0
  50. data/test/functional/test_rails_compatibility.rb +30 -0
  51. data/test/functional/test_validations.rb +279 -0
  52. data/test/models.rb +169 -0
  53. data/test/test_helper.rb +29 -0
  54. data/test/unit/serializers/test_json_serializer.rb +189 -0
  55. data/test/unit/test_association_base.rb +144 -0
  56. data/test/unit/test_document.rb +165 -0
  57. data/test/unit/test_dynamic_finder.rb +125 -0
  58. data/test/unit/test_embedded_document.rb +645 -0
  59. data/test/unit/test_finder_options.rb +193 -0
  60. data/test/unit/test_key.rb +163 -0
  61. data/test/unit/test_mongomapper.rb +28 -0
  62. data/test/unit/test_observing.rb +101 -0
  63. data/test/unit/test_pagination.rb +109 -0
  64. data/test/unit/test_rails_compatibility.rb +39 -0
  65. data/test/unit/test_serializations.rb +52 -0
  66. data/test/unit/test_support.rb +272 -0
  67. data/test/unit/test_time_zones.rb +40 -0
  68. data/test/unit/test_validations.rb +503 -0
  69. metadata +204 -0
@@ -0,0 +1,69 @@
1
+ module MongoMapper
2
+ module Validations
3
+ module Macros
4
+ def validates_uniqueness_of(*args)
5
+ add_validations(args, MongoMapper::Validations::ValidatesUniquenessOf)
6
+ end
7
+
8
+ def validates_exclusion_of(*args)
9
+ add_validations(args, MongoMapper::Validations::ValidatesExclusionOf)
10
+ end
11
+
12
+ def validates_inclusion_of(*args)
13
+ add_validations(args, MongoMapper::Validations::ValidatesInclusionOf)
14
+ end
15
+ end
16
+
17
+ class ValidatesUniquenessOf < Validatable::ValidationBase
18
+ option :scope
19
+
20
+ def valid?(instance)
21
+ doc = instance.class.find(:first, :conditions => {self.attribute => instance[attribute]}.merge(scope_conditions(instance)), :limit => 1)
22
+ doc.nil? || instance.id == doc.id
23
+ end
24
+
25
+ def message(instance)
26
+ super || "has already been taken"
27
+ end
28
+
29
+ def scope_conditions(instance)
30
+ return {} unless scope
31
+ Array(scope).inject({}) do |conditions, key|
32
+ conditions.merge(key => instance[key])
33
+ end
34
+ end
35
+ end
36
+
37
+ class ValidatesExclusionOf < Validatable::ValidationBase
38
+ required_option :within
39
+
40
+ def valid?(instance)
41
+ value = instance[attribute]
42
+ return true if allow_nil && value.nil?
43
+ return true if allow_blank && value.blank?
44
+
45
+ !within.include?(instance[attribute])
46
+ end
47
+
48
+ def message(instance)
49
+ super || "is reserved"
50
+ end
51
+ end
52
+
53
+ class ValidatesInclusionOf < Validatable::ValidationBase
54
+ required_option :within
55
+
56
+ def valid?(instance)
57
+ value = instance[attribute]
58
+ return true if allow_nil && value.nil?
59
+ return true if allow_blank && value.blank?
60
+
61
+ within.include?(value)
62
+ end
63
+
64
+ def message(instance)
65
+ super || "is not in the list"
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,156 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{mongo_mapper}
8
+ s.version = "0.5.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["John Nunemaker"]
12
+ s.date = %q{2009-10-07}
13
+ s.default_executable = %q{mmconsole}
14
+ s.email = %q{nunemaker@gmail.com}
15
+ s.executables = ["mmconsole"]
16
+ s.extra_rdoc_files = [
17
+ "LICENSE",
18
+ "README.rdoc"
19
+ ]
20
+ s.files = [
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "bin/mmconsole",
27
+ "lib/mongo_mapper.rb",
28
+ "lib/mongo_mapper/associations.rb",
29
+ "lib/mongo_mapper/associations/base.rb",
30
+ "lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb",
31
+ "lib/mongo_mapper/associations/belongs_to_proxy.rb",
32
+ "lib/mongo_mapper/associations/many_documents_as_proxy.rb",
33
+ "lib/mongo_mapper/associations/many_documents_proxy.rb",
34
+ "lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb",
35
+ "lib/mongo_mapper/associations/many_embedded_proxy.rb",
36
+ "lib/mongo_mapper/associations/many_polymorphic_proxy.rb",
37
+ "lib/mongo_mapper/associations/many_proxy.rb",
38
+ "lib/mongo_mapper/associations/proxy.rb",
39
+ "lib/mongo_mapper/callbacks.rb",
40
+ "lib/mongo_mapper/document.rb",
41
+ "lib/mongo_mapper/dynamic_finder.rb",
42
+ "lib/mongo_mapper/embedded_document.rb",
43
+ "lib/mongo_mapper/finder_options.rb",
44
+ "lib/mongo_mapper/key.rb",
45
+ "lib/mongo_mapper/observing.rb",
46
+ "lib/mongo_mapper/pagination.rb",
47
+ "lib/mongo_mapper/rails_compatibility/document.rb",
48
+ "lib/mongo_mapper/rails_compatibility/embedded_document.rb",
49
+ "lib/mongo_mapper/save_with_validation.rb",
50
+ "lib/mongo_mapper/serialization.rb",
51
+ "lib/mongo_mapper/serializers/json_serializer.rb",
52
+ "lib/mongo_mapper/support.rb",
53
+ "lib/mongo_mapper/validations.rb",
54
+ "mongo_mapper.gemspec",
55
+ "test/NOTE_ON_TESTING",
56
+ "test/custom_matchers.rb",
57
+ "test/functional/associations/test_belongs_to_polymorphic_proxy.rb",
58
+ "test/functional/associations/test_belongs_to_proxy.rb",
59
+ "test/functional/associations/test_many_documents_as_proxy.rb",
60
+ "test/functional/associations/test_many_embedded_polymorphic_proxy.rb",
61
+ "test/functional/associations/test_many_embedded_proxy.rb",
62
+ "test/functional/associations/test_many_polymorphic_proxy.rb",
63
+ "test/functional/associations/test_many_proxy.rb",
64
+ "test/functional/test_associations.rb",
65
+ "test/functional/test_binary.rb",
66
+ "test/functional/test_callbacks.rb",
67
+ "test/functional/test_document.rb",
68
+ "test/functional/test_embedded_document.rb",
69
+ "test/functional/test_pagination.rb",
70
+ "test/functional/test_rails_compatibility.rb",
71
+ "test/functional/test_validations.rb",
72
+ "test/models.rb",
73
+ "test/test_helper.rb",
74
+ "test/unit/serializers/test_json_serializer.rb",
75
+ "test/unit/test_association_base.rb",
76
+ "test/unit/test_document.rb",
77
+ "test/unit/test_dynamic_finder.rb",
78
+ "test/unit/test_embedded_document.rb",
79
+ "test/unit/test_finder_options.rb",
80
+ "test/unit/test_key.rb",
81
+ "test/unit/test_mongomapper.rb",
82
+ "test/unit/test_observing.rb",
83
+ "test/unit/test_pagination.rb",
84
+ "test/unit/test_rails_compatibility.rb",
85
+ "test/unit/test_serializations.rb",
86
+ "test/unit/test_support.rb",
87
+ "test/unit/test_time_zones.rb",
88
+ "test/unit/test_validations.rb"
89
+ ]
90
+ s.homepage = %q{http://github.com/jnunemaker/mongomapper}
91
+ s.rdoc_options = ["--charset=UTF-8"]
92
+ s.require_paths = ["lib"]
93
+ s.rubyforge_project = %q{mongomapper}
94
+ s.rubygems_version = %q{1.3.5}
95
+ s.summary = %q{Awesome gem for modeling your domain and storing it in mongo}
96
+ s.test_files = [
97
+ "test/custom_matchers.rb",
98
+ "test/functional/associations/test_belongs_to_polymorphic_proxy.rb",
99
+ "test/functional/associations/test_belongs_to_proxy.rb",
100
+ "test/functional/associations/test_many_documents_as_proxy.rb",
101
+ "test/functional/associations/test_many_embedded_polymorphic_proxy.rb",
102
+ "test/functional/associations/test_many_embedded_proxy.rb",
103
+ "test/functional/associations/test_many_polymorphic_proxy.rb",
104
+ "test/functional/associations/test_many_proxy.rb",
105
+ "test/functional/test_associations.rb",
106
+ "test/functional/test_binary.rb",
107
+ "test/functional/test_callbacks.rb",
108
+ "test/functional/test_document.rb",
109
+ "test/functional/test_embedded_document.rb",
110
+ "test/functional/test_pagination.rb",
111
+ "test/functional/test_rails_compatibility.rb",
112
+ "test/functional/test_validations.rb",
113
+ "test/models.rb",
114
+ "test/test_helper.rb",
115
+ "test/unit/serializers/test_json_serializer.rb",
116
+ "test/unit/test_association_base.rb",
117
+ "test/unit/test_document.rb",
118
+ "test/unit/test_dynamic_finder.rb",
119
+ "test/unit/test_embedded_document.rb",
120
+ "test/unit/test_finder_options.rb",
121
+ "test/unit/test_key.rb",
122
+ "test/unit/test_mongomapper.rb",
123
+ "test/unit/test_observing.rb",
124
+ "test/unit/test_pagination.rb",
125
+ "test/unit/test_rails_compatibility.rb",
126
+ "test/unit/test_serializations.rb",
127
+ "test/unit/test_support.rb",
128
+ "test/unit/test_time_zones.rb",
129
+ "test/unit/test_validations.rb"
130
+ ]
131
+
132
+ if s.respond_to? :specification_version then
133
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
134
+ s.specification_version = 3
135
+
136
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
137
+ s.add_runtime_dependency(%q<activesupport>, [">= 0"])
138
+ s.add_runtime_dependency(%q<mongo>, ["= 0.15"])
139
+ s.add_runtime_dependency(%q<jnunemaker-validatable>, ["= 1.7.3"])
140
+ s.add_development_dependency(%q<mocha>, ["= 0.9.4"])
141
+ s.add_development_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
142
+ else
143
+ s.add_dependency(%q<activesupport>, [">= 0"])
144
+ s.add_dependency(%q<mongo>, ["= 0.15"])
145
+ s.add_dependency(%q<jnunemaker-validatable>, ["= 1.7.3"])
146
+ s.add_dependency(%q<mocha>, ["= 0.9.4"])
147
+ s.add_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
148
+ end
149
+ else
150
+ s.add_dependency(%q<activesupport>, [">= 0"])
151
+ s.add_dependency(%q<mongo>, ["= 0.15"])
152
+ s.add_dependency(%q<jnunemaker-validatable>, ["= 1.7.3"])
153
+ s.add_dependency(%q<mocha>, ["= 0.9.4"])
154
+ s.add_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
155
+ end
156
+ end
@@ -0,0 +1 @@
1
+ I am doing my best to keep unit and functional tests separate. As I see them, functional tests hit the database and should never care about internals. Unit tests do not hit the database.
@@ -0,0 +1,48 @@
1
+ module CustomMatchers
2
+ custom_matcher :be_nil do |receiver, matcher, args|
3
+ matcher.positive_failure_message = "Expected #{receiver} to be nil but it wasn't"
4
+ matcher.negative_failure_message = "Expected #{receiver} not to be nil but it was"
5
+ receiver.nil?
6
+ end
7
+
8
+ custom_matcher :be_blank do |receiver, matcher, args|
9
+ matcher.positive_failure_message = "Expected #{receiver} to be blank but it wasn't"
10
+ matcher.negative_failure_message = "Expected #{receiver} not to be blank but it was"
11
+ receiver.blank?
12
+ end
13
+
14
+ custom_matcher :be_true do |receiver, matcher, args|
15
+ matcher.positive_failure_message = "Expected #{receiver} to be true but it wasn't"
16
+ matcher.negative_failure_message = "Expected #{receiver} not to be true but it was"
17
+ receiver.eql?(true)
18
+ end
19
+
20
+ custom_matcher :be_false do |receiver, matcher, args|
21
+ matcher.positive_failure_message = "Expected #{receiver} to be false but it wasn't"
22
+ matcher.negative_failure_message = "Expected #{receiver} not to be false but it was"
23
+ receiver.eql?(false)
24
+ end
25
+
26
+ custom_matcher :be_valid do |receiver, matcher, args|
27
+ matcher.positive_failure_message = "Expected to be valid but it was invalid #{receiver.errors.inspect}"
28
+ matcher.negative_failure_message = "Expected to be invalid but it was valid #{receiver.errors.inspect}"
29
+ receiver.valid?
30
+ end
31
+
32
+ custom_matcher :have_error_on do |receiver, matcher, args|
33
+ receiver.valid?
34
+ attribute = args[0]
35
+ expected_message = args[1]
36
+
37
+ if expected_message.nil?
38
+ matcher.positive_failure_message = "#{receiver} had no errors on #{attribute}"
39
+ matcher.negative_failure_message = "#{receiver} had errors on #{attribute} #{receiver.errors.inspect}"
40
+ !receiver.errors.on(attribute).blank?
41
+ else
42
+ actual = receiver.errors.on(attribute)
43
+ matcher.positive_failure_message = %Q(Expected error on #{attribute} to be "#{expected_message}" but was "#{actual}")
44
+ matcher.negative_failure_message = %Q(Expected error on #{attribute} not to be "#{expected_message}" but was "#{actual}")
45
+ actual == expected_message
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,54 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class BelongsToPolymorphicProxyTest < Test::Unit::TestCase
5
+ def setup
6
+ Status.collection.clear
7
+ Project.collection.clear
8
+ end
9
+
10
+ should "default to nil" do
11
+ status = Status.new
12
+ status.target.should be_nil
13
+ end
14
+
15
+ should "be able to replace the association" do
16
+ status = Status.new
17
+ project = Project.new(:name => "mongomapper")
18
+ status.target = project
19
+ status.save.should be_true
20
+
21
+ from_db = Status.find(status.id)
22
+ from_db.target.should_not be_nil
23
+ from_db.target_id.should == project.id
24
+ from_db.target_type.should == "Project"
25
+ from_db.target.name.should == "mongomapper"
26
+ end
27
+
28
+ should "unset the association" do
29
+ status = Status.new
30
+ project = Project.new(:name => "mongomapper")
31
+ status.target = project
32
+ status.save.should be_true
33
+
34
+ from_db = Status.find(status.id)
35
+ from_db.target = nil
36
+ from_db.target_type.should be_nil
37
+ from_db.target_id.should be_nil
38
+ from_db.target.should be_nil
39
+ end
40
+
41
+ context "association id set but document not found" do
42
+ setup do
43
+ @status = Status.new
44
+ project = Project.new(:name => "mongomapper")
45
+ @status.target = project
46
+ @status.save.should be_true
47
+ project.destroy
48
+ end
49
+
50
+ should "return nil instead of raising error" do
51
+ @status.target.should be_nil
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,46 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class BelongsToProxyTest < Test::Unit::TestCase
5
+ def setup
6
+ Status.collection.clear
7
+ Project.collection.clear
8
+ end
9
+
10
+ should "default to nil" do
11
+ status = Status.new
12
+ status.project.should be_nil
13
+ end
14
+
15
+ should "be able to replace the association" do
16
+ status = Status.new
17
+ project = Project.new(:name => "mongomapper")
18
+ status.project = project
19
+ status.save.should be_true
20
+
21
+ from_db = Status.find(status.id)
22
+ from_db.project.should_not be_nil
23
+ from_db.project.name.should == "mongomapper"
24
+ end
25
+
26
+ should "unset the association" do
27
+ status = Status.new
28
+ project = Project.new(:name => "mongomapper")
29
+ status.project = project
30
+ status.save.should be_true
31
+
32
+ from_db = Status.find(status.id)
33
+ from_db.project = nil
34
+ from_db.project.should be_nil
35
+ end
36
+
37
+ context "association id set but document not found" do
38
+ setup do
39
+ @status = Status.new(:name => 'Foo', :project_id => '1234')
40
+ end
41
+
42
+ should "return nil instead of raising error" do
43
+ @status.project.should be_nil
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,244 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class ManyDocumentsAsProxyTest < Test::Unit::TestCase
5
+ def setup
6
+ Post.collection.clear
7
+ PostComment.collection.clear
8
+ end
9
+
10
+ should "default reader to empty array" do
11
+ Post.new.comments.should == []
12
+ end
13
+
14
+ should "add type and id key to polymorphic class base" do
15
+ PostComment.keys.keys.should include('commentable_type')
16
+ PostComment.keys.keys.should include('commentable_id')
17
+ end
18
+
19
+ should "allow adding to association like it was an array" do
20
+ post = Post.new
21
+ post.comments << PostComment.new(:body => 'foo bar')
22
+ post.comments << PostComment.new(:body => 'baz')
23
+ post.comments.concat PostComment.new(:body => 'baz')
24
+
25
+ post.comments.size.should == 3
26
+ end
27
+
28
+ should "be able to replace the association" do
29
+ post = Post.new
30
+
31
+ lambda {
32
+ post.comments = [
33
+ PostComment.new(:body => 'foo'),
34
+ PostComment.new(:body => 'bar'),
35
+ PostComment.new(:body => 'baz')
36
+ ]
37
+ }.should change { PostComment.count }.by(3)
38
+
39
+ from_db = Post.find(post.id)
40
+ from_db.comments.size.should == 3
41
+ from_db.comments[0].body.should == 'foo'
42
+ from_db.comments[1].body.should == 'bar'
43
+ from_db.comments[2].body.should == 'baz'
44
+ end
45
+
46
+ context "build" do
47
+ should "assign foreign key" do
48
+ post = Post.new
49
+ comment = post.comments.build
50
+ comment.commentable_id.should == post.id
51
+ end
52
+
53
+ should "assign _type" do
54
+ post = Post.new
55
+ comment = post.comments.build
56
+ comment.commentable_type.should == "Post"
57
+ end
58
+
59
+ should "allow assigning attributes" do
60
+ post = Post.new
61
+ comment = post.comments.build(:body => 'foo bar')
62
+ comment.body.should == 'foo bar'
63
+ end
64
+ end
65
+
66
+ context "create" do
67
+ should "assign foreign key" do
68
+ post = Post.new
69
+ comment = post.comments.create
70
+ comment.commentable_id.should == post.id
71
+ end
72
+
73
+ should "assign _type" do
74
+ post = Post.new
75
+ comment = post.comments.create
76
+ comment.commentable_type.should == "Post"
77
+ end
78
+
79
+ should "save record" do
80
+ post = Post.new
81
+ lambda {
82
+ post.comments.create(:body => 'baz')
83
+ }.should change { PostComment.count }
84
+ end
85
+
86
+ should "allow passing attributes" do
87
+ post = Post.create
88
+ comment = post.comments.create(:body => 'foo bar')
89
+ comment.body.should == 'foo bar'
90
+ end
91
+ end
92
+
93
+ context "count" do
94
+ should "work scoped to association" do
95
+ post = Post.create
96
+ 3.times { post.comments.create(:body => 'foo bar') }
97
+
98
+ other_post = Post.create
99
+ 2.times { other_post.comments.create(:body => 'baz') }
100
+
101
+ post.comments.count.should == 3
102
+ other_post.comments.count.should == 2
103
+ end
104
+
105
+ should "work with conditions" do
106
+ post = Post.create
107
+ post.comments.create(:body => 'foo bar')
108
+ post.comments.create(:body => 'baz')
109
+ post.comments.create(:body => 'foo bar')
110
+
111
+ post.comments.count(:body => 'foo bar').should == 2
112
+ end
113
+ end
114
+
115
+ context "Finding scoped to association" do
116
+ setup do
117
+ @post = Post.new
118
+
119
+ @comment1 = PostComment.create(:body => 'comment1', :name => 'John')
120
+ @comment2 = PostComment.create(:body => 'comment2', :name => 'Steve')
121
+ @comment3 = PostComment.create(:body => 'comment3', :name => 'John')
122
+ @post.comments = [@comment1, @comment2]
123
+ @post.save
124
+
125
+ @post2 = Post.create(:body => "post #2")
126
+ @comment4 = PostComment.create(:body => 'comment1', :name => 'Chas')
127
+ @comment5 = PostComment.create(:body => 'comment2', :name => 'Dan')
128
+ @comment6 = PostComment.create(:body => 'comment3', :name => 'Ed')
129
+ @post2.comments = [@comment4, @comment5, @comment6]
130
+ @post2.save
131
+ end
132
+
133
+ context "with :all" do
134
+ should "work" do
135
+ @post.comments.find(:all).should include(@comment1)
136
+ @post.comments.find(:all).should include(@comment2)
137
+ end
138
+
139
+ should "work with conditions" do
140
+ comments = @post.comments.find(:all, :conditions => {:body => 'comment1'})
141
+ comments.should == [@comment1]
142
+ end
143
+
144
+ should "work with order" do
145
+ comments = @post.comments.find(:all, :order => 'body desc')
146
+ comments.should == [@comment2, @comment1]
147
+ end
148
+ end
149
+
150
+ context "with #all" do
151
+ should "work" do
152
+ @post.comments.all.should == [@comment1, @comment2]
153
+ end
154
+
155
+ should "work with conditions" do
156
+ comments = @post.comments.all(:conditions => {:body => 'comment1'})
157
+ comments.should == [@comment1]
158
+ end
159
+
160
+ should "work with order" do
161
+ comments = @post.comments.all(:order => '$natural desc')
162
+ comments.should == [@comment2, @comment1]
163
+ end
164
+ end
165
+
166
+ context "with one id" do
167
+ should "work for id in association" do
168
+ @post.comments.find(@comment2.id).should == @comment2
169
+ end
170
+
171
+ should "not work for id not in association" do
172
+ lambda {
173
+ @post.comments.find(@comment5.id)
174
+ }.should raise_error(MongoMapper::DocumentNotFound)
175
+ end
176
+ end
177
+
178
+ context "with multiple ids" do
179
+ should "work for ids in association" do
180
+ posts = @post.comments.find(@comment1.id, @comment2.id)
181
+ posts.should == [@comment1, @comment2]
182
+ end
183
+
184
+ should "not work for ids not in association" do
185
+ lambda {
186
+ @post.comments.find(@comment1.id, @comment2.id, @comment4.id)
187
+ }.should raise_error(MongoMapper::DocumentNotFound)
188
+ end
189
+ end
190
+
191
+ context "dynamic finders" do
192
+ should "work with single key" do
193
+ @post.comments.find_by_body('comment1').should == @comment1
194
+ @post2.comments.find_by_body('comment1').should == @comment4
195
+ end
196
+
197
+ should "work with multiple keys" do
198
+ @post.comments.find_by_body_and_name('comment1', 'John').should == @comment1
199
+ @post.comments.find_by_body_and_name('comment1', 'Frank').should be_nil
200
+ end
201
+
202
+ should "raise error when using !" do
203
+ lambda {
204
+ @post.comments.find_by_body!('asdf')
205
+ }.should raise_error(MongoMapper::DocumentNotFound)
206
+ end
207
+
208
+ context "find_or_create_by" do
209
+ should "not create document if found" do
210
+ lambda {
211
+ comment = @post.comments.find_or_create_by_name('Steve')
212
+ comment.commentable.should == @post
213
+ comment.should == @comment2
214
+ }.should_not change { PostComment.count }
215
+ end
216
+
217
+ should "create document if not found" do
218
+ lambda {
219
+ @post.comments.find_or_create_by_name('Chas')
220
+ }.should change { PostComment.count }.by(1)
221
+ end
222
+ end
223
+ end
224
+
225
+ context "with #paginate" do
226
+ setup do
227
+ @comments = @post2.comments.paginate(:per_page => 2, :page => 1, :order => 'name')
228
+ end
229
+
230
+ should "return total pages" do
231
+ @comments.total_pages.should == 2
232
+ end
233
+
234
+ should "return total entries" do
235
+ @comments.total_entries.should == 3
236
+ end
237
+
238
+ should "return the subject" do
239
+ @comments.should include(@comment4)
240
+ @comments.should include(@comment5)
241
+ end
242
+ end
243
+ end
244
+ end