djsun-mongomapper 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/.gitignore +7 -0
  2. data/History +51 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +39 -0
  5. data/Rakefile +71 -0
  6. data/VERSION +1 -0
  7. data/bin/mmconsole +56 -0
  8. data/lib/mongomapper.rb +96 -0
  9. data/lib/mongomapper/associations.rb +61 -0
  10. data/lib/mongomapper/associations/base.rb +71 -0
  11. data/lib/mongomapper/associations/belongs_to_polymorphic_proxy.rb +32 -0
  12. data/lib/mongomapper/associations/belongs_to_proxy.rb +22 -0
  13. data/lib/mongomapper/associations/many_documents_proxy.rb +85 -0
  14. data/lib/mongomapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
  15. data/lib/mongomapper/associations/many_embedded_proxy.rb +17 -0
  16. data/lib/mongomapper/associations/many_polymorphic_proxy.rb +11 -0
  17. data/lib/mongomapper/associations/many_proxy.rb +6 -0
  18. data/lib/mongomapper/associations/proxy.rb +67 -0
  19. data/lib/mongomapper/callbacks.rb +106 -0
  20. data/lib/mongomapper/document.rb +278 -0
  21. data/lib/mongomapper/embedded_document.rb +237 -0
  22. data/lib/mongomapper/finder_options.rb +96 -0
  23. data/lib/mongomapper/key.rb +80 -0
  24. data/lib/mongomapper/observing.rb +50 -0
  25. data/lib/mongomapper/pagination.rb +52 -0
  26. data/lib/mongomapper/rails_compatibility/document.rb +15 -0
  27. data/lib/mongomapper/rails_compatibility/embedded_document.rb +25 -0
  28. data/lib/mongomapper/save_with_validation.rb +19 -0
  29. data/lib/mongomapper/serialization.rb +55 -0
  30. data/lib/mongomapper/serializers/json_serializer.rb +79 -0
  31. data/lib/mongomapper/validations.rb +47 -0
  32. data/mongomapper.gemspec +139 -0
  33. data/test/NOTE_ON_TESTING +1 -0
  34. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +39 -0
  35. data/test/functional/associations/test_belongs_to_proxy.rb +35 -0
  36. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +131 -0
  37. data/test/functional/associations/test_many_embedded_proxy.rb +106 -0
  38. data/test/functional/associations/test_many_polymorphic_proxy.rb +267 -0
  39. data/test/functional/associations/test_many_proxy.rb +236 -0
  40. data/test/functional/test_associations.rb +40 -0
  41. data/test/functional/test_callbacks.rb +85 -0
  42. data/test/functional/test_document.rb +691 -0
  43. data/test/functional/test_pagination.rb +81 -0
  44. data/test/functional/test_rails_compatibility.rb +31 -0
  45. data/test/functional/test_validations.rb +172 -0
  46. data/test/models.rb +108 -0
  47. data/test/test_helper.rb +67 -0
  48. data/test/unit/serializers/test_json_serializer.rb +103 -0
  49. data/test/unit/test_association_base.rb +136 -0
  50. data/test/unit/test_document.rb +125 -0
  51. data/test/unit/test_embedded_document.rb +370 -0
  52. data/test/unit/test_finder_options.rb +214 -0
  53. data/test/unit/test_key.rb +217 -0
  54. data/test/unit/test_mongo_id.rb +35 -0
  55. data/test/unit/test_mongomapper.rb +28 -0
  56. data/test/unit/test_observing.rb +101 -0
  57. data/test/unit/test_pagination.rb +113 -0
  58. data/test/unit/test_rails_compatibility.rb +34 -0
  59. data/test/unit/test_serializations.rb +52 -0
  60. data/test/unit/test_validations.rb +259 -0
  61. metadata +189 -0
@@ -0,0 +1,103 @@
1
+ require 'test_helper'
2
+
3
+ class JsonSerializationTest < Test::Unit::TestCase
4
+ class Contact
5
+ include MongoMapper::Document
6
+ key :name, String
7
+ key :age, Integer
8
+ key :created_at, Time
9
+ key :awesome, Boolean
10
+ key :preferences, Hash
11
+ end
12
+
13
+ def setup
14
+ Contact.include_root_in_json = false
15
+ @contact = Contact.new(
16
+ :name => 'Konata Izumi',
17
+ :age => 16,
18
+ :created_at => Time.utc(2006, 8, 1),
19
+ :awesome => true,
20
+ :preferences => { :shows => 'anime' }
21
+ )
22
+ end
23
+
24
+ should "include demodulized root" do
25
+ Contact.include_root_in_json = true
26
+ assert_match %r{^\{"contact": \{}, @contact.to_json
27
+ end
28
+
29
+ should "encode all encodable attributes" do
30
+ json = @contact.to_json
31
+
32
+ assert_match %r{"name":"Konata Izumi"}, json
33
+ assert_match %r{"age":16}, json
34
+ assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}))
35
+ assert_match %r{"awesome":true}, json
36
+ assert_match %r{"preferences":\{"shows":"anime"\}}, json
37
+ end
38
+
39
+ should "allow attribute filtering with only" do
40
+ json = @contact.to_json(:only => [:name, :age])
41
+
42
+ assert_match %r{"name":"Konata Izumi"}, json
43
+ assert_match %r{"age":16}, json
44
+ assert_no_match %r{"awesome"}, json
45
+ assert_no_match %r{"created_at"}, json
46
+ assert_no_match %r{"preferences"}, json
47
+ end
48
+
49
+ should "allow attribute filtering with except" do
50
+ json = @contact.to_json(:except => [:name, :age])
51
+
52
+ assert_no_match %r{"name"}, json
53
+ assert_no_match %r{"age"}, json
54
+ assert_match %r{"awesome"}, json
55
+ assert_match %r{"created_at"}, json
56
+ assert_match %r{"preferences"}, json
57
+ end
58
+
59
+ context "including methods" do
60
+ setup do
61
+ def @contact.label; "Has cheezburger"; end
62
+ def @contact.favorite_quote; "Constraints are liberating"; end
63
+ end
64
+
65
+ should "include single method" do
66
+ assert_match %r{"label":"Has cheezburger"}, @contact.to_json(:only => :name, :methods => :label)
67
+ end
68
+
69
+ should "include multiple methods" do
70
+ json = @contact.to_json(:only => :name, :methods => [:label, :favorite_quote])
71
+ assert_match %r{"label":"Has cheezburger"}, json
72
+ assert_match %r{"favorite_quote":"Constraints are liberating"}, json
73
+ end
74
+ end
75
+
76
+ context "array of records" do
77
+ setup do
78
+ @contacts = [
79
+ Contact.new(:name => 'David', :age => 39),
80
+ Contact.new(:name => 'Mary', :age => 14)
81
+ ]
82
+ end
83
+
84
+ should "allow attribute filtering with only" do
85
+ assert_equal %([{"name":"David"},{"name":"Mary"}]), @contacts.to_json(:only => :name)
86
+ end
87
+
88
+ should "allow attribute filtering with except" do
89
+ json = @contacts.to_json(:except => [:name, :preferences, :awesome, :created_at, :updated_at, :_id])
90
+ assert_equal %([{"id":"","age":39},{"id":"","age":14}]), json
91
+ end
92
+ end
93
+
94
+ should "allow options for hash of records" do
95
+ contacts = {
96
+ 1 => Contact.new(:name => 'David', :age => 39),
97
+ 2 => Contact.new(:name => 'Mary', :age => 14)
98
+ }
99
+
100
+ assert_equal %({"1":{"name":"David"}}), contacts.to_json(:only => [1, :name])
101
+ end
102
+
103
+ end
@@ -0,0 +1,136 @@
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 ManyPolymorphicProxy for polymorphic many" do
111
+ base = Base.new(:many, :messages, :polymorphic => true)
112
+ base.proxy_class.should == ManyPolymorphicProxy
113
+ end
114
+
115
+ should "be ManyEmbeddedProxy for many embedded" do
116
+ base = Base.new(:many, :medias)
117
+ base.proxy_class.should == ManyEmbeddedProxy
118
+ end
119
+
120
+ should "be ManyEmbeddedPolymorphicProxy for polymorphic many embedded" do
121
+ base = Base.new(:many, :medias, :polymorphic => true)
122
+ base.proxy_class.should == ManyEmbeddedPolymorphicProxy
123
+ end
124
+
125
+ should "be BelongsToProxy for belongs_to" do
126
+ base = Base.new(:belongs_to, :project)
127
+ base.proxy_class.should == BelongsToProxy
128
+ end
129
+
130
+ should "be BelongsToPolymorphicProxy for polymorphic belongs_to" do
131
+ base = Base.new(:belongs_to, :target, :polymorphic => true)
132
+ base.proxy_class.should == BelongsToPolymorphicProxy
133
+ end
134
+ end
135
+
136
+ end
@@ -0,0 +1,125 @@
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
+ end
10
+ end
11
+
12
+ should "track its descendants" do
13
+ MongoMapper::Document.descendants.should include(@document)
14
+ end
15
+
16
+ should "use default database by default" do
17
+ @document.database.should == MongoMapper.database
18
+ end
19
+
20
+ should "have a connection" do
21
+ @document.connection.should be_instance_of(XGen::Mongo::Driver::Mongo)
22
+ end
23
+
24
+ should "allow setting different connection without affecting the default" do
25
+ conn = XGen::Mongo::Driver::Mongo.new
26
+ @document.connection conn
27
+ @document.connection.should == conn
28
+ @document.connection.should_not == MongoMapper.connection
29
+ end
30
+
31
+ should "allow setting a different database without affecting the default" do
32
+ @document.database AlternateDatabase
33
+ @document.database.name.should == AlternateDatabase
34
+
35
+ another_document = Class.new do
36
+ include MongoMapper::Document
37
+ end
38
+ another_document.database.should == MongoMapper.database
39
+ end
40
+
41
+ should "default collection name to class name tableized" do
42
+ class Item
43
+ include MongoMapper::Document
44
+ end
45
+
46
+ Item.collection.should be_instance_of(XGen::Mongo::Driver::Collection)
47
+ Item.collection.name.should == 'items'
48
+ end
49
+
50
+ should "allow setting the collection name" do
51
+ @document.collection('foobar')
52
+ @document.collection.should be_instance_of(XGen::Mongo::Driver::Collection)
53
+ @document.collection.name.should == 'foobar'
54
+ end
55
+ end # Document class
56
+
57
+ context "Documents that inherit from other documents" do
58
+ should "default collection to inherited class" do
59
+ Message.collection.name.should == 'messages'
60
+ Enter.collection.name.should == 'messages'
61
+ Exit.collection.name.should == 'messages'
62
+ Chat.collection.name.should == 'messages'
63
+ end
64
+
65
+ should "track subclasses" do
66
+ Message.subclasses.should == [Enter, Exit, Chat]
67
+ end
68
+ end
69
+
70
+ context "An instance of a document" do
71
+ setup do
72
+ @document = Class.new do
73
+ include MongoMapper::Document
74
+
75
+ key :name, String
76
+ key :age, Integer
77
+ end
78
+ @document.collection.clear
79
+ end
80
+
81
+ should "have access to the class's collection" do
82
+ doc = @document.new
83
+ doc.collection.should == @document.collection
84
+ end
85
+
86
+ should "automatically have a created_at key" do
87
+ @document.keys.keys.should include('created_at')
88
+ end
89
+
90
+ should "automatically have an updated_at key" do
91
+ @document.keys.keys.should include('updated_at')
92
+ end
93
+
94
+ should "use default values if defined for keys" do
95
+ @document.key :active, Boolean, :default => true
96
+
97
+ @document.new.active.should be_true
98
+ @document.new(:active => false).active.should be_false
99
+ end
100
+
101
+ context "new?" do
102
+ should "be true if no id" do
103
+ @document.new.new?.should be(true)
104
+ end
105
+ end
106
+
107
+ context "equality" do
108
+ should "be equal if id and class are the same" do
109
+ (@document.new('_id' => 1) == @document.new('_id' => 1)).should be(true)
110
+ end
111
+
112
+ should "not be equal if class same but id different" do
113
+ (@document.new('_id' => 1) == @document.new('_id' => 2)).should be(false)
114
+ end
115
+
116
+ should "not be equal if id same but class different" do
117
+ @another_document = Class.new do
118
+ include MongoMapper::Document
119
+ end
120
+
121
+ (@document.new('_id' => 1) == @another_document.new('_id' => 1)).should be(false)
122
+ end
123
+ end
124
+ end # instance of a document
125
+ end # DocumentTest
@@ -0,0 +1,370 @@
1
+ require 'test_helper'
2
+
3
+ class Grandparent
4
+ include MongoMapper::EmbeddedDocument
5
+ key :grandparent, String
6
+ end
7
+
8
+ class Parent < Grandparent
9
+ include MongoMapper::EmbeddedDocument
10
+ key :parent, String
11
+ end
12
+
13
+ class Child < Parent
14
+ include MongoMapper::EmbeddedDocument
15
+ key :child, String
16
+ end
17
+
18
+ class OtherChild < Parent
19
+ include MongoMapper::EmbeddedDocument
20
+ key :other_child, String
21
+ end
22
+
23
+ class EmbeddedDocumentTest < Test::Unit::TestCase
24
+ context "Including MongoMapper::EmbeddedDocument" do
25
+ setup do
26
+ @klass = Class.new do
27
+ include MongoMapper::EmbeddedDocument
28
+ end
29
+ end
30
+
31
+ should "add _id key" do
32
+ @klass.keys['_id'].should_not be_nil
33
+ end
34
+ end
35
+
36
+ context "parent_model" do
37
+ should "be nil if none of parents ancestors include EmbeddedDocument" do
38
+ parent = Class.new
39
+ document = Class.new(parent) do
40
+ include MongoMapper::EmbeddedDocument
41
+ end
42
+ document.parent_model.should be_nil
43
+ end
44
+
45
+ should "find parent" do
46
+ Parent.parent_model.should == Grandparent
47
+ Child.parent_model.should == Parent
48
+ end
49
+ end
50
+
51
+ context "defining a key" do
52
+ setup do
53
+ @document = Class.new do
54
+ include MongoMapper::EmbeddedDocument
55
+ end
56
+ end
57
+
58
+ should "work" do
59
+ key = @document.key(:name, String)
60
+ key.name.should == 'name'
61
+ key.type.should == String
62
+ key.should be_instance_of(MongoMapper::Key)
63
+ end
64
+
65
+ should "work with options" do
66
+ key = @document.key(:name, String, :required => true)
67
+ key.options[:required].should be(true)
68
+ end
69
+
70
+ should "be tracked per document" do
71
+ @document.key(:name, String)
72
+ @document.key(:age, Integer)
73
+ @document.keys['name'].name.should == 'name'
74
+ @document.keys['name'].type.should == String
75
+ @document.keys['age'].name.should == 'age'
76
+ @document.keys['age'].type.should == Integer
77
+ end
78
+
79
+ should "be redefinable" do
80
+ @document.key(:foo, String)
81
+ @document.keys['foo'].type.should == String
82
+ @document.key(:foo, Integer)
83
+ @document.keys['foo'].type.should == Integer
84
+ end
85
+
86
+ should "create reader method" do
87
+ @document.new.should_not respond_to(:foo)
88
+ @document.key(:foo, String)
89
+ @document.new.should respond_to(:foo)
90
+ end
91
+
92
+ should "create reader before typecast method" do
93
+ @document.new.should_not respond_to(:foo_before_typecast)
94
+ @document.key(:foo, String)
95
+ @document.new.should respond_to(:foo_before_typecast)
96
+ end
97
+
98
+ should "create writer method" do
99
+ @document.new.should_not respond_to(:foo=)
100
+ @document.key(:foo, String)
101
+ @document.new.should respond_to(:foo=)
102
+ end
103
+
104
+ should "create boolean method" do
105
+ @document.new.should_not respond_to(:foo?)
106
+ @document.key(:foo, String)
107
+ @document.new.should respond_to(:foo?)
108
+ end
109
+ end
110
+
111
+ context "keys" do
112
+ should "be inherited" do
113
+ Grandparent.keys.keys.sort.should == ['_id', 'grandparent']
114
+ Parent.keys.keys.sort.should == ['_id', 'grandparent', 'parent']
115
+ Child.keys.keys.sort.should == ['_id', 'child', 'grandparent', 'parent']
116
+ end
117
+
118
+ should "propogate to subclasses if key added after class definition" do
119
+ Grandparent.key :_type, String
120
+
121
+ Grandparent.keys.keys.sort.should == ['_id', '_type', 'grandparent']
122
+ Parent.keys.keys.sort.should == ['_id', '_type', 'grandparent', 'parent']
123
+ Child.keys.keys.sort.should == ['_id', '_type', 'child', 'grandparent', 'parent']
124
+ end
125
+ end
126
+
127
+ context "subclasses" do
128
+ should "default to nil" do
129
+ Child.subclasses.should be_nil
130
+ end
131
+
132
+ should "be recorded" do
133
+ Grandparent.subclasses.should == [Parent]
134
+ Parent.subclasses.should == [Child, OtherChild]
135
+ end
136
+ end
137
+
138
+ context "An instance of an embedded document" do
139
+ setup do
140
+ @document = Class.new do
141
+ include MongoMapper::EmbeddedDocument
142
+
143
+ key :name, String
144
+ key :age, Integer
145
+ end
146
+ end
147
+
148
+ should "automatically have an _id key" do
149
+ @document.keys.keys.should include('_id')
150
+ end
151
+
152
+ should "have id method that is string representation of _id" do
153
+ doc = @document.new
154
+ doc.id.should == doc._id.to_s
155
+ end
156
+
157
+ should "be able to set _id using id=" do
158
+ id = MongoID.new
159
+ doc = @document.new(:id => id.to_s)
160
+ doc._id.should == id
161
+ doc.id.should == id.to_s
162
+ end
163
+
164
+ context "being initialized" do
165
+ should "accept a hash that sets keys and values" do
166
+ doc = @document.new(:name => 'John', :age => 23)
167
+ doc.attributes.keys.sort.should == ['_id', 'age', 'name']
168
+ doc.attributes['name'].should == 'John'
169
+ doc.attributes['age'].should == 23
170
+ end
171
+
172
+ should "not throw error if initialized with nil" do
173
+ lambda {
174
+ @document.new(nil)
175
+ }.should_not raise_error
176
+ end
177
+ end
178
+
179
+ context "mass assigning keys" do
180
+ should "update values for keys provided" do
181
+ doc = @document.new(:name => 'foobar', :age => 10)
182
+ doc.attributes = {:name => 'new value', :age => 5}
183
+ doc.attributes[:name].should == 'new value'
184
+ doc.attributes[:age].should == 5
185
+ end
186
+
187
+ should "not update values for keys that were not provided" do
188
+ doc = @document.new(:name => 'foobar', :age => 10)
189
+ doc.attributes = {:name => 'new value'}
190
+ doc.attributes[:name].should == 'new value'
191
+ doc.attributes[:age].should == 10
192
+ end
193
+
194
+ should "raise undefined method if no key exists" do
195
+ doc = @document.new(:name => 'foobar', :age => 10)
196
+ lambda {
197
+ doc.attributes = {:name => 'new value', :foobar => 'baz'}
198
+ }.should raise_error(NoMethodError)
199
+ end
200
+
201
+ should "not ignore keys that have methods defined" do
202
+ @document.class_eval do
203
+ attr_writer :password
204
+
205
+ def passwd
206
+ @password
207
+ end
208
+ end
209
+
210
+ doc = @document.new(:name => 'foobar', :password => 'secret')
211
+ doc.passwd.should == 'secret'
212
+ end
213
+
214
+ should "typecast key values" do
215
+ doc = @document.new(:name => 1234, :age => '21')
216
+ doc.name.should == '1234'
217
+ doc.age.should == 21
218
+ end
219
+ end
220
+
221
+ context "attributes" do
222
+ should "default to hash with _id" do
223
+ doc = @document.new
224
+ doc.attributes.keys.should == ['_id']
225
+ end
226
+
227
+ should "return all keys that aren't nil" do
228
+ doc = @document.new(:name => 'string', :age => nil)
229
+ doc.attributes.keys.sort.should == ['_id', 'name']
230
+ doc.attributes.values.should include('string')
231
+ end
232
+ end
233
+
234
+ context "key shorcut access" do
235
+ should "be able to read key with []" do
236
+ doc = @document.new(:name => 'string')
237
+ doc[:name].should == 'string'
238
+ end
239
+
240
+ should "be able to write key value with []=" do
241
+ doc = @document.new
242
+ doc[:name] = 'string'
243
+ doc[:name].should == 'string'
244
+ end
245
+ end
246
+
247
+ context "indifferent access" do
248
+ should "be enabled for keys" do
249
+ doc = @document.new(:name => 'string')
250
+ doc.attributes[:name].should == 'string'
251
+ doc.attributes['name'].should == 'string'
252
+ end
253
+ end
254
+
255
+ context "reading an attribute" do
256
+ should "work for defined keys" do
257
+ doc = @document.new(:name => 'string')
258
+ doc.name.should == 'string'
259
+ end
260
+
261
+ should "raise no method error for undefined keys" do
262
+ doc = @document.new
263
+ lambda { doc.fart }.should raise_error(NoMethodError)
264
+ end
265
+
266
+ should "be accessible for use in the model" do
267
+ @document.class_eval do
268
+ def name_and_age
269
+ "#{read_attribute(:name)} (#{read_attribute(:age)})"
270
+ end
271
+ end
272
+
273
+ doc = @document.new(:name => 'John', :age => 27)
274
+ doc.name_and_age.should == 'John (27)'
275
+ end
276
+ end
277
+
278
+ context "reading an attribute before typcasting" do
279
+ should "work for defined keys" do
280
+ doc = @document.new(:name => 12)
281
+ doc.name_before_typecast.should == 12
282
+ end
283
+
284
+ should "raise no method error for undefined keys" do
285
+ doc = @document.new
286
+ lambda { doc.foo_before_typecast }.should raise_error(NoMethodError)
287
+ end
288
+
289
+ should "be accessible for use in a document" do
290
+ @document.class_eval do
291
+ def untypcasted_name
292
+ read_attribute_before_typecast(:name)
293
+ end
294
+ end
295
+
296
+ doc = @document.new(:name => 12)
297
+ doc.name.should == '12'
298
+ doc.untypcasted_name.should == 12
299
+ end
300
+ end
301
+
302
+ context "writing an attribute" do
303
+ should "work for defined keys" do
304
+ doc = @document.new
305
+ doc.name = 'John'
306
+ doc.name.should == 'John'
307
+ end
308
+
309
+ should "raise no method error for undefined keys" do
310
+ doc = @document.new
311
+ lambda { doc.fart = 'poof!' }.should raise_error(NoMethodError)
312
+ end
313
+
314
+ should "typecast value" do
315
+ doc = @document.new
316
+ doc.name = 1234
317
+ doc.name.should == '1234'
318
+ doc.age = '21'
319
+ doc.age.should == 21
320
+ end
321
+
322
+ should "be accessible for use in the model" do
323
+ @document.class_eval do
324
+ def name_and_age=(new_value)
325
+ new_value.match(/([^\(\s]+) \((.*)\)/)
326
+ write_attribute :name, $1
327
+ write_attribute :age, $2
328
+ end
329
+ end
330
+
331
+ doc = @document.new
332
+ doc.name_and_age = 'Frank (62)'
333
+ doc.name.should == 'Frank'
334
+ doc.age.should == 62
335
+ end
336
+ end # writing an attribute
337
+
338
+ context "checking if an attributes value is present" do
339
+ should "work for defined keys" do
340
+ doc = @document.new
341
+ doc.name?.should be_false
342
+ doc.name = 'John'
343
+ doc.name?.should be_true
344
+ end
345
+
346
+ should "raise no method error for undefined keys" do
347
+ doc = @document.new
348
+ lambda { doc.fart? }.should raise_error(NoMethodError)
349
+ end
350
+ end
351
+
352
+ context "equality" do
353
+ should "be equal if id and class are the same" do
354
+ (@document.new('_id' => 1) == @document.new('_id' => 1)).should be(true)
355
+ end
356
+
357
+ should "not be equal if class same but id different" do
358
+ (@document.new('_id' => 1) == @document.new('_id' => 2)).should be(false)
359
+ end
360
+
361
+ should "not be equal if id same but class different" do
362
+ @another_document = Class.new do
363
+ include MongoMapper::Document
364
+ end
365
+
366
+ (@document.new('_id' => 1) == @another_document.new('_id' => 1)).should be(false)
367
+ end
368
+ end
369
+ end # instance of a embedded document
370
+ end