tpitale-mongo_mapper 0.6.9

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 (75) hide show
  1. data/.gitignore +10 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +53 -0
  4. data/Rakefile +55 -0
  5. data/VERSION +1 -0
  6. data/bin/mmconsole +60 -0
  7. data/lib/mongo_mapper/associations/base.rb +110 -0
  8. data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +26 -0
  9. data/lib/mongo_mapper/associations/belongs_to_proxy.rb +21 -0
  10. data/lib/mongo_mapper/associations/collection.rb +19 -0
  11. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +26 -0
  12. data/lib/mongo_mapper/associations/many_documents_proxy.rb +115 -0
  13. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +31 -0
  14. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +54 -0
  15. data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +11 -0
  16. data/lib/mongo_mapper/associations/proxy.rb +113 -0
  17. data/lib/mongo_mapper/associations.rb +70 -0
  18. data/lib/mongo_mapper/callbacks.rb +109 -0
  19. data/lib/mongo_mapper/dirty.rb +136 -0
  20. data/lib/mongo_mapper/document.rb +472 -0
  21. data/lib/mongo_mapper/dynamic_finder.rb +74 -0
  22. data/lib/mongo_mapper/embedded_document.rb +384 -0
  23. data/lib/mongo_mapper/finder_options.rb +133 -0
  24. data/lib/mongo_mapper/key.rb +36 -0
  25. data/lib/mongo_mapper/observing.rb +50 -0
  26. data/lib/mongo_mapper/pagination.rb +55 -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/serialization.rb +54 -0
  30. data/lib/mongo_mapper/serializers/json_serializer.rb +92 -0
  31. data/lib/mongo_mapper/support.rb +206 -0
  32. data/lib/mongo_mapper/validations.rb +41 -0
  33. data/lib/mongo_mapper.rb +120 -0
  34. data/mongo_mapper.gemspec +173 -0
  35. data/specs.watchr +32 -0
  36. data/test/NOTE_ON_TESTING +1 -0
  37. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +55 -0
  38. data/test/functional/associations/test_belongs_to_proxy.rb +48 -0
  39. data/test/functional/associations/test_many_documents_as_proxy.rb +246 -0
  40. data/test/functional/associations/test_many_documents_proxy.rb +387 -0
  41. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +156 -0
  42. data/test/functional/associations/test_many_embedded_proxy.rb +192 -0
  43. data/test/functional/associations/test_many_polymorphic_proxy.rb +339 -0
  44. data/test/functional/test_associations.rb +44 -0
  45. data/test/functional/test_binary.rb +18 -0
  46. data/test/functional/test_callbacks.rb +85 -0
  47. data/test/functional/test_dirty.rb +159 -0
  48. data/test/functional/test_document.rb +1235 -0
  49. data/test/functional/test_embedded_document.rb +135 -0
  50. data/test/functional/test_logger.rb +20 -0
  51. data/test/functional/test_pagination.rb +95 -0
  52. data/test/functional/test_rails_compatibility.rb +25 -0
  53. data/test/functional/test_string_id_compatibility.rb +72 -0
  54. data/test/functional/test_validations.rb +378 -0
  55. data/test/models.rb +271 -0
  56. data/test/support/custom_matchers.rb +55 -0
  57. data/test/support/timing.rb +16 -0
  58. data/test/test_helper.rb +27 -0
  59. data/test/unit/associations/test_base.rb +166 -0
  60. data/test/unit/associations/test_proxy.rb +91 -0
  61. data/test/unit/serializers/test_json_serializer.rb +189 -0
  62. data/test/unit/test_document.rb +204 -0
  63. data/test/unit/test_dynamic_finder.rb +125 -0
  64. data/test/unit/test_embedded_document.rb +718 -0
  65. data/test/unit/test_finder_options.rb +296 -0
  66. data/test/unit/test_key.rb +172 -0
  67. data/test/unit/test_mongo_mapper.rb +65 -0
  68. data/test/unit/test_observing.rb +101 -0
  69. data/test/unit/test_pagination.rb +113 -0
  70. data/test/unit/test_rails_compatibility.rb +49 -0
  71. data/test/unit/test_serializations.rb +52 -0
  72. data/test/unit/test_support.rb +342 -0
  73. data/test/unit/test_time_zones.rb +40 -0
  74. data/test/unit/test_validations.rb +503 -0
  75. metadata +235 -0
@@ -0,0 +1,27 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/../lib/mongo_mapper')
2
+
3
+ gem 'jnunemaker-matchy', '0.4.0'
4
+ gem 'shoulda', '2.10.2'
5
+ gem 'timecop', '0.3.1'
6
+ gem 'mocha', '0.9.8'
7
+
8
+ require 'matchy'
9
+ require 'shoulda'
10
+ require 'timecop'
11
+ require 'mocha'
12
+ require 'pp'
13
+
14
+ require 'support/custom_matchers'
15
+ require 'support/timing'
16
+
17
+ class Test::Unit::TestCase
18
+ include CustomMatchers
19
+ end
20
+
21
+ test_dir = File.expand_path(File.dirname(__FILE__) + '/../tmp')
22
+ FileUtils.mkdir_p(test_dir) unless File.exist?(test_dir)
23
+
24
+ MongoMapper.connection = Mongo::Connection.new('127.0.0.1', 27017, {
25
+ :logger => Logger.new(test_dir + '/test.log')
26
+ })
27
+ MongoMapper.database = 'test'
@@ -0,0 +1,166 @@
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 "default to class_name constantized" do
37
+ Base.new(:belongs_to, :foo_monster).klass.should == FooMonster
38
+ end
39
+
40
+ should "be the specified class" do
41
+ anonnymous_class = Class.new
42
+ Base.new(:belongs_to, :foo_monster, :class => anonnymous_class).klass.should == anonnymous_class
43
+ end
44
+ end
45
+
46
+ context "many?" do
47
+ should "be true if many" do
48
+ Base.new(:many, :foos).many?.should be_true
49
+ end
50
+
51
+ should "be false if not many" do
52
+ Base.new(:belongs_to, :foo).many?.should be_false
53
+ end
54
+ end
55
+
56
+ context "finder_options" do
57
+ should "default to empty hash" do
58
+ base = Base.new(:many, :foos)
59
+ base.finder_options.should == {}
60
+ end
61
+
62
+ should "work with order" do
63
+ base = Base.new(:many, :foos, :order => 'position')
64
+ base.finder_options.should == {:order => 'position'}
65
+ end
66
+
67
+ should "correctly parse from options" do
68
+ base = Base.new(:many, :foos, :order => 'position', :somekey => 'somevalue')
69
+ base.finder_options.should == {:order => 'position', :somekey => 'somevalue'}
70
+ end
71
+ end
72
+
73
+ context "belongs_to?" do
74
+ should "be true if belongs_to" do
75
+ Base.new(:belongs_to, :foo).belongs_to?.should be_true
76
+ end
77
+
78
+ should "be false if not belongs_to" do
79
+ Base.new(:many, :foos).belongs_to?.should be_false
80
+ end
81
+ end
82
+
83
+ context "polymorphic?" do
84
+ should "be true if polymorphic" do
85
+ Base.new(:many, :foos, :polymorphic => true).polymorphic?.should be_true
86
+ end
87
+
88
+ should "be false if not polymorphic" do
89
+ Base.new(:many, :bars).polymorphic?.should be_false
90
+ end
91
+ end
92
+
93
+ context "type_key_name" do
94
+ should "be _type for many" do
95
+ Base.new(:many, :foos).type_key_name.should == '_type'
96
+ end
97
+
98
+ should "be association name _ type for belongs_to" do
99
+ Base.new(:belongs_to, :foo).type_key_name.should == 'foo_type'
100
+ end
101
+ end
102
+
103
+ context "foreign_key" do
104
+ should "default to assocation_name_id" do
105
+ base = Base.new(:belongs_to, :foo)
106
+ base.foreign_key.should == 'foo_id'
107
+ end
108
+
109
+ should "be overridable with :foreign_key option" do
110
+ base = Base.new(:belongs_to, :foo, :foreign_key => 'foobar_id')
111
+ base.foreign_key.should == 'foobar_id'
112
+ end
113
+ end
114
+
115
+ should "have ivar that is association name" do
116
+ Base.new(:belongs_to, :foo).ivar.should == '@_foo'
117
+ end
118
+
119
+ context "embeddable?" do
120
+ should "be true if class is embeddable" do
121
+ base = Base.new(:many, :medias)
122
+ base.embeddable?.should be_true
123
+ end
124
+
125
+ should "be false if class is not embeddable" do
126
+ base = Base.new(:many, :statuses)
127
+ base.embeddable?.should be_false
128
+
129
+ base = Base.new(:belongs_to, :project)
130
+ base.embeddable?.should be_false
131
+ end
132
+ end
133
+
134
+ context "proxy_class" do
135
+ should "be ManyDocumentsProxy for many" do
136
+ base = Base.new(:many, :statuses)
137
+ base.proxy_class.should == ManyDocumentsProxy
138
+ end
139
+
140
+ should "be ManyPolymorphicProxy for polymorphic many" do
141
+ base = Base.new(:many, :messages, :polymorphic => true)
142
+ base.proxy_class.should == ManyPolymorphicProxy
143
+ end
144
+
145
+ should "be ManyEmbeddedProxy for many embedded" do
146
+ base = Base.new(:many, :medias)
147
+ base.proxy_class.should == ManyEmbeddedProxy
148
+ end
149
+
150
+ should "be ManyEmbeddedPolymorphicProxy for polymorphic many embedded" do
151
+ base = Base.new(:many, :medias, :polymorphic => true)
152
+ base.proxy_class.should == ManyEmbeddedPolymorphicProxy
153
+ end
154
+
155
+ should "be BelongsToProxy for belongs_to" do
156
+ base = Base.new(:belongs_to, :project)
157
+ base.proxy_class.should == BelongsToProxy
158
+ end
159
+
160
+ should "be BelongsToPolymorphicProxy for polymorphic belongs_to" do
161
+ base = Base.new(:belongs_to, :target, :polymorphic => true)
162
+ base.proxy_class.should == BelongsToPolymorphicProxy
163
+ end
164
+ end
165
+
166
+ end
@@ -0,0 +1,91 @@
1
+ require 'test_helper'
2
+
3
+ class FakeNilProxy < MongoMapper::Associations::Proxy
4
+ def find_target; nil end
5
+ end
6
+
7
+ class FakeBlankProxy < MongoMapper::Associations::Proxy
8
+ def find_target; '' end
9
+ end
10
+
11
+ class FakeProxy < MongoMapper::Associations::Proxy
12
+ def find_target
13
+ [1, 2]
14
+ end
15
+ end
16
+
17
+ class ProxyTest < Test::Unit::TestCase
18
+ def setup
19
+ @owner = mock('owner')
20
+ @owner.stubs(:new?).returns(false)
21
+ @association = mock('association')
22
+ @association.stubs(:options).returns({:extend => []})
23
+
24
+ @proxy = FakeProxy.new(@owner, @association)
25
+ @nil_proxy = FakeNilProxy.new(@owner, @association)
26
+ @blank_proxy = FakeBlankProxy.new(@owner, @association)
27
+ end
28
+
29
+ should 'return true for === target' do
30
+ @proxy = FakeProxy.new(@owner, @association)
31
+ @proxy.should === Array
32
+ end
33
+
34
+ should "set target to nil when reset is called" do
35
+ @proxy.reset
36
+ @proxy.target.should be_nil
37
+ end
38
+
39
+ should "be able to inspect the proxy" do
40
+ @proxy.inspect.should == '[1, 2]'
41
+ end
42
+
43
+ context "nil?" do
44
+ should "be true if nil" do
45
+ @nil_proxy.nil?.should be_true
46
+ end
47
+
48
+ should "be false if not nil" do
49
+ @proxy.nil?.should be_false
50
+ end
51
+ end
52
+
53
+ context "blank?" do
54
+ should "be true if blank" do
55
+ @blank_proxy.blank?.should be_true
56
+ end
57
+
58
+ should "be false if not blank" do
59
+ @proxy.blank?.should be_false
60
+ end
61
+ end
62
+
63
+ should "delegate respond_to? to target" do
64
+ @proxy.respond_to?(:each).should be_true
65
+ @proxy.respond_to?(:size).should be_true
66
+ @proxy.respond_to?(:gsub).should be_false
67
+ end
68
+
69
+ should "alias proxy owner to owner" do
70
+ @proxy.proxy_owner.should == @owner
71
+ end
72
+
73
+ should "alias proxy target to target" do
74
+ @proxy.proxy_target.should == @target
75
+ end
76
+
77
+ context "send" do
78
+ should "work if proxy responds to method" do
79
+ @proxy.send(:reset)
80
+ @proxy.target.should be_nil
81
+ end
82
+
83
+ should "work if the target responds to the method" do
84
+ @proxy.send(:size).should == 2
85
+ end
86
+
87
+ should "not work if neither the proxy or target respond to method" do
88
+ lambda { @proxy.send(:gsub) }.should raise_error
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,189 @@
1
+ require 'test_helper'
2
+
3
+ class JsonSerializationTest < Test::Unit::TestCase
4
+ class Tag
5
+ include MongoMapper::EmbeddedDocument
6
+ key :name, String
7
+ end
8
+
9
+ class Contact
10
+ include MongoMapper::Document
11
+ key :name, String
12
+ key :age, Integer
13
+ key :created_at, Time
14
+ key :awesome, Boolean
15
+ key :preferences, Hash
16
+
17
+ many :tags, :class_name => 'JsonSerializationTest::Tag'
18
+ end
19
+
20
+ def setup
21
+ Contact.include_root_in_json = false
22
+ @contact = Contact.new(
23
+ :name => 'Konata Izumi',
24
+ :age => 16,
25
+ :created_at => Time.utc(2006, 8, 1),
26
+ :awesome => true,
27
+ :preferences => { :shows => 'anime' }
28
+ )
29
+ end
30
+
31
+ should "include demodulized root" do
32
+ Contact.include_root_in_json = true
33
+ assert_match %r{^\{"contact": \{}, @contact.to_json
34
+ end
35
+
36
+ should "encode all encodable attributes" do
37
+ json = @contact.to_json
38
+
39
+ assert_no_match %r{"_id"}, json
40
+ assert_match %r{"name":"Konata Izumi"}, json
41
+ assert_match %r{"age":16}, json
42
+ assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
43
+ assert_match %r{"awesome":true}, json
44
+ assert_match %r{"preferences":\{"shows":"anime"\}}, json
45
+ end
46
+
47
+ should "allow attribute filtering with only" do
48
+ json = @contact.to_json(:only => [:name, :age])
49
+
50
+ assert_no_match %r{"_id"}, json
51
+ assert_match %r{"name":"Konata Izumi"}, json
52
+ assert_match %r{"age":16}, json
53
+ assert_no_match %r{"awesome"}, json
54
+ assert_no_match %r{"created_at"}, json
55
+ assert_no_match %r{"preferences"}, json
56
+ end
57
+
58
+ should "allow attribute filtering with except" do
59
+ json = @contact.to_json(:except => [:name, :age])
60
+
61
+ assert_no_match %r{"_id"}, json
62
+ assert_no_match %r{"name"}, json
63
+ assert_no_match %r{"age"}, json
64
+ assert_match %r{"awesome"}, json
65
+ assert_match %r{"created_at"}, json
66
+ assert_match %r{"preferences"}, json
67
+ end
68
+
69
+ context "_id key" do
70
+ should "not be included by default" do
71
+ json = @contact.to_json
72
+ assert_no_match %r{"_id":}, json
73
+ end
74
+
75
+ should "not be included even if :except is used" do
76
+ json = @contact.to_json(:except => :name)
77
+ assert_no_match %r{"_id":}, json
78
+ end
79
+ end
80
+
81
+ context "id method" do
82
+ setup do
83
+ def @contact.label; "Has cheezburger"; end
84
+ def @contact.favorite_quote; "Constraints are liberating"; end
85
+ end
86
+
87
+ should "be included by default" do
88
+ json = @contact.to_json
89
+ assert_match %r{"id"}, json
90
+ end
91
+
92
+ should "be included when single method included" do
93
+ json = @contact.to_json(:methods => :label)
94
+ assert_match %r{"id"}, json
95
+ assert_match %r{"label":"Has cheezburger"}, json
96
+ assert_match %r{"name":"Konata Izumi"}, json
97
+ assert_no_match %r{"favorite_quote":"Constraints are liberating"}, json
98
+ end
99
+
100
+ should "be included when multiple methods included" do
101
+ json = @contact.to_json(:methods => [:label, :favorite_quote])
102
+ assert_match %r{"id"}, json
103
+ assert_match %r{"label":"Has cheezburger"}, json
104
+ assert_match %r{"favorite_quote":"Constraints are liberating"}, json
105
+ assert_match %r{"name":"Konata Izumi"}, json
106
+ end
107
+
108
+ should "not be included if :only is present" do
109
+ json = @contact.to_json(:only => :name)
110
+ assert_no_match %r{"id":}, json
111
+ end
112
+ end
113
+
114
+ context "including methods" do
115
+ setup do
116
+ def @contact.label; "Has cheezburger"; end
117
+ def @contact.favorite_quote; "Constraints are liberating"; end
118
+ end
119
+
120
+ should "include single method" do
121
+ json = @contact.to_json(:methods => :label)
122
+ assert_match %r{"label":"Has cheezburger"}, json
123
+ end
124
+
125
+ should "include multiple methods" do
126
+ json = @contact.to_json(:only => :name, :methods => [:label, :favorite_quote])
127
+ assert_match %r{"label":"Has cheezburger"}, json
128
+ assert_match %r{"favorite_quote":"Constraints are liberating"}, json
129
+ assert_match %r{"name":"Konata Izumi"}, json
130
+ assert_no_match %r{"age":16}, json
131
+ assert_no_match %r{"awesome"}, json
132
+ assert_no_match %r{"created_at"}, json
133
+ assert_no_match %r{"preferences"}, json
134
+ end
135
+ end
136
+
137
+ context "array of records" do
138
+ setup do
139
+ @contacts = [
140
+ Contact.new(:name => 'David', :age => 39),
141
+ Contact.new(:name => 'Mary', :age => 14)
142
+ ]
143
+ end
144
+
145
+ should "allow attribute filtering with only" do
146
+ json = @contacts.to_json(:only => :name)
147
+ assert_match %r{\{"name":"David"\}}, json
148
+ assert_match %r{\{"name":"Mary"\}}, json
149
+ end
150
+
151
+ should "allow attribute filtering with except" do
152
+ json = @contacts.to_json(:except => [:name, :preferences, :awesome, :created_at, :updated_at])
153
+ assert_match %r{"age":39}, json
154
+ assert_match %r{"age":14}, json
155
+ assert_no_match %r{"name":}, json
156
+ assert_no_match %r{"preferences":}, json
157
+ assert_no_match %r{"awesome":}, json
158
+ assert_no_match %r{"created_at":}, json
159
+ assert_no_match %r{"updated_at":}, json
160
+ end
161
+ end
162
+
163
+ should "allow options for hash of records" do
164
+ contacts = {
165
+ 1 => Contact.new(:name => 'David', :age => 39),
166
+ 2 => Contact.new(:name => 'Mary', :age => 14)
167
+ }
168
+ json = contacts.to_json(:only => [1, :name])
169
+ assert_match %r{"1":}, json
170
+ assert_match %r{\{"name":"David"\}}, json
171
+ assert_no_match %r{"2":}, json
172
+ end
173
+
174
+ should "include embedded attributes" do
175
+ contact = Contact.new(:name => 'John', :age => 27)
176
+ contact.tags = [Tag.new(:name => 'awesome'), Tag.new(:name => 'ruby')]
177
+ json = contact.to_json
178
+ assert_match %r{"tags":}, json
179
+ assert_match %r{"name":"awesome"}, json
180
+ assert_match %r{"name":"ruby"}, json
181
+ end
182
+
183
+ should "include dynamic attributes" do
184
+ contact = Contact.new(:name => 'John', :age => 27, :foo => 'bar')
185
+ contact['smell'] = 'stinky'
186
+ json = contact.to_json
187
+ assert_match %r{"smell":"stinky"}, json
188
+ end
189
+ end
@@ -0,0 +1,204 @@
1
+ require 'test_helper'
2
+ require 'models'
3
+
4
+ class DocumentTest < Test::Unit::TestCase
5
+ context "The Document Class" do
6
+ setup do
7
+ @document = Class.new do
8
+ include MongoMapper::Document
9
+ set_collection_name 'test'
10
+ end
11
+ @document.collection.remove
12
+ end
13
+
14
+ should "have logger method" do
15
+ @document.logger.should == MongoMapper.logger
16
+ @document.logger.should be_instance_of(Logger)
17
+ end
18
+
19
+ should "track its descendants" do
20
+ MongoMapper::Document.descendants.should include(@document)
21
+ end
22
+
23
+ should "use default database by default" do
24
+ @document.database.should == MongoMapper.database
25
+ end
26
+
27
+ should "have a connection" do
28
+ @document.connection.should be_instance_of(Mongo::Connection)
29
+ end
30
+
31
+ should "allow setting different connection without affecting the default" do
32
+ conn = Mongo::Connection.new
33
+ @document.connection conn
34
+ @document.connection.should == conn
35
+ @document.connection.should_not == MongoMapper.connection
36
+ end
37
+
38
+ should "allow setting a different database without affecting the default" do
39
+ @document.set_database_name 'test2'
40
+ @document.database_name.should == 'test2'
41
+ @document.database.name.should == 'test2'
42
+
43
+ another_document = Class.new do
44
+ include MongoMapper::Document
45
+ set_collection_name 'test'
46
+ end
47
+ another_document.database.should == MongoMapper.database
48
+ end
49
+
50
+ should "default collection name to class name tableized" do
51
+ class ::Item
52
+ include MongoMapper::Document
53
+ end
54
+
55
+ Item.collection.should be_instance_of(Mongo::Collection)
56
+ Item.collection.name.should == 'items'
57
+ end
58
+
59
+ should "default collection name of namespaced class to tableized with dot separation" do
60
+ module ::BloggyPoo
61
+ class Post
62
+ include MongoMapper::Document
63
+ end
64
+ end
65
+
66
+ BloggyPoo::Post.collection.should be_instance_of(Mongo::Collection)
67
+ BloggyPoo::Post.collection.name.should == 'bloggy_poo.posts'
68
+ end
69
+
70
+ should "allow setting the collection name" do
71
+ @document.set_collection_name('foobar')
72
+ @document.collection.should be_instance_of(Mongo::Collection)
73
+ @document.collection.name.should == 'foobar'
74
+ end
75
+ end # Document class
76
+
77
+ context "Documents that inherit from other documents" do
78
+ should "default collection name to inherited class" do
79
+ Message.collection_name.should == 'messages'
80
+ Enter.collection_name.should == 'messages'
81
+ Exit.collection_name.should == 'messages'
82
+ Chat.collection_name.should == 'messages'
83
+ end
84
+
85
+ should "default associations to inherited class" do
86
+ Message.associations.keys.should include("room")
87
+ Enter.associations.keys.should include("room")
88
+ Exit.associations.keys.should include("room")
89
+ Chat.associations.keys.should include("room")
90
+ end
91
+
92
+ should "track subclasses" do
93
+ Message.subclasses.should == [Enter, Exit, Chat]
94
+ end
95
+ end
96
+
97
+ context "An instance of a document" do
98
+ setup do
99
+ @document = Class.new do
100
+ include MongoMapper::Document
101
+ set_collection_name 'test'
102
+
103
+ key :name, String
104
+ key :age, Integer
105
+ end
106
+ @document.collection.remove
107
+ end
108
+
109
+ should "have access to logger" do
110
+ doc = @document.new
111
+ doc.logger.should == @document.logger
112
+ doc.logger.should be_instance_of(Logger)
113
+ end
114
+
115
+ should "have access to the class's collection" do
116
+ doc = @document.new
117
+ doc.collection.name.should == @document.collection.name
118
+ end
119
+
120
+ should "use default values if defined for keys" do
121
+ @document.key :active, Boolean, :default => true
122
+
123
+ @document.new.active.should be_true
124
+ @document.new(:active => false).active.should be_false
125
+ end
126
+
127
+ should "use default values if defined even when custom data type" do
128
+ @document.key :window, WindowSize, :default => WindowSize.new(600, 480)
129
+
130
+ doc = @document.new
131
+ doc.window.should == WindowSize.new(600, 480)
132
+ end
133
+
134
+ context "root document" do
135
+ should "have a nil _root_document" do
136
+ @document.new._root_document.should be_nil
137
+ end
138
+
139
+ should "set self to the root document on embedded documents" do
140
+ klass = Class.new do
141
+ include MongoMapper::Document
142
+ end
143
+
144
+ pets = Class.new do
145
+ include MongoMapper::EmbeddedDocument
146
+ end
147
+ klass.many :pets, :class => pets
148
+
149
+ doc = klass.new(:pets => [{}])
150
+ doc.pets.first._root_document.should == doc
151
+ end
152
+ end
153
+
154
+ context "new?" do
155
+ should "be true if no id" do
156
+ @document.new.new?.should be_true
157
+ end
158
+
159
+ should "be true if id but using custom id and not saved yet" do
160
+ @document.key :_id, String
161
+ doc = @document.new
162
+ doc.id = '1234'
163
+ doc.new?.should be_true
164
+ end
165
+ end
166
+
167
+ context "clone" do
168
+ should "not set the id" do
169
+ doc = @document.create(:name => "foo", :age => 27)
170
+ clone = doc.clone
171
+ clone.should be_new
172
+ end
173
+
174
+ should "copy the attributes" do
175
+ doc = @document.create(:name => "foo", :age => 27)
176
+ clone = doc.clone
177
+ clone.name.should == "foo"
178
+ clone.age.should == 27
179
+ end
180
+ end
181
+
182
+ context "equality" do
183
+ setup do
184
+ @oid = Mongo::ObjectID.new
185
+ end
186
+ should "be equal if id and class are the same" do
187
+ (@document.new('_id' => @oid) == @document.new('_id' => @oid)).should be(true)
188
+ end
189
+
190
+ should "not be equal if class same but id different" do
191
+ (@document.new('_id' => @oid) == @document.new('_id' => Mongo::ObjectID.new)).should be(false)
192
+ end
193
+
194
+ should "not be equal if id same but class different" do
195
+ @another_document = Class.new do
196
+ include MongoMapper::Document
197
+ set_collection_name 'test'
198
+ end
199
+
200
+ (@document.new('_id' => @oid) == @another_document.new('_id' => @oid)).should be(false)
201
+ end
202
+ end
203
+ end # instance of a document
204
+ end # DocumentTest