jmonteiro-mongo_mapper 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 (91) hide show
  1. data/.gitignore +10 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +38 -0
  4. data/Rakefile +55 -0
  5. data/VERSION +1 -0
  6. data/bin/mmconsole +60 -0
  7. data/jmonteiro-mongo_mapper.gemspec +195 -0
  8. data/lib/mongo_mapper.rb +128 -0
  9. data/lib/mongo_mapper/descendant_appends.rb +44 -0
  10. data/lib/mongo_mapper/document.rb +402 -0
  11. data/lib/mongo_mapper/dynamic_finder.rb +74 -0
  12. data/lib/mongo_mapper/embedded_document.rb +61 -0
  13. data/lib/mongo_mapper/finder_options.rb +127 -0
  14. data/lib/mongo_mapper/plugins.rb +19 -0
  15. data/lib/mongo_mapper/plugins/associations.rb +104 -0
  16. data/lib/mongo_mapper/plugins/associations/base.rb +121 -0
  17. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +28 -0
  18. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +23 -0
  19. data/lib/mongo_mapper/plugins/associations/collection.rb +21 -0
  20. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +49 -0
  21. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +139 -0
  22. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
  23. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +117 -0
  24. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +31 -0
  25. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +23 -0
  26. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +13 -0
  27. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +66 -0
  28. data/lib/mongo_mapper/plugins/associations/proxy.rb +118 -0
  29. data/lib/mongo_mapper/plugins/callbacks.rb +65 -0
  30. data/lib/mongo_mapper/plugins/clone.rb +13 -0
  31. data/lib/mongo_mapper/plugins/descendants.rb +16 -0
  32. data/lib/mongo_mapper/plugins/dirty.rb +119 -0
  33. data/lib/mongo_mapper/plugins/equality.rb +11 -0
  34. data/lib/mongo_mapper/plugins/identity_map.rb +66 -0
  35. data/lib/mongo_mapper/plugins/inspect.rb +14 -0
  36. data/lib/mongo_mapper/plugins/keys.rb +295 -0
  37. data/lib/mongo_mapper/plugins/logger.rb +17 -0
  38. data/lib/mongo_mapper/plugins/pagination.rb +85 -0
  39. data/lib/mongo_mapper/plugins/protected.rb +31 -0
  40. data/lib/mongo_mapper/plugins/rails.rb +80 -0
  41. data/lib/mongo_mapper/plugins/serialization.rb +109 -0
  42. data/lib/mongo_mapper/plugins/validations.rb +48 -0
  43. data/lib/mongo_mapper/support.rb +213 -0
  44. data/performance/read_write.rb +52 -0
  45. data/specs.watchr +51 -0
  46. data/test/NOTE_ON_TESTING +1 -0
  47. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +63 -0
  48. data/test/functional/associations/test_belongs_to_proxy.rb +93 -0
  49. data/test/functional/associations/test_in_array_proxy.rb +309 -0
  50. data/test/functional/associations/test_many_documents_as_proxy.rb +246 -0
  51. data/test/functional/associations/test_many_documents_proxy.rb +437 -0
  52. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +175 -0
  53. data/test/functional/associations/test_many_embedded_proxy.rb +216 -0
  54. data/test/functional/associations/test_many_polymorphic_proxy.rb +340 -0
  55. data/test/functional/associations/test_one_proxy.rb +149 -0
  56. data/test/functional/test_associations.rb +44 -0
  57. data/test/functional/test_binary.rb +27 -0
  58. data/test/functional/test_callbacks.rb +81 -0
  59. data/test/functional/test_dirty.rb +156 -0
  60. data/test/functional/test_document.rb +1171 -0
  61. data/test/functional/test_embedded_document.rb +125 -0
  62. data/test/functional/test_identity_map.rb +233 -0
  63. data/test/functional/test_logger.rb +20 -0
  64. data/test/functional/test_modifiers.rb +252 -0
  65. data/test/functional/test_pagination.rb +93 -0
  66. data/test/functional/test_protected.rb +41 -0
  67. data/test/functional/test_string_id_compatibility.rb +67 -0
  68. data/test/functional/test_validations.rb +329 -0
  69. data/test/models.rb +232 -0
  70. data/test/support/custom_matchers.rb +55 -0
  71. data/test/support/timing.rb +16 -0
  72. data/test/test_helper.rb +60 -0
  73. data/test/unit/associations/test_base.rb +207 -0
  74. data/test/unit/associations/test_proxy.rb +103 -0
  75. data/test/unit/serializers/test_json_serializer.rb +189 -0
  76. data/test/unit/test_descendant_appends.rb +71 -0
  77. data/test/unit/test_document.rb +203 -0
  78. data/test/unit/test_dynamic_finder.rb +125 -0
  79. data/test/unit/test_embedded_document.rb +628 -0
  80. data/test/unit/test_finder_options.rb +325 -0
  81. data/test/unit/test_keys.rb +169 -0
  82. data/test/unit/test_mongo_mapper.rb +65 -0
  83. data/test/unit/test_pagination.rb +127 -0
  84. data/test/unit/test_plugins.rb +42 -0
  85. data/test/unit/test_rails.rb +139 -0
  86. data/test/unit/test_rails_compatibility.rb +42 -0
  87. data/test/unit/test_serialization.rb +51 -0
  88. data/test/unit/test_support.rb +350 -0
  89. data/test/unit/test_time_zones.rb +39 -0
  90. data/test/unit/test_validations.rb +492 -0
  91. metadata +260 -0
@@ -0,0 +1,103 @@
1
+ require 'test_helper'
2
+
3
+ class FakeNilProxy < MongoMapper::Plugins::Associations::Proxy
4
+ def find_target; nil end
5
+ end
6
+
7
+ class FakeBlankProxy < MongoMapper::Plugins::Associations::Proxy
8
+ def find_target; '' end
9
+ end
10
+
11
+ class FakeProxy < MongoMapper::Plugins::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
+ @nil_proxy.blank?.should be_true
57
+ end
58
+
59
+ should "be false if not blank" do
60
+ @proxy.blank?.should be_false
61
+ end
62
+ end
63
+
64
+ context "present?" do
65
+ should "be true if present" do
66
+ @proxy.present?.should be_true
67
+ end
68
+
69
+ should "be false if not present" do
70
+ @blank_proxy.present?.should be_false
71
+ @nil_proxy.present?.should be_false
72
+ end
73
+ end
74
+
75
+ should "delegate respond_to? to target" do
76
+ @proxy.respond_to?(:each).should be_true
77
+ @proxy.respond_to?(:size).should be_true
78
+ @proxy.respond_to?(:gsub).should be_false
79
+ end
80
+
81
+ should "alias proxy owner to owner" do
82
+ @proxy.proxy_owner.should == @owner
83
+ end
84
+
85
+ should "alias proxy target to target" do
86
+ @proxy.proxy_target.should == @target
87
+ end
88
+
89
+ context "send" do
90
+ should "work if proxy responds to method" do
91
+ @proxy.send(:reset)
92
+ @proxy.target.should be_nil
93
+ end
94
+
95
+ should "work if the target responds to the method" do
96
+ @proxy.send(:size).should == 2
97
+ end
98
+
99
+ should "not work if neither the proxy or target respond to method" do
100
+ lambda { @proxy.send(:gsub) }.should raise_error
101
+ end
102
+ end
103
+ 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,71 @@
1
+ require 'test_helper'
2
+
3
+ class DescendantAppendsTest < Test::Unit::TestCase
4
+ context "Document" do
5
+ should "default descendants to a new set" do
6
+ MongoMapper::Document.descendants.should be_instance_of(Set)
7
+ end
8
+
9
+ should 'allow extensions to Document to be appended' do
10
+ module Extension; def test_this_extension; end end
11
+ MongoMapper::Document.append_extensions(Extension)
12
+ article = Doc()
13
+ article.should respond_to(:test_this_extension)
14
+ end
15
+
16
+ should 'add appended extensions to classes that include Document before they are added' do
17
+ module Extension; def test_this_extension; end end
18
+ article = Doc()
19
+ MongoMapper::Document.append_extensions(Extension)
20
+ article.should respond_to(:test_this_extension)
21
+ end
22
+
23
+ should 'allow inclusions to Document to be appended' do
24
+ module Inclusion; def test_this_inclusion; end end
25
+ MongoMapper::Document.append_inclusions(Inclusion)
26
+ article = Doc()
27
+ article.new.should respond_to(:test_this_inclusion)
28
+ end
29
+
30
+ should 'add appended inclusions to classes that include Document before they are added' do
31
+ module Inclusion; def test_this_inclusion; end end
32
+ article = Doc()
33
+ MongoMapper::Document.append_inclusions(Inclusion)
34
+ article.new.should respond_to(:test_this_inclusion)
35
+ end
36
+ end
37
+
38
+ context "EmbeddedDocument" do
39
+ should "default descendants to a new set" do
40
+ MongoMapper::EmbeddedDocument.descendants.should be_instance_of(Set)
41
+ end
42
+
43
+ should 'allow extensions to Document to be appended' do
44
+ module Extension; def test_this_extension; end end
45
+ MongoMapper::EmbeddedDocument.append_extensions(Extension)
46
+ article = EDoc()
47
+ article.should respond_to(:test_this_extension)
48
+ end
49
+
50
+ should 'add appended extensions to classes that include Document before they are added' do
51
+ module Extension; def test_this_extension; end end
52
+ article = EDoc()
53
+ MongoMapper::EmbeddedDocument.append_extensions(Extension)
54
+ article.should respond_to(:test_this_extension)
55
+ end
56
+
57
+ should 'allow inclusions to Document to be appended' do
58
+ module Inclusion; def test_this_inclusion; end end
59
+ MongoMapper::EmbeddedDocument.append_inclusions(Inclusion)
60
+ article = EDoc()
61
+ article.new.should respond_to(:test_this_inclusion)
62
+ end
63
+
64
+ should 'add appended inclusions to classes that include Document before they are added' do
65
+ module Inclusion; def test_this_inclusion; end end
66
+ article = EDoc()
67
+ MongoMapper::EmbeddedDocument.append_inclusions(Inclusion)
68
+ article.new.should respond_to(:test_this_inclusion)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,203 @@
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 = Doc()
8
+ end
9
+
10
+ should "return false for embeddable" do
11
+ Doc().embeddable?.should be_false
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 "use default database by default" do
20
+ @document.database.should == MongoMapper.database
21
+ end
22
+
23
+ should "have a connection" do
24
+ @document.connection.should be_instance_of(Mongo::Connection)
25
+ end
26
+
27
+ should "allow setting different connection without affecting the default" do
28
+ conn = Mongo::Connection.new
29
+ @document.connection conn
30
+ @document.connection.should == conn
31
+ @document.connection.should_not == MongoMapper.connection
32
+ end
33
+
34
+ should "allow setting a different database without affecting the default" do
35
+ @document.set_database_name 'test2'
36
+ @document.database_name.should == 'test2'
37
+ @document.database.name.should == 'test2'
38
+
39
+ another_document = Doc()
40
+ another_document.database.should == MongoMapper.database
41
+ end
42
+
43
+ should "default collection name to class name tableized" do
44
+ class ::Item
45
+ include MongoMapper::Document
46
+ end
47
+
48
+ Item.collection.should be_instance_of(Mongo::Collection)
49
+ Item.collection.name.should == 'items'
50
+ end
51
+
52
+ should "default collection name of namespaced class to tableized with dot separation" do
53
+ module ::BloggyPoo
54
+ class Post
55
+ include MongoMapper::Document
56
+ end
57
+ end
58
+
59
+ BloggyPoo::Post.collection.should be_instance_of(Mongo::Collection)
60
+ BloggyPoo::Post.collection.name.should == 'bloggy_poo.posts'
61
+ end
62
+
63
+ should "allow setting the collection name" do
64
+ @document.set_collection_name('foobar')
65
+ @document.collection.should be_instance_of(Mongo::Collection)
66
+ @document.collection.name.should == 'foobar'
67
+ end
68
+ end # Document class
69
+
70
+ context "Documents that inherit from other documents" do
71
+ should "default collection name to inherited class" do
72
+ Message.collection_name.should == 'messages'
73
+ Enter.collection_name.should == 'messages'
74
+ Exit.collection_name.should == 'messages'
75
+ Chat.collection_name.should == 'messages'
76
+ end
77
+
78
+ should "default associations to inherited class" do
79
+ Message.associations.keys.should include("room")
80
+ Enter.associations.keys.should include("room")
81
+ Exit.associations.keys.should include("room")
82
+ Chat.associations.keys.should include("room")
83
+ end
84
+ end
85
+
86
+ context "descendants" do
87
+ should "default to nil" do
88
+ Enter.descendants.should be_nil
89
+ end
90
+
91
+ should "be recorded" do
92
+ Message.descendants.should == [Enter, Exit, Chat]
93
+ end
94
+ end
95
+
96
+ context "An instance of a document" do
97
+ setup do
98
+ @document = Doc do
99
+ key :name, String
100
+ key :age, Integer
101
+ end
102
+ end
103
+
104
+ should "create id during initialization" do
105
+ @document.new._id.should be_instance_of(Mongo::ObjectID)
106
+ end
107
+
108
+ should "have to_param that is string representation of id" do
109
+ doc = @document.new(:id => Mongo::ObjectID.new)
110
+ doc.to_param.should == doc.id.to_s
111
+ doc.to_param.should be_instance_of(String)
112
+ end
113
+
114
+ should "have access to logger" do
115
+ doc = @document.new
116
+ doc.logger.should == @document.logger
117
+ doc.logger.should be_instance_of(Logger)
118
+ end
119
+
120
+ should "have access to the class's collection" do
121
+ doc = @document.new
122
+ doc.collection.name.should == @document.collection.name
123
+ end
124
+
125
+ should "use default values if defined for keys" do
126
+ @document.key :active, Boolean, :default => true
127
+
128
+ @document.new.active.should be_true
129
+ @document.new(:active => false).active.should be_false
130
+ end
131
+
132
+ should "use default values if defined even when custom data type" do
133
+ @document.key :window, WindowSize, :default => WindowSize.new(600, 480)
134
+
135
+ doc = @document.new
136
+ doc.window.should == WindowSize.new(600, 480)
137
+ end
138
+
139
+ context "root document" do
140
+ should "set self to the root document on embedded documents" do
141
+ klass = Doc()
142
+ pets = EDoc()
143
+
144
+ klass.many :pets, :class => pets
145
+
146
+ doc = klass.new(:pets => [{}])
147
+ doc.pets.first._root_document.should == doc
148
+ end
149
+ end
150
+
151
+ context "new?" do
152
+ should "be true if no id" do
153
+ @document.new.new?.should be_true
154
+ end
155
+
156
+ should "be true if id but using custom id and not saved yet" do
157
+ @document.key :_id, String
158
+ doc = @document.new
159
+ doc.id = '1234'
160
+ doc.new?.should be_true
161
+ end
162
+ end
163
+
164
+ context "clone" do
165
+ should "be new" do
166
+ doc = @document.create(:name => "foo", :age => 27)
167
+ clone = doc.clone
168
+ clone.should be_new
169
+ end
170
+
171
+ should "copy the attributes" do
172
+ doc = @document.create(:name => "foo", :age => 27)
173
+ clone = doc.clone
174
+ clone.name.should == "foo"
175
+ clone.age.should == 27
176
+ end
177
+ end
178
+
179
+ should "call inspect on the document's attributes instead of to_s when inspecting the document" do
180
+ doc = @document.new(:animals => %w(dog cat))
181
+ doc.inspect.should include(%(animals: ["dog", "cat"]))
182
+ end
183
+
184
+ context "equality" do
185
+ setup do
186
+ @oid = Mongo::ObjectID.new
187
+ end
188
+
189
+ should "be equal if id and class are the same" do
190
+ (@document.new('_id' => @oid) == @document.new('_id' => @oid)).should be(true)
191
+ end
192
+
193
+ should "not be equal if class same but id different" do
194
+ (@document.new('_id' => @oid) == @document.new('_id' => Mongo::ObjectID.new)).should be(false)
195
+ end
196
+
197
+ should "not be equal if id same but class different" do
198
+ another_document = Doc()
199
+ (@document.new('_id' => @oid) == another_document.new('_id' => @oid)).should be(false)
200
+ end
201
+ end
202
+ end # instance of a document
203
+ end # DocumentTest