ramsingla-mongomapper 0.2.1
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.
- data/.gitignore +7 -0
- data/History +30 -0
- data/LICENSE +20 -0
- data/README.rdoc +39 -0
- data/Rakefile +71 -0
- data/VERSION +1 -0
- data/lib/mongomapper.rb +60 -0
- data/lib/mongomapper/associations.rb +69 -0
- data/lib/mongomapper/associations/array_proxy.rb +6 -0
- data/lib/mongomapper/associations/base.rb +50 -0
- data/lib/mongomapper/associations/belongs_to_proxy.rb +26 -0
- data/lib/mongomapper/associations/has_many_embedded_proxy.rb +19 -0
- data/lib/mongomapper/associations/has_many_proxy.rb +28 -0
- data/lib/mongomapper/associations/polymorphic_belongs_to_proxy.rb +31 -0
- data/lib/mongomapper/associations/proxy.rb +60 -0
- data/lib/mongomapper/callbacks.rb +106 -0
- data/lib/mongomapper/document.rb +263 -0
- data/lib/mongomapper/embedded_document.rb +295 -0
- data/lib/mongomapper/finder_options.rb +81 -0
- data/lib/mongomapper/key.rb +82 -0
- data/lib/mongomapper/observing.rb +50 -0
- data/lib/mongomapper/pagination.rb +52 -0
- data/lib/mongomapper/rails_compatibility.rb +23 -0
- data/lib/mongomapper/save_with_validation.rb +19 -0
- data/lib/mongomapper/serialization.rb +55 -0
- data/lib/mongomapper/serializers/json_serializer.rb +77 -0
- data/lib/mongomapper/validations.rb +47 -0
- data/mongomapper.gemspec +105 -0
- data/test/serializers/test_json_serializer.rb +104 -0
- data/test/test_associations.rb +211 -0
- data/test/test_callbacks.rb +84 -0
- data/test/test_document.rb +995 -0
- data/test/test_embedded_document.rb +253 -0
- data/test/test_finder_options.rb +148 -0
- data/test/test_helper.rb +62 -0
- data/test/test_key.rb +200 -0
- data/test/test_mongomapper.rb +28 -0
- data/test/test_observing.rb +101 -0
- data/test/test_rails_compatibility.rb +29 -0
- data/test/test_serializations.rb +54 -0
- data/test/test_validations.rb +409 -0
- metadata +156 -0
@@ -0,0 +1,47 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
module Validations
|
3
|
+
class ValidatesUniquenessOf < Validatable::ValidationBase
|
4
|
+
def valid?(instance)
|
5
|
+
# TODO: scope
|
6
|
+
doc = instance._root.class.find(:first, :conditions => { instance.full_key_path(self.attribute) => instance[attribute]}, :limit => 1)
|
7
|
+
doc.nil? || instance._root.id == doc.id
|
8
|
+
end
|
9
|
+
|
10
|
+
def message(instance)
|
11
|
+
super || "has already been taken"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class ValidatesExclusionOf < Validatable::ValidationBase
|
16
|
+
required_option :within
|
17
|
+
|
18
|
+
def valid?(instance)
|
19
|
+
value = instance[attribute]
|
20
|
+
return true if allow_nil && value.nil?
|
21
|
+
return true if allow_blank && value.blank?
|
22
|
+
|
23
|
+
!within.include?(instance[attribute])
|
24
|
+
end
|
25
|
+
|
26
|
+
def message(instance)
|
27
|
+
super || "is reserved"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class ValidatesInclusionOf < Validatable::ValidationBase
|
32
|
+
required_option :within
|
33
|
+
|
34
|
+
def valid?(instance)
|
35
|
+
value = instance[attribute]
|
36
|
+
return true if allow_nil && value.nil?
|
37
|
+
return true if allow_blank && value.blank?
|
38
|
+
|
39
|
+
within.include?(value)
|
40
|
+
end
|
41
|
+
|
42
|
+
def message(instance)
|
43
|
+
super || "is not in the list"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/mongomapper.gemspec
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{mongomapper}
|
5
|
+
s.version = "0.2.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["John Nunemaker", "Ram Singla"]
|
9
|
+
s.date = %q{2009-07-23}
|
10
|
+
s.email = %q{ram.singla@gmail.com}
|
11
|
+
s.extra_rdoc_files = [
|
12
|
+
"LICENSE",
|
13
|
+
"README.rdoc"
|
14
|
+
]
|
15
|
+
s.files = [
|
16
|
+
".gitignore",
|
17
|
+
"History",
|
18
|
+
"LICENSE",
|
19
|
+
"README.rdoc",
|
20
|
+
"Rakefile",
|
21
|
+
"VERSION",
|
22
|
+
"lib/mongomapper.rb",
|
23
|
+
"lib/mongomapper/associations.rb",
|
24
|
+
"lib/mongomapper/associations/array_proxy.rb",
|
25
|
+
"lib/mongomapper/associations/base.rb",
|
26
|
+
"lib/mongomapper/associations/belongs_to_proxy.rb",
|
27
|
+
"lib/mongomapper/associations/has_many_embedded_proxy.rb",
|
28
|
+
"lib/mongomapper/associations/has_many_proxy.rb",
|
29
|
+
"lib/mongomapper/associations/polymorphic_belongs_to_proxy.rb",
|
30
|
+
"lib/mongomapper/associations/proxy.rb",
|
31
|
+
"lib/mongomapper/callbacks.rb",
|
32
|
+
"lib/mongomapper/document.rb",
|
33
|
+
"lib/mongomapper/embedded_document.rb",
|
34
|
+
"lib/mongomapper/finder_options.rb",
|
35
|
+
"lib/mongomapper/key.rb",
|
36
|
+
"lib/mongomapper/observing.rb",
|
37
|
+
"lib/mongomapper/pagination.rb",
|
38
|
+
"lib/mongomapper/rails_compatibility.rb",
|
39
|
+
"lib/mongomapper/save_with_validation.rb",
|
40
|
+
"lib/mongomapper/serialization.rb",
|
41
|
+
"lib/mongomapper/serializers/json_serializer.rb",
|
42
|
+
"lib/mongomapper/validations.rb",
|
43
|
+
"mongomapper.gemspec",
|
44
|
+
"test/serializers/test_json_serializer.rb",
|
45
|
+
"test/test_associations.rb",
|
46
|
+
"test/test_callbacks.rb",
|
47
|
+
"test/test_document.rb",
|
48
|
+
"test/test_embedded_document.rb",
|
49
|
+
"test/test_finder_options.rb",
|
50
|
+
"test/test_helper.rb",
|
51
|
+
"test/test_key.rb",
|
52
|
+
"test/test_mongomapper.rb",
|
53
|
+
"test/test_observing.rb",
|
54
|
+
"test/test_rails_compatibility.rb",
|
55
|
+
"test/test_serializations.rb",
|
56
|
+
"test/test_validations.rb"
|
57
|
+
]
|
58
|
+
s.has_rdoc = true
|
59
|
+
s.homepage = %q{http://github.com/jnunemaker/mongomapper}
|
60
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
61
|
+
s.require_paths = ["lib"]
|
62
|
+
s.rubyforge_project = %q{mongomapper}
|
63
|
+
s.rubygems_version = %q{1.3.1}
|
64
|
+
s.summary = %q{Awesome gem for modeling your domain and storing it in mongo}
|
65
|
+
s.test_files = [
|
66
|
+
"test/serializers/test_json_serializer.rb",
|
67
|
+
"test/test_associations.rb",
|
68
|
+
"test/test_callbacks.rb",
|
69
|
+
"test/test_document.rb",
|
70
|
+
"test/test_embedded_document.rb",
|
71
|
+
"test/test_finder_options.rb",
|
72
|
+
"test/test_helper.rb",
|
73
|
+
"test/test_key.rb",
|
74
|
+
"test/test_mongomapper.rb",
|
75
|
+
"test/test_observing.rb",
|
76
|
+
"test/test_rails_compatibility.rb",
|
77
|
+
"test/test_serializations.rb",
|
78
|
+
"test/test_validations.rb"
|
79
|
+
]
|
80
|
+
|
81
|
+
if s.respond_to? :specification_version then
|
82
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
83
|
+
s.specification_version = 2
|
84
|
+
|
85
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
86
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 0"])
|
87
|
+
s.add_runtime_dependency(%q<mongodb-mongo>, ["= 0.9"])
|
88
|
+
s.add_runtime_dependency(%q<jnunemaker-validatable>, ["= 1.7.1"])
|
89
|
+
s.add_development_dependency(%q<mocha>, ["= 0.9.4"])
|
90
|
+
s.add_development_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
|
91
|
+
else
|
92
|
+
s.add_dependency(%q<activesupport>, [">= 0"])
|
93
|
+
s.add_dependency(%q<mongodb-mongo>, ["= 0.9"])
|
94
|
+
s.add_dependency(%q<jnunemaker-validatable>, ["= 1.7.1"])
|
95
|
+
s.add_dependency(%q<mocha>, ["= 0.9.4"])
|
96
|
+
s.add_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
|
97
|
+
end
|
98
|
+
else
|
99
|
+
s.add_dependency(%q<activesupport>, [">= 0"])
|
100
|
+
s.add_dependency(%q<mongodb-mongo>, ["= 0.9"])
|
101
|
+
s.add_dependency(%q<jnunemaker-validatable>, ["= 1.7.1"])
|
102
|
+
s.add_dependency(%q<mocha>, ["= 0.9.4"])
|
103
|
+
s.add_dependency(%q<jnunemaker-matchy>, ["= 0.4.0"])
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class JsonSerializationTest < Test::Unit::TestCase
|
4
|
+
class Contact
|
5
|
+
include MongoMapper::EmbeddedDocument
|
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
|
+
# Single method.
|
67
|
+
assert_match %r{"label": "Has cheezburger"}, @contact.to_json(:only => :name, :methods => :label)
|
68
|
+
end
|
69
|
+
|
70
|
+
should "include multiple methods" do
|
71
|
+
json = @contact.to_json(:only => :name, :methods => [:label, :favorite_quote])
|
72
|
+
assert_match %r{"label": "Has cheezburger"}, json
|
73
|
+
assert_match %r{"favorite_quote": "Constraints are liberating"}, json
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context "array of records" do
|
78
|
+
setup do
|
79
|
+
@contacts = [
|
80
|
+
Contact.new(:name => 'David', :age => 39),
|
81
|
+
Contact.new(:name => 'Mary', :age => 14)
|
82
|
+
]
|
83
|
+
end
|
84
|
+
|
85
|
+
should "allow attribute filtering with only" do
|
86
|
+
assert_equal %([{"name": "David"}, {"name": "Mary"}]), @contacts.to_json(:only => :name)
|
87
|
+
end
|
88
|
+
|
89
|
+
should "allow attribute filtering with except" do
|
90
|
+
json = @contacts.to_json(:except => [:name, :preferences, :awesome, :created_at])
|
91
|
+
assert_equal %([{"age": 39}, {"age": 14}]), json
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
should "allow options for hash of records" do
|
96
|
+
contacts = {
|
97
|
+
1 => Contact.new(:name => 'David', :age => 39),
|
98
|
+
2 => Contact.new(:name => 'Mary', :age => 14)
|
99
|
+
}
|
100
|
+
|
101
|
+
assert_equal %({"1": {"name": "David"}}), contacts.to_json(:only => [1, :name])
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
@@ -0,0 +1,211 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'ruby-debug'
|
3
|
+
class Address
|
4
|
+
include MongoMapper::EmbeddedDocument
|
5
|
+
|
6
|
+
key :address, String
|
7
|
+
key :city, String
|
8
|
+
key :state, String
|
9
|
+
key :zip, Integer
|
10
|
+
end
|
11
|
+
|
12
|
+
class Project
|
13
|
+
include MongoMapper::Document
|
14
|
+
|
15
|
+
key :name, String
|
16
|
+
|
17
|
+
many :statuses
|
18
|
+
many :addresses
|
19
|
+
end
|
20
|
+
|
21
|
+
class Status
|
22
|
+
include MongoMapper::Document
|
23
|
+
|
24
|
+
belongs_to :project
|
25
|
+
belongs_to :target, :polymorphic => true
|
26
|
+
|
27
|
+
key :name, String
|
28
|
+
end
|
29
|
+
|
30
|
+
class Person
|
31
|
+
include MongoMapper::EmbeddedDocument
|
32
|
+
key :name, String
|
33
|
+
key :child, Person
|
34
|
+
|
35
|
+
many :pets
|
36
|
+
end
|
37
|
+
|
38
|
+
class Pet
|
39
|
+
include MongoMapper::EmbeddedDocument
|
40
|
+
|
41
|
+
key :name, String
|
42
|
+
key :species, String
|
43
|
+
end
|
44
|
+
|
45
|
+
class AssociationsTest < Test::Unit::TestCase
|
46
|
+
def setup
|
47
|
+
Project.collection.clear
|
48
|
+
Status.collection.clear
|
49
|
+
end
|
50
|
+
|
51
|
+
context "Polymorphic Belongs To" do
|
52
|
+
should "default to nil" do
|
53
|
+
status = Status.new
|
54
|
+
status.target.should be_nil
|
55
|
+
end
|
56
|
+
|
57
|
+
should "store the association" do
|
58
|
+
status = Status.new
|
59
|
+
project = Project.new(:name => "mongomapper")
|
60
|
+
status.target = project
|
61
|
+
status.save.should be_true
|
62
|
+
|
63
|
+
from_db = Status.find(status.id)
|
64
|
+
from_db.target.should_not be_nil
|
65
|
+
from_db.target_id.should == project.id
|
66
|
+
from_db.target_type.should == "Project"
|
67
|
+
from_db.target.name.should == "mongomapper"
|
68
|
+
end
|
69
|
+
|
70
|
+
should "unset the association" do
|
71
|
+
status = Status.new
|
72
|
+
project = Project.new(:name => "mongomapper")
|
73
|
+
status.target = project
|
74
|
+
status.save.should be_true
|
75
|
+
|
76
|
+
from_db = Status.find(status.id)
|
77
|
+
from_db.target = nil
|
78
|
+
from_db.target_type.should be_nil
|
79
|
+
from_db.target_id.should be_nil
|
80
|
+
from_db.target.should be_nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
context "Belongs To" do
|
85
|
+
should "default to nil" do
|
86
|
+
status = Status.new
|
87
|
+
status.project.should be_nil
|
88
|
+
end
|
89
|
+
|
90
|
+
should "store the association" do
|
91
|
+
status = Status.new
|
92
|
+
project = Project.new(:name => "mongomapper")
|
93
|
+
status.project = project
|
94
|
+
status.save.should be_true
|
95
|
+
|
96
|
+
from_db = Status.find(status.id)
|
97
|
+
from_db.project.should_not be_nil
|
98
|
+
from_db.project.name.should == "mongomapper"
|
99
|
+
end
|
100
|
+
|
101
|
+
should "unset the association" do
|
102
|
+
status = Status.new
|
103
|
+
project = Project.new(:name => "mongomapper")
|
104
|
+
status.project = project
|
105
|
+
status.save.should be_true
|
106
|
+
|
107
|
+
from_db = Status.find(status.id)
|
108
|
+
from_db.project = nil
|
109
|
+
from_db.project.should be_nil
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context "Many documents" do
|
114
|
+
should "default reader to empty array" do
|
115
|
+
project = Project.new
|
116
|
+
project.statuses.should == []
|
117
|
+
end
|
118
|
+
|
119
|
+
should "allow adding to association like it was an array" do
|
120
|
+
project = Project.new
|
121
|
+
project.statuses << Status.new
|
122
|
+
project.statuses.push Status.new
|
123
|
+
project.statuses.size.should == 2
|
124
|
+
end
|
125
|
+
|
126
|
+
should "store the association" do
|
127
|
+
project = Project.new
|
128
|
+
project.statuses = [Status.new("name" => "ready")]
|
129
|
+
project.save.should be_true
|
130
|
+
|
131
|
+
from_db = Project.find(project.id)
|
132
|
+
from_db.statuses.size.should == 1
|
133
|
+
from_db.statuses[0].name.should == "ready"
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
context "Many embedded documents" do
|
138
|
+
should "default reader to empty array" do
|
139
|
+
project = Project.new
|
140
|
+
project.addresses.should == []
|
141
|
+
end
|
142
|
+
|
143
|
+
should "allow adding to association like it was an array" do
|
144
|
+
project = Project.new
|
145
|
+
project.addresses << Address.new
|
146
|
+
project.addresses.push Address.new
|
147
|
+
project.addresses.size.should == 2
|
148
|
+
end
|
149
|
+
|
150
|
+
should "be embedded in document on save" do
|
151
|
+
sb = Address.new(:city => 'South Bend', :state => 'IN')
|
152
|
+
chi = Address.new(:city => 'Chicago', :state => 'IL')
|
153
|
+
project = Project.new
|
154
|
+
project.addresses << sb
|
155
|
+
project.addresses << chi
|
156
|
+
project.save
|
157
|
+
|
158
|
+
from_db = Project.find(project.id)
|
159
|
+
from_db.addresses.size.should == 2
|
160
|
+
from_db.addresses[0].should == sb
|
161
|
+
from_db.addresses[1].should == chi
|
162
|
+
end
|
163
|
+
|
164
|
+
should "allow embedding arbitrarily deep" do
|
165
|
+
@document = Class.new do
|
166
|
+
include MongoMapper::Document
|
167
|
+
key :person, Person
|
168
|
+
end
|
169
|
+
|
170
|
+
meg = Person.new(:name => "Meg")
|
171
|
+
meg.child = Person.new(:name => "Steve")
|
172
|
+
meg.child.child = Person.new(:name => "Linda")
|
173
|
+
|
174
|
+
doc = @document.new(:person => meg)
|
175
|
+
doc.save
|
176
|
+
|
177
|
+
from_db = @document.find(doc.id)
|
178
|
+
from_db.person.name.should == 'Meg'
|
179
|
+
from_db.person.child.name.should == 'Steve'
|
180
|
+
from_db.person.child.child.name.should == 'Linda'
|
181
|
+
end
|
182
|
+
|
183
|
+
should "allow saving embedded documents in 'many' embedded documents" do
|
184
|
+
@document = Class.new do
|
185
|
+
include MongoMapper::Document
|
186
|
+
|
187
|
+
many :people
|
188
|
+
end
|
189
|
+
|
190
|
+
meg = Person.new(:name => "Meg")
|
191
|
+
sparky = Pet.new(:name => "Sparky", :species => "Dog")
|
192
|
+
koda = Pet.new(:name => "Koda", :species => "Dog")
|
193
|
+
|
194
|
+
doc = @document.new
|
195
|
+
|
196
|
+
meg.pets << sparky
|
197
|
+
meg.pets << koda
|
198
|
+
|
199
|
+
doc.people << meg
|
200
|
+
doc.save
|
201
|
+
|
202
|
+
from_db = @document.find(doc.id)
|
203
|
+
from_db.people.first.name.should == "Meg"
|
204
|
+
from_db.people.first.pets.should_not == []
|
205
|
+
from_db.people.first.pets.first.name.should == "Sparky"
|
206
|
+
from_db.people.first.pets.first.species.should == "Dog"
|
207
|
+
from_db.people.first.pets[1].name.should == "Koda"
|
208
|
+
from_db.people.first.pets[1].species.should == "Dog"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
end
|