jnunemaker-mongomapper 0.2.0 → 0.3.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 (55) hide show
  1. data/.gitignore +1 -0
  2. data/History +17 -0
  3. data/README.rdoc +6 -3
  4. data/Rakefile +3 -2
  5. data/VERSION +1 -1
  6. data/bin/mmconsole +56 -0
  7. data/lib/mongomapper.rb +48 -17
  8. data/lib/mongomapper/associations.rb +31 -39
  9. data/lib/mongomapper/associations/base.rb +40 -22
  10. data/lib/mongomapper/associations/belongs_to_polymorphic_proxy.rb +33 -0
  11. data/lib/mongomapper/associations/belongs_to_proxy.rb +10 -14
  12. data/lib/mongomapper/associations/many_embedded_polymorphic_proxy.rb +34 -0
  13. data/lib/mongomapper/associations/{has_many_embedded_proxy.rb → many_embedded_proxy.rb} +5 -5
  14. data/lib/mongomapper/associations/many_proxy.rb +55 -0
  15. data/lib/mongomapper/associations/proxy.rb +21 -14
  16. data/lib/mongomapper/callbacks.rb +1 -1
  17. data/lib/mongomapper/document.rb +82 -59
  18. data/lib/mongomapper/embedded_document.rb +121 -130
  19. data/lib/mongomapper/finder_options.rb +21 -6
  20. data/lib/mongomapper/key.rb +5 -7
  21. data/lib/mongomapper/observing.rb +1 -41
  22. data/lib/mongomapper/pagination.rb +52 -0
  23. data/lib/mongomapper/rails_compatibility/document.rb +15 -0
  24. data/lib/mongomapper/rails_compatibility/embedded_document.rb +25 -0
  25. data/lib/mongomapper/serialization.rb +1 -1
  26. data/mongomapper.gemspec +62 -36
  27. data/test/NOTE_ON_TESTING +1 -0
  28. data/test/functional/test_associations.rb +485 -0
  29. data/test/{test_callbacks.rb → functional/test_callbacks.rb} +2 -1
  30. data/test/functional/test_document.rb +636 -0
  31. data/test/functional/test_pagination.rb +82 -0
  32. data/test/functional/test_rails_compatibility.rb +31 -0
  33. data/test/functional/test_validations.rb +172 -0
  34. data/test/models.rb +92 -0
  35. data/test/test_helper.rb +5 -0
  36. data/test/{serializers → unit/serializers}/test_json_serializer.rb +0 -0
  37. data/test/unit/test_association_base.rb +131 -0
  38. data/test/unit/test_document.rb +115 -0
  39. data/test/{test_embedded_document.rb → unit/test_embedded_document.rb} +158 -66
  40. data/test/{test_finder_options.rb → unit/test_finder_options.rb} +66 -0
  41. data/test/{test_key.rb → unit/test_key.rb} +13 -1
  42. data/test/unit/test_mongo_id.rb +35 -0
  43. data/test/{test_mongomapper.rb → unit/test_mongomapper.rb} +0 -0
  44. data/test/{test_observing.rb → unit/test_observing.rb} +0 -0
  45. data/test/unit/test_pagination.rb +113 -0
  46. data/test/unit/test_rails_compatibility.rb +34 -0
  47. data/test/{test_serializations.rb → unit/test_serializations.rb} +0 -2
  48. data/test/{test_validations.rb → unit/test_validations.rb} +0 -134
  49. metadata +68 -36
  50. data/lib/mongomapper/associations/has_many_proxy.rb +0 -28
  51. data/lib/mongomapper/associations/polymorphic_belongs_to_proxy.rb +0 -31
  52. data/lib/mongomapper/rails_compatibility.rb +0 -23
  53. data/test/test_associations.rb +0 -149
  54. data/test/test_document.rb +0 -944
  55. data/test/test_rails_compatibility.rb +0 -29
@@ -0,0 +1,82 @@
1
+ require 'test_helper'
2
+
3
+ class PaginationTest < Test::Unit::TestCase
4
+ context "Paginating" do
5
+ setup do
6
+ @document = Class.new do
7
+ include MongoMapper::Document
8
+ collection 'users'
9
+
10
+ key :first_name, String
11
+ key :last_name, String
12
+ key :age, Integer
13
+ end
14
+
15
+ @document.collection.clear
16
+
17
+ @doc1 = @document.create({:first_name => 'John', :last_name => 'Nunemaker', :age => '27'})
18
+ @doc2 = @document.create({:first_name => 'Steve', :last_name => 'Smith', :age => '28'})
19
+ @doc3 = @document.create({:first_name => 'Steph', :last_name => 'Nunemaker', :age => '26'})
20
+ end
21
+
22
+ should "return the total pages" do
23
+ result = @document.paginate(:per_page => 2, :page => 1)
24
+ result.total_pages.should == 2
25
+ end
26
+
27
+ should "return the total of records" do
28
+ result = @document.paginate(:per_page => 2, :page => 1)
29
+ result.total_entries.should == 3
30
+ end
31
+
32
+ should "return the items" do
33
+ result = @document.paginate(:per_page => 2, :page => 1)
34
+ result.size.should == 2
35
+ result.subject.should == [@doc1, @doc2]
36
+ result.should == [@doc1, @doc2]
37
+ end
38
+
39
+ should "accept conditions" do
40
+ result = @document.paginate({
41
+ :conditions => {:last_name => 'Nunemaker'},
42
+ :order => "age DESC",
43
+ :per_page => 2,
44
+ :page => 1,
45
+ })
46
+ result.should == [@doc1, @doc3]
47
+ result.first.age.should == 27
48
+ end
49
+
50
+ should "withstand rigor" do
51
+ result = @document.paginate({
52
+ :per_page => 1,
53
+ :page => 1,
54
+ :order => 'age desc',
55
+ :conditions => {:last_name => 'Nunemaker'}
56
+ })
57
+ result.should == [@doc1]
58
+ result.total_entries.should == 2
59
+ result.total_pages.should == 2
60
+
61
+ result = @document.paginate({
62
+ :per_page => 1,
63
+ :page => 2,
64
+ :order => 'age desc',
65
+ :conditions => {:last_name => 'Nunemaker'}
66
+ })
67
+ result.should == [@doc3]
68
+ result.total_entries.should == 2
69
+ result.total_pages.should == 2
70
+
71
+ result = @document.paginate({
72
+ :per_page => 2,
73
+ :page => 1,
74
+ :order => 'age desc',
75
+ :conditions => {:last_name => 'Nunemaker'}
76
+ })
77
+ result.should == [@doc1, @doc3]
78
+ result.total_entries.should == 2
79
+ result.total_pages.should == 1
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,31 @@
1
+ require 'test_helper'
2
+
3
+ class TestRailsCompatibility < Test::Unit::TestCase
4
+ class Item
5
+ include MongoMapper::EmbeddedDocument
6
+ key :for_all, String
7
+ end
8
+
9
+ class Order
10
+ include MongoMapper::Document
11
+ many :items, :class_name => 'TestRailsCompatibility::Item'
12
+ key :order_only, String
13
+ end
14
+
15
+ context "Document" do
16
+ setup do
17
+ Order.collection.clear
18
+ end
19
+
20
+ should "have to_param that returns id" do
21
+ id = MongoID.new
22
+ instance = Order.create('_id' => id.to_s)
23
+ instance.to_param.should == id.to_s
24
+ end
25
+
26
+ should "alias new to new_record?" do
27
+ instance = Order.new
28
+ instance.new_record?.should == instance.new?
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,172 @@
1
+ require 'test_helper'
2
+
3
+ class ValidationsTest < Test::Unit::TestCase
4
+ context "Saving a new document that is invalid" do
5
+ setup do
6
+ @document = Class.new do
7
+ include MongoMapper::Document
8
+ key :name, String, :required => true
9
+ end
10
+ @document.collection.clear
11
+ end
12
+
13
+ should "not insert document" do
14
+ doc = @document.new
15
+ doc.save
16
+ @document.count.should == 0
17
+ end
18
+
19
+ should "populate document's errors" do
20
+ doc = @document.new
21
+ doc.errors.size.should == 0
22
+ doc.save
23
+ doc.errors.full_messages.should == ["Name can't be empty"]
24
+ end
25
+ end
26
+
27
+ context "Saving a document that is invalid (destructive)" do
28
+ setup do
29
+ @document = Class.new do
30
+ include MongoMapper::Document
31
+ key :name, String, :required => true
32
+ end
33
+ @document.collection.clear
34
+ end
35
+
36
+ should "raise error" do
37
+ doc = @document.new
38
+ lambda { doc.save! }.should raise_error(MongoMapper::DocumentNotValid)
39
+ end
40
+ end
41
+
42
+ context "Saving an existing document that is invalid" do
43
+ setup do
44
+ @document = Class.new do
45
+ include MongoMapper::Document
46
+ key :name, String, :required => true
47
+ end
48
+ @document.collection.clear
49
+
50
+ @doc = @document.create(:name => 'John Nunemaker')
51
+ end
52
+
53
+ should "not update document" do
54
+ @doc.name = nil
55
+ @doc.save
56
+ @document.find(@doc.id).name.should == 'John Nunemaker'
57
+ end
58
+
59
+ should "populate document's errors" do
60
+ @doc.name = nil
61
+ @doc.save
62
+ @doc.errors.full_messages.should == ["Name can't be empty"]
63
+ end
64
+ end
65
+
66
+ context "Adding validation errors" do
67
+ setup do
68
+ @document = Class.new do
69
+ include MongoMapper::Document
70
+ key :action, String
71
+ def action_present
72
+ errors.add(:action, 'is invalid') if action.blank?
73
+ end
74
+ end
75
+ @document.collection.clear
76
+ end
77
+
78
+ should "work with validate_on_create callback" do
79
+ @document.validate_on_create :action_present
80
+
81
+ doc = @document.new
82
+ doc.action = nil
83
+ doc.should have_error_on(:action)
84
+
85
+ doc.action = 'kick'
86
+ doc.should_not have_error_on(:action)
87
+ doc.save
88
+
89
+ doc.action = nil
90
+ doc.should_not have_error_on(:action)
91
+ end
92
+
93
+ should "work with validate_on_update callback" do
94
+ @document.validate_on_update :action_present
95
+
96
+ doc = @document.new
97
+ doc.action = nil
98
+ doc.should_not have_error_on(:action)
99
+ doc.save
100
+
101
+ doc.action = nil
102
+ doc.should have_error_on(:action)
103
+
104
+ doc.action = 'kick'
105
+ doc.should_not have_error_on(:action)
106
+ end
107
+ end
108
+
109
+ context "validating uniqueness of" do
110
+ setup do
111
+ @document = Class.new do
112
+ include MongoMapper::Document
113
+ key :name, String
114
+ validates_uniqueness_of :name
115
+ end
116
+ @document.collection.clear
117
+ end
118
+
119
+ should "not fail if object is new" do
120
+ doc = @document.new
121
+ doc.should_not have_error_on(:name)
122
+ end
123
+
124
+ should "allow to update an object" do
125
+ doc = @document.new("name" => "joe")
126
+ doc.save.should be_true
127
+
128
+ @document \
129
+ .stubs(:find) \
130
+ .with(:first, :conditions => {:name => 'joe'}, :limit => 1) \
131
+ .returns(doc)
132
+
133
+ doc.name = "joe"
134
+ doc.valid?.should be_true
135
+ doc.should_not have_error_on(:name)
136
+ end
137
+
138
+ should "fail if object name is not unique" do
139
+ doc = @document.new("name" => "joe")
140
+ doc.save.should be_true
141
+
142
+ @document \
143
+ .stubs(:find) \
144
+ .with(:first, :conditions => {:name => 'joe'}, :limit => 1) \
145
+ .returns(doc)
146
+
147
+ doc2 = @document.new("name" => "joe")
148
+ doc2.should have_error_on(:name)
149
+ end
150
+ end
151
+
152
+ context "validates uniqueness of with :unique shortcut" do
153
+ should "work" do
154
+ @document = Class.new do
155
+ include MongoMapper::Document
156
+ key :name, String, :unique => true
157
+ end
158
+ @document.collection.clear
159
+
160
+ doc = @document.create(:name => 'John')
161
+ doc.should_not have_error_on(:name)
162
+
163
+ @document \
164
+ .stubs(:find) \
165
+ .with(:first, :conditions => {:name => 'John'}, :limit => 1) \
166
+ .returns(doc)
167
+
168
+ second_john = @document.create(:name => 'John')
169
+ second_john.should have_error_on(:name, 'has already been taken')
170
+ end
171
+ end
172
+ end
data/test/models.rb ADDED
@@ -0,0 +1,92 @@
1
+ class Address
2
+ include MongoMapper::EmbeddedDocument
3
+ key :address, String
4
+ key :city, String
5
+ key :state, String
6
+ key :zip, Integer
7
+ end
8
+
9
+ class Project
10
+ include MongoMapper::Document
11
+ key :name, String
12
+ many :statuses
13
+ many :addresses
14
+ end
15
+
16
+ class Status
17
+ include MongoMapper::Document
18
+ belongs_to :project
19
+ belongs_to :target, :polymorphic => true
20
+ key :name, String
21
+ end
22
+
23
+ class RealPerson
24
+ include MongoMapper::Document
25
+ many :pets
26
+ key :name, String
27
+ end
28
+
29
+ class Person
30
+ include MongoMapper::EmbeddedDocument
31
+ key :name, String
32
+ key :child, Person
33
+ many :pets
34
+ end
35
+
36
+ class Pet
37
+ include MongoMapper::EmbeddedDocument
38
+ key :name, String
39
+ key :species, String
40
+ end
41
+
42
+ class Media
43
+ include MongoMapper::EmbeddedDocument
44
+ key :file, String
45
+ end
46
+
47
+ class Video < Media
48
+ key :length, Integer
49
+ end
50
+
51
+ class Image < Media
52
+ key :width, Integer
53
+ key :height, Integer
54
+ end
55
+
56
+ class Music < Media
57
+ key :bitrate, String
58
+ end
59
+
60
+ class Catalog
61
+ include MongoMapper::Document
62
+ many :medias, :polymorphic => true
63
+ end
64
+
65
+ module TrModels
66
+ class Transport
67
+ include MongoMapper::EmbeddedDocument
68
+ key :license_plate, String
69
+ end
70
+
71
+ class Car < TrModels::Transport
72
+ include MongoMapper::EmbeddedDocument
73
+ key :model, String
74
+ key :year, Integer
75
+ end
76
+
77
+ class Bus < TrModels::Transport
78
+ include MongoMapper::EmbeddedDocument
79
+ key :max_passengers, Integer
80
+ end
81
+
82
+ class Ambulance < TrModels::Transport
83
+ include MongoMapper::EmbeddedDocument
84
+ key :icu, Boolean
85
+ end
86
+
87
+ class Fleet
88
+ include MongoMapper::Document
89
+ many :transports, :polymorphic => true, :class_name => "TrModels::Transport"
90
+ key :name, String
91
+ end
92
+ end
data/test/test_helper.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require 'pathname'
2
+ require 'pp'
2
3
  require 'rubygems'
3
4
  require 'test/unit'
4
5
  require 'shoulda'
@@ -14,6 +15,10 @@ dir = (Pathname(__FILE__).dirname + '..' + 'lib').expand_path
14
15
  require dir + 'mongomapper'
15
16
 
16
17
  class Test::Unit::TestCase
18
+ def clear_all_collections
19
+ MongoMapper::Document.descendants.map(&:delete_all)
20
+ end
21
+
17
22
  custom_matcher :be_nil do |receiver, matcher, args|
18
23
  matcher.positive_failure_message = "Expected #{receiver} to be nil but it wasn't"
19
24
  matcher.negative_failure_message = "Expected #{receiver} not to be nil but it was"
@@ -0,0 +1,131 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class FooMonster; end
5
+
6
+ class AssociationBaseTest < Test::Unit::TestCase
7
+ include MongoMapper::Associations
8
+
9
+ should "initialize with type and name" do
10
+ base = Base.new(:many, :foos)
11
+ base.type.should == :many
12
+ base.name.should == :foos
13
+ end
14
+
15
+ should "also allow options when initializing" do
16
+ base = Base.new(:many, :foos, :polymorphic => true)
17
+ base.options[:polymorphic].should be_true
18
+ end
19
+
20
+ context "class_name" do
21
+ should "work for belongs_to" do
22
+ Base.new(:belongs_to, :user).class_name.should == 'User'
23
+ end
24
+
25
+ should "work for many" do
26
+ Base.new(:many, :smart_people).class_name.should == 'SmartPerson'
27
+ end
28
+
29
+ should "be changeable using class_name option" do
30
+ base = Base.new(:many, :smart_people, :class_name => 'IntelligentPerson')
31
+ base.class_name.should == 'IntelligentPerson'
32
+ end
33
+ end
34
+
35
+ context "klass" do
36
+ should "be class_name constantized" do
37
+ Base.new(:belongs_to, :foo_monster).klass.should == FooMonster
38
+ end
39
+ end
40
+
41
+ context "many?" do
42
+ should "be true if many" do
43
+ Base.new(:many, :foos).many?.should be_true
44
+ end
45
+
46
+ should "be false if not many" do
47
+ Base.new(:belongs_to, :foo).many?.should be_false
48
+ end
49
+ end
50
+
51
+ context "belongs_to?" do
52
+ should "be true if belongs_to" do
53
+ Base.new(:belongs_to, :foo).belongs_to?.should be_true
54
+ end
55
+
56
+ should "be false if not belongs_to" do
57
+ Base.new(:many, :foos).belongs_to?.should be_false
58
+ end
59
+ end
60
+
61
+ context "polymorphic?" do
62
+ should "be true if polymorphic" do
63
+ Base.new(:many, :foos, :polymorphic => true).polymorphic?.should be_true
64
+ end
65
+
66
+ should "be false if not polymorphic" do
67
+ Base.new(:many, :bars).polymorphic?.should be_false
68
+ end
69
+ end
70
+
71
+ context "type_key_name" do
72
+ should "be _type for many" do
73
+ Base.new(:many, :foos).type_key_name.should == '_type'
74
+ end
75
+
76
+ should "be association name _ type for belongs_to" do
77
+ Base.new(:belongs_to, :foo).type_key_name.should == 'foo_type'
78
+ end
79
+ end
80
+
81
+ should "have belongs_to_key_name" do
82
+ Base.new(:belongs_to, :foo).belongs_to_key_name.should == 'foo_id'
83
+ end
84
+
85
+ should "have ivar that is association name" do
86
+ Base.new(:belongs_to, :foo).ivar.should == '@_foo'
87
+ end
88
+
89
+ context "embeddable?" do
90
+ should "be true if class is embeddable" do
91
+ base = Base.new(:many, :medias)
92
+ base.embeddable?.should be_true
93
+ end
94
+
95
+ should "be false if class is not embeddable" do
96
+ base = Base.new(:many, :statuses)
97
+ base.embeddable?.should be_false
98
+
99
+ base = Base.new(:belongs_to, :project)
100
+ base.embeddable?.should be_false
101
+ end
102
+ end
103
+
104
+ context "proxy_class" do
105
+ should "be ManyProxy for many" do
106
+ base = Base.new(:many, :statuses)
107
+ base.proxy_class.should == ManyProxy
108
+ end
109
+
110
+ should "be ManyEmbeddedProxy for many embedded" do
111
+ base = Base.new(:many, :medias)
112
+ base.proxy_class.should == ManyEmbeddedProxy
113
+ end
114
+
115
+ should "be ManyEmbeddedPolymorphicProxy for polymorphic many embedded" do
116
+ base = Base.new(:many, :medias, :polymorphic => true)
117
+ base.proxy_class.should == ManyEmbeddedPolymorphicProxy
118
+ end
119
+
120
+ should "be BelongsToProxy for belongs_to" do
121
+ base = Base.new(:belongs_to, :project)
122
+ base.proxy_class.should == BelongsToProxy
123
+ end
124
+
125
+ should "be BelongsToPolymorphicProxy for polymorphic belongs_to" do
126
+ base = Base.new(:belongs_to, :target, :polymorphic => true)
127
+ base.proxy_class.should == BelongsToPolymorphicProxy
128
+ end
129
+ end
130
+
131
+ end