fcoury-mongomapper 0.2.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 (42) hide show
  1. data/.gitignore +7 -0
  2. data/History +30 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +39 -0
  5. data/Rakefile +71 -0
  6. data/VERSION +1 -0
  7. data/lib/mongomapper.rb +70 -0
  8. data/lib/mongomapper/associations.rb +69 -0
  9. data/lib/mongomapper/associations/array_proxy.rb +6 -0
  10. data/lib/mongomapper/associations/base.rb +54 -0
  11. data/lib/mongomapper/associations/belongs_to_proxy.rb +26 -0
  12. data/lib/mongomapper/associations/has_many_embedded_proxy.rb +19 -0
  13. data/lib/mongomapper/associations/has_many_proxy.rb +29 -0
  14. data/lib/mongomapper/associations/polymorphic_belongs_to_proxy.rb +31 -0
  15. data/lib/mongomapper/associations/proxy.rb +66 -0
  16. data/lib/mongomapper/callbacks.rb +106 -0
  17. data/lib/mongomapper/document.rb +276 -0
  18. data/lib/mongomapper/document_rails_compatibility.rb +13 -0
  19. data/lib/mongomapper/embedded_document.rb +248 -0
  20. data/lib/mongomapper/embedded_document_rails_compatibility.rb +22 -0
  21. data/lib/mongomapper/finder_options.rb +81 -0
  22. data/lib/mongomapper/key.rb +82 -0
  23. data/lib/mongomapper/observing.rb +50 -0
  24. data/lib/mongomapper/save_with_validation.rb +19 -0
  25. data/lib/mongomapper/serialization.rb +55 -0
  26. data/lib/mongomapper/serializers/json_serializer.rb +77 -0
  27. data/lib/mongomapper/validations.rb +47 -0
  28. data/mongomapper.gemspec +105 -0
  29. data/test/serializers/test_json_serializer.rb +104 -0
  30. data/test/test_associations.rb +444 -0
  31. data/test/test_callbacks.rb +84 -0
  32. data/test/test_document.rb +1002 -0
  33. data/test/test_embedded_document.rb +253 -0
  34. data/test/test_finder_options.rb +148 -0
  35. data/test/test_helper.rb +62 -0
  36. data/test/test_key.rb +200 -0
  37. data/test/test_mongomapper.rb +28 -0
  38. data/test/test_observing.rb +101 -0
  39. data/test/test_rails_compatibility.rb +73 -0
  40. data/test/test_serializations.rb +54 -0
  41. data/test/test_validations.rb +409 -0
  42. metadata +155 -0
@@ -0,0 +1,253 @@
1
+ require 'test_helper'
2
+
3
+ class EmbeddedDocumentTest < Test::Unit::TestCase
4
+ context "Including MongoMapper::EmbeddedDocument" do
5
+ setup do
6
+ @klass = Class.new do
7
+ include MongoMapper::EmbeddedDocument
8
+ end
9
+ end
10
+
11
+ should "clear out document default keys" do
12
+ @klass.keys.size.should == 0
13
+ end
14
+ end
15
+
16
+ context "An instance of an embedded document" do
17
+ setup do
18
+ @document = Class.new do
19
+ include MongoMapper::EmbeddedDocument
20
+
21
+ key :name, String
22
+ key :age, Integer
23
+ end
24
+ end
25
+
26
+ context "when initialized" do
27
+ should "accept a hash that sets keys and values" do
28
+ doc = @document.new(:name => 'John', :age => 23)
29
+ doc.attributes.should == {'name' => 'John', 'age' => 23}
30
+ end
31
+
32
+ should "not throw error if initialized with nil" do
33
+ doc = @document.new(nil)
34
+ end
35
+ end
36
+
37
+ context "mass assigning keys" do
38
+ should "update values for keys provided" do
39
+ doc = @document.new(:name => 'foobar', :age => 10)
40
+ doc.attributes = {:name => 'new value', :age => 5}
41
+ doc.attributes[:name].should == 'new value'
42
+ doc.attributes[:age].should == 5
43
+ end
44
+
45
+ should "not update values for keys that were not provided" do
46
+ doc = @document.new(:name => 'foobar', :age => 10)
47
+ doc.attributes = {:name => 'new value'}
48
+ doc.attributes[:name].should == 'new value'
49
+ doc.attributes[:age].should == 10
50
+ end
51
+
52
+ should "ignore keys that do not exist" do
53
+ doc = @document.new(:name => 'foobar', :age => 10)
54
+ doc.attributes = {:name => 'new value', :foobar => 'baz'}
55
+ doc.attributes[:name].should == 'new value'
56
+ doc.attributes[:foobar].should be(nil)
57
+ end
58
+
59
+ should "not ignore keys that have methods defined" do
60
+ @document.class_eval do
61
+ attr_writer :password
62
+
63
+ def passwd
64
+ @password
65
+ end
66
+ end
67
+
68
+ doc = @document.new(:name => 'foobar', :password => 'secret')
69
+ doc.passwd.should == 'secret'
70
+ end
71
+
72
+ should "typecast key values" do
73
+ doc = @document.new(:name => 1234, :age => '21')
74
+ doc.name.should == '1234'
75
+ doc.age.should == 21
76
+ end
77
+ end
78
+
79
+ context "requesting keys" do
80
+ should "default to empty hash" do
81
+ doc = @document.new
82
+ doc.attributes.should == {}
83
+ end
84
+
85
+ should "return all keys that aren't nil" do
86
+ doc = @document.new(:name => 'string', :age => nil)
87
+ doc.attributes.should == {'name' => 'string'}
88
+ end
89
+ end
90
+
91
+ context "key shorcuts" do
92
+ should "be able to read key with []" do
93
+ doc = @document.new(:name => 'string')
94
+ doc[:name].should == 'string'
95
+ end
96
+
97
+ should "be able to write key value with []=" do
98
+ doc = @document.new
99
+ doc[:name] = 'string'
100
+ doc[:name].should == 'string'
101
+ end
102
+ end
103
+
104
+ context "indifferent access" do
105
+ should "be enabled for keys" do
106
+ doc = @document.new(:name => 'string')
107
+ doc.attributes[:name].should == 'string'
108
+ doc.attributes['name'].should == 'string'
109
+ end
110
+ end
111
+
112
+ context "reading an attribute" do
113
+ should "work for defined keys" do
114
+ doc = @document.new(:name => 'string')
115
+ doc.name.should == 'string'
116
+ end
117
+
118
+ should "raise no method error for undefined keys" do
119
+ doc = @document.new
120
+ lambda { doc.fart }.should raise_error(NoMethodError)
121
+ end
122
+
123
+ should "know if reader defined" do
124
+ doc = @document.new
125
+ doc.reader?('name').should be(true)
126
+ doc.reader?(:name).should be(true)
127
+ doc.reader?('age').should be(true)
128
+ doc.reader?(:age).should be(true)
129
+ doc.reader?('foobar').should be(false)
130
+ doc.reader?(:foobar).should be(false)
131
+ end
132
+
133
+ should "be accessible for use in the model" do
134
+ @document.class_eval do
135
+ def name_and_age
136
+ "#{read_attribute(:name)} (#{read_attribute(:age)})"
137
+ end
138
+ end
139
+
140
+ doc = @document.new(:name => 'John', :age => 27)
141
+ doc.name_and_age.should == 'John (27)'
142
+ end
143
+ end
144
+
145
+ context "reading an attribute before typcasting" do
146
+ should "work for defined keys" do
147
+ doc = @document.new(:name => 12)
148
+ doc.name_before_typecast.should == 12
149
+ end
150
+
151
+ should "raise no method error for undefined keys" do
152
+ doc = @document.new
153
+ lambda { doc.foo_before_typecast }.should raise_error(NoMethodError)
154
+ end
155
+
156
+ should "be accessible for use in a document" do
157
+ @document.class_eval do
158
+ def untypcasted_name
159
+ read_attribute_before_typecast(:name)
160
+ end
161
+ end
162
+
163
+ doc = @document.new(:name => 12)
164
+ doc.name.should == '12'
165
+ doc.untypcasted_name.should == 12
166
+ end
167
+ end
168
+
169
+ context "writing an attribute" do
170
+ should "work for defined keys" do
171
+ doc = @document.new
172
+ doc.name = 'John'
173
+ doc.name.should == 'John'
174
+ end
175
+
176
+ should "raise no method error for undefined keys" do
177
+ doc = @document.new
178
+ lambda { doc.fart = 'poof!' }.should raise_error(NoMethodError)
179
+ end
180
+
181
+ should "typecast value" do
182
+ doc = @document.new
183
+ doc.name = 1234
184
+ doc.name.should == '1234'
185
+ doc.age = '21'
186
+ doc.age.should == 21
187
+ end
188
+
189
+ should "know if writer defined" do
190
+ doc = @document.new
191
+ doc.writer?('name').should be(true)
192
+ doc.writer?('name=').should be(true)
193
+ doc.writer?(:name).should be(true)
194
+ doc.writer?('age').should be(true)
195
+ doc.writer?('age=').should be(true)
196
+ doc.writer?(:age).should be(true)
197
+ doc.writer?('foobar').should be(false)
198
+ doc.writer?('foobar=').should be(false)
199
+ doc.writer?(:foobar).should be(false)
200
+ end
201
+
202
+ should "be accessible for use in the model" do
203
+ @document.class_eval do
204
+ def name_and_age=(new_value)
205
+ new_value.match(/([^\(\s]+) \((.*)\)/)
206
+ write_attribute :name, $1
207
+ write_attribute :age, $2
208
+ end
209
+ end
210
+
211
+ doc = @document.new
212
+ doc.name_and_age = 'Frank (62)'
213
+ doc.name.should == 'Frank'
214
+ doc.age.should == 62
215
+ end
216
+ end # writing an attribute
217
+
218
+ context "respond_to?" do
219
+ setup do
220
+ @doc = @document.new
221
+ end
222
+
223
+ should "work for readers" do
224
+ @doc.respond_to?(:name).should be_true
225
+ @doc.respond_to?('name').should be_true
226
+ end
227
+
228
+ should "work for writers" do
229
+ @doc.respond_to?(:name=).should be_true
230
+ @doc.respond_to?('name=').should be_true
231
+ end
232
+
233
+ should "work for readers before typecast" do
234
+ @doc.respond_to?(:name_before_typecast).should be_true
235
+ @doc.respond_to?('name_before_typecast').should be_true
236
+ end
237
+ end
238
+
239
+ context "equality" do
240
+ should "be true if all keys and values are equal" do
241
+ doc1 = @document.new(:name => 'John', :age => 27)
242
+ doc2 = @document.new(:name => 'John', :age => 27)
243
+ doc1.should == doc2
244
+ end
245
+
246
+ should "be false if not all the keys and values are equal" do
247
+ doc1 = @document.new(:name => 'Steve', :age => 27)
248
+ doc2 = @document.new(:name => 'John', :age => 27)
249
+ doc1.should_not == doc2
250
+ end
251
+ end
252
+ end # instance of a embedded document
253
+ end
@@ -0,0 +1,148 @@
1
+ require 'test_helper'
2
+
3
+ class FinderOptionsTest < Test::Unit::TestCase
4
+ include MongoMapper
5
+
6
+ should "raise error if provided something other than a hash" do
7
+ lambda { FinderOptions.new }.should raise_error(ArgumentError)
8
+ lambda { FinderOptions.new(1) }.should raise_error(ArgumentError)
9
+ end
10
+
11
+ should "have symbolize the keys of the hash provided" do
12
+ FinderOptions.new('offset' => 1).options.keys.map do |key|
13
+ key.should be_instance_of(Symbol)
14
+ end
15
+ end
16
+
17
+ context "#criteria" do
18
+ should "convert conditions to criteria" do
19
+ FinderOptions.expects(:to_mongo_criteria).with(:foo => 1).returns({})
20
+ FinderOptions.new(:conditions => {:foo => 1}).criteria
21
+ end
22
+ end
23
+
24
+ context "#options" do
25
+ should "convert options to mongo options" do
26
+ FinderOptions.expects(:to_mongo_options).with(:order => 'foo asc', :select => 'foo,bar').returns({})
27
+ FinderOptions.new(:order => 'foo asc', :select => 'foo,bar').options
28
+ end
29
+ end
30
+
31
+
32
+ context "Converting conditions to criteria" do
33
+ should "work with simple criteria" do
34
+ FinderOptions.to_mongo_criteria(:foo => 'bar').should == {
35
+ :foo => 'bar'
36
+ }
37
+
38
+ FinderOptions.to_mongo_criteria(:foo => 'bar', :baz => 'wick').should == {
39
+ :foo => 'bar',
40
+ :baz => 'wick'
41
+ }
42
+ end
43
+
44
+ should "use $in for arrays" do
45
+ FinderOptions.to_mongo_criteria(:foo => [1,2,3]).should == {
46
+ :foo => {'$in' => [1,2,3]}
47
+ }
48
+ end
49
+
50
+ should "work arbitrarily deep" do
51
+ FinderOptions.to_mongo_criteria(:foo => {:bar => [1,2,3]}).should == {
52
+ :foo => {:bar => {'$in' => [1,2,3]}}
53
+ }
54
+ end
55
+ end
56
+
57
+ context "ordering" do
58
+ should "single field with ascending direction" do
59
+ hash = OrderedHash.new
60
+ hash[:foo] = 1
61
+ FinderOptions.to_mongo_options(:order => 'foo asc')[:sort].should == hash
62
+ FinderOptions.to_mongo_options(:order => 'foo ASC')[:sort].should == hash
63
+ end
64
+
65
+ should "single field with descending direction" do
66
+ hash = OrderedHash.new
67
+ hash[:foo] = -1
68
+ FinderOptions.to_mongo_options(:order => 'foo desc')[:sort].should == hash
69
+ FinderOptions.to_mongo_options(:order => 'foo DESC')[:sort].should == hash
70
+ end
71
+
72
+ should "convert field without direction to ascending" do
73
+ hash = OrderedHash.new
74
+ hash[:foo] = 1
75
+ FinderOptions.to_mongo_options(:order => 'foo')[:sort].should == hash
76
+ end
77
+
78
+ should "convert multiple fields with directions" do
79
+ hash = OrderedHash.new
80
+ hash[:foo] = -1
81
+ hash[:bar] = 1
82
+ hash[:baz] = -1
83
+ FinderOptions.to_mongo_options(:order => 'foo desc, bar asc, baz desc')[:sort].should == hash
84
+ end
85
+
86
+ should "convert multiple fields with some missing directions" do
87
+ hash = OrderedHash.new
88
+ hash[:foo] = -1
89
+ hash[:bar] = 1
90
+ hash[:baz] = 1
91
+ FinderOptions.to_mongo_options(:order => 'foo desc, bar, baz')[:sort].should == hash
92
+ end
93
+ end
94
+
95
+ context "offset" do
96
+ should "default to 0" do
97
+ FinderOptions.to_mongo_options({})[:offset].should == 0
98
+ end
99
+
100
+ should "use offset provided" do
101
+ FinderOptions.to_mongo_options(:offset => 2)[:offset].should == 2
102
+ end
103
+
104
+ should "covert string to integer" do
105
+ FinderOptions.to_mongo_options(:offset => '2')[:offset].should == 2
106
+ end
107
+ end
108
+
109
+ context "limit" do
110
+ should "default to 0" do
111
+ FinderOptions.to_mongo_options({})[:limit].should == 0
112
+ end
113
+
114
+ should "use offset provided" do
115
+ FinderOptions.to_mongo_options(:limit => 2)[:limit].should == 2
116
+ end
117
+
118
+ should "covert string to integer" do
119
+ FinderOptions.to_mongo_options(:limit => '2')[:limit].should == 2
120
+ end
121
+ end
122
+
123
+ context "fields" do
124
+ should "default to nil" do
125
+ FinderOptions.to_mongo_options({})[:fields].should be(nil)
126
+ end
127
+
128
+ should "be converted to nil if empty string" do
129
+ FinderOptions.to_mongo_options(:fields => '')[:fields].should be(nil)
130
+ end
131
+
132
+ should "be converted to nil if []" do
133
+ FinderOptions.to_mongo_options(:fields => [])[:fields].should be(nil)
134
+ end
135
+
136
+ should "should work with array" do
137
+ FinderOptions.to_mongo_options({:fields => %w(a b)})[:fields].should == %w(a b)
138
+ end
139
+
140
+ should "convert comma separated list to array" do
141
+ FinderOptions.to_mongo_options({:fields => 'a, b'})[:fields].should == %w(a b)
142
+ end
143
+
144
+ should "also work as select" do
145
+ FinderOptions.new(:select => %w(a b)).options[:fields].should == %w(a b)
146
+ end
147
+ end
148
+ end # FinderOptionsTest
@@ -0,0 +1,62 @@
1
+ require 'pathname'
2
+ require 'rubygems'
3
+ require 'test/unit'
4
+ require 'shoulda'
5
+
6
+ gem 'mocha', '~> 0.9.4'
7
+ gem 'jnunemaker-matchy', '0.4.0'
8
+
9
+ require 'matchy'
10
+ require 'mocha'
11
+
12
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
13
+ dir = (Pathname(__FILE__).dirname + '..' + 'lib').expand_path
14
+ require dir + 'mongomapper'
15
+
16
+ class Test::Unit::TestCase
17
+ custom_matcher :be_nil do |receiver, matcher, args|
18
+ matcher.positive_failure_message = "Expected #{receiver} to be nil but it wasn't"
19
+ matcher.negative_failure_message = "Expected #{receiver} not to be nil but it was"
20
+ receiver.nil?
21
+ end
22
+
23
+ custom_matcher :be_true do |receiver, matcher, args|
24
+ matcher.positive_failure_message = "Expected #{receiver} to be true but it wasn't"
25
+ matcher.negative_failure_message = "Expected #{receiver} not to be true but it was"
26
+ receiver.eql?(true)
27
+ end
28
+
29
+ custom_matcher :be_false do |receiver, matcher, args|
30
+ matcher.positive_failure_message = "Expected #{receiver} to be false but it wasn't"
31
+ matcher.negative_failure_message = "Expected #{receiver} not to be false but it was"
32
+ receiver.eql?(false)
33
+ end
34
+
35
+ custom_matcher :be_valid do |receiver, matcher, args|
36
+ matcher.positive_failure_message = "Expected to be valid but it was invalid #{receiver.errors.inspect}"
37
+ matcher.negative_failure_message = "Expected to be invalid but it was valid #{receiver.errors.inspect}"
38
+ receiver.valid?
39
+ end
40
+
41
+ custom_matcher :have_error_on do |receiver, matcher, args|
42
+ receiver.valid?
43
+ attribute = args[0]
44
+ expected_message = args[1]
45
+
46
+ if expected_message.nil?
47
+ matcher.positive_failure_message = "#{receiver} had no errors on #{attribute}"
48
+ matcher.negative_failure_message = "#{receiver} had errors on #{attribute} #{receiver.errors.inspect}"
49
+ !receiver.errors.on(attribute).blank?
50
+ else
51
+ actual = receiver.errors.on(attribute)
52
+ matcher.positive_failure_message = %Q(Expected error on #{attribute} to be "#{expected_message}" but was "#{actual}")
53
+ matcher.negative_failure_message = %Q(Expected error on #{attribute} not to be "#{expected_message}" but was "#{actual}")
54
+ actual == expected_message
55
+ end
56
+ end
57
+ end
58
+
59
+ DefaultDatabase = 'test' unless defined?(DefaultDatabase)
60
+ AlternateDatabase = 'test2' unless defined?(AlternateDatabase)
61
+
62
+ MongoMapper.database = DefaultDatabase