mongo_mapper 0.5.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 (69) hide show
  1. data/.gitignore +7 -0
  2. data/LICENSE +20 -0
  3. data/README.rdoc +39 -0
  4. data/Rakefile +87 -0
  5. data/VERSION +1 -0
  6. data/bin/mmconsole +55 -0
  7. data/lib/mongo_mapper.rb +92 -0
  8. data/lib/mongo_mapper/associations.rb +86 -0
  9. data/lib/mongo_mapper/associations/base.rb +83 -0
  10. data/lib/mongo_mapper/associations/belongs_to_polymorphic_proxy.rb +34 -0
  11. data/lib/mongo_mapper/associations/belongs_to_proxy.rb +22 -0
  12. data/lib/mongo_mapper/associations/many_documents_as_proxy.rb +27 -0
  13. data/lib/mongo_mapper/associations/many_documents_proxy.rb +116 -0
  14. data/lib/mongo_mapper/associations/many_embedded_polymorphic_proxy.rb +33 -0
  15. data/lib/mongo_mapper/associations/many_embedded_proxy.rb +67 -0
  16. data/lib/mongo_mapper/associations/many_polymorphic_proxy.rb +11 -0
  17. data/lib/mongo_mapper/associations/many_proxy.rb +6 -0
  18. data/lib/mongo_mapper/associations/proxy.rb +64 -0
  19. data/lib/mongo_mapper/callbacks.rb +106 -0
  20. data/lib/mongo_mapper/document.rb +317 -0
  21. data/lib/mongo_mapper/dynamic_finder.rb +35 -0
  22. data/lib/mongo_mapper/embedded_document.rb +354 -0
  23. data/lib/mongo_mapper/finder_options.rb +94 -0
  24. data/lib/mongo_mapper/key.rb +32 -0
  25. data/lib/mongo_mapper/observing.rb +50 -0
  26. data/lib/mongo_mapper/pagination.rb +51 -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/save_with_validation.rb +19 -0
  30. data/lib/mongo_mapper/serialization.rb +55 -0
  31. data/lib/mongo_mapper/serializers/json_serializer.rb +92 -0
  32. data/lib/mongo_mapper/support.rb +157 -0
  33. data/lib/mongo_mapper/validations.rb +69 -0
  34. data/mongo_mapper.gemspec +156 -0
  35. data/test/NOTE_ON_TESTING +1 -0
  36. data/test/custom_matchers.rb +48 -0
  37. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +54 -0
  38. data/test/functional/associations/test_belongs_to_proxy.rb +46 -0
  39. data/test/functional/associations/test_many_documents_as_proxy.rb +244 -0
  40. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +132 -0
  41. data/test/functional/associations/test_many_embedded_proxy.rb +174 -0
  42. data/test/functional/associations/test_many_polymorphic_proxy.rb +297 -0
  43. data/test/functional/associations/test_many_proxy.rb +331 -0
  44. data/test/functional/test_associations.rb +48 -0
  45. data/test/functional/test_binary.rb +18 -0
  46. data/test/functional/test_callbacks.rb +85 -0
  47. data/test/functional/test_document.rb +951 -0
  48. data/test/functional/test_embedded_document.rb +97 -0
  49. data/test/functional/test_pagination.rb +87 -0
  50. data/test/functional/test_rails_compatibility.rb +30 -0
  51. data/test/functional/test_validations.rb +279 -0
  52. data/test/models.rb +169 -0
  53. data/test/test_helper.rb +29 -0
  54. data/test/unit/serializers/test_json_serializer.rb +189 -0
  55. data/test/unit/test_association_base.rb +144 -0
  56. data/test/unit/test_document.rb +165 -0
  57. data/test/unit/test_dynamic_finder.rb +125 -0
  58. data/test/unit/test_embedded_document.rb +645 -0
  59. data/test/unit/test_finder_options.rb +193 -0
  60. data/test/unit/test_key.rb +163 -0
  61. data/test/unit/test_mongomapper.rb +28 -0
  62. data/test/unit/test_observing.rb +101 -0
  63. data/test/unit/test_pagination.rb +109 -0
  64. data/test/unit/test_rails_compatibility.rb +39 -0
  65. data/test/unit/test_serializations.rb +52 -0
  66. data/test/unit/test_support.rb +272 -0
  67. data/test/unit/test_time_zones.rb +40 -0
  68. data/test/unit/test_validations.rb +503 -0
  69. metadata +204 -0
@@ -0,0 +1,169 @@
1
+ class Post
2
+ include MongoMapper::Document
3
+
4
+ key :title, String
5
+ key :body, String
6
+
7
+ has_many :comments, :as => :commentable, :class_name => 'PostComment'
8
+
9
+ timestamps!
10
+ end
11
+
12
+ class PostComment
13
+ include MongoMapper::Document
14
+
15
+ key :username, String, :default => 'Anonymous'
16
+ key :body, String
17
+
18
+ key :commentable_id, String
19
+ key :commentable_type, String
20
+ belongs_to :commentable, :polymorphic => true
21
+
22
+ timestamps!
23
+ end
24
+
25
+ class Address
26
+ include MongoMapper::EmbeddedDocument
27
+
28
+ key :address, String
29
+ key :city, String
30
+ key :state, String
31
+ key :zip, Integer
32
+ end
33
+
34
+ class Message
35
+ include MongoMapper::Document
36
+
37
+ key :body, String
38
+ key :position, Integer
39
+ key :_type, String
40
+ key :room_id, String
41
+
42
+ belongs_to :room
43
+ end
44
+
45
+ class Answer
46
+ include MongoMapper::Document
47
+
48
+ key :body, String
49
+ end
50
+
51
+ class Enter < Message; end
52
+ class Exit < Message; end
53
+ class Chat < Message; end
54
+
55
+ class Room
56
+ include MongoMapper::Document
57
+
58
+ key :name, String
59
+ many :messages, :polymorphic => true
60
+ end
61
+
62
+ class Project
63
+ include MongoMapper::Document
64
+
65
+ key :name, String
66
+ many :statuses
67
+ many :addresses
68
+ end
69
+
70
+ class Status
71
+ include MongoMapper::Document
72
+
73
+ key :project_id, String
74
+ key :target_id, String
75
+ key :target_type, String
76
+ key :name, String
77
+ key :position, Integer
78
+
79
+ belongs_to :project
80
+ belongs_to :target, :polymorphic => true
81
+ end
82
+
83
+ class RealPerson
84
+ include MongoMapper::Document
85
+
86
+ many :pets
87
+ key :name, String
88
+
89
+ def realname=(n)
90
+ self.name = n
91
+ end
92
+ end
93
+
94
+ class Person
95
+ include MongoMapper::EmbeddedDocument
96
+
97
+ key :name, String
98
+ key :child, Person
99
+
100
+ many :pets
101
+ end
102
+
103
+ class Pet
104
+ include MongoMapper::EmbeddedDocument
105
+
106
+ key :name, String
107
+ key :species, String
108
+ end
109
+
110
+ class Media
111
+ include MongoMapper::EmbeddedDocument
112
+
113
+ key :_type, String
114
+ key :file, String
115
+ end
116
+
117
+ class Video < Media
118
+ key :length, Integer
119
+ end
120
+
121
+ class Image < Media
122
+ key :width, Integer
123
+ key :height, Integer
124
+ end
125
+
126
+ class Music < Media
127
+ key :bitrate, String
128
+ end
129
+
130
+ class Catalog
131
+ include MongoMapper::Document
132
+
133
+ many :medias, :polymorphic => true
134
+ end
135
+
136
+ module TrModels
137
+ class Transport
138
+ include MongoMapper::EmbeddedDocument
139
+
140
+ key :_type, String
141
+ key :license_plate, String
142
+ end
143
+
144
+ class Car < TrModels::Transport
145
+ include MongoMapper::EmbeddedDocument
146
+
147
+ key :model, String
148
+ key :year, Integer
149
+ end
150
+
151
+ class Bus < TrModels::Transport
152
+ include MongoMapper::EmbeddedDocument
153
+
154
+ key :max_passengers, Integer
155
+ end
156
+
157
+ class Ambulance < TrModels::Transport
158
+ include MongoMapper::EmbeddedDocument
159
+
160
+ key :icu, Boolean
161
+ end
162
+
163
+ class Fleet
164
+ include MongoMapper::Document
165
+
166
+ many :transports, :polymorphic => true, :class_name => "TrModels::Transport"
167
+ key :name, String
168
+ end
169
+ end
@@ -0,0 +1,29 @@
1
+ require 'pathname'
2
+ require 'pp'
3
+ require 'rubygems'
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
+ require 'custom_matchers'
12
+
13
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
14
+ dir = (Pathname(__FILE__).dirname + '..' + 'lib').expand_path
15
+ require dir + 'mongo_mapper'
16
+
17
+ class Test::Unit::TestCase
18
+ include CustomMatchers
19
+
20
+ def clear_all_collections
21
+ MongoMapper::Document.descendants.map { |d| d.collection.clear }
22
+ end
23
+ end
24
+
25
+ DefaultDatabase = 'test' unless defined?(DefaultDatabase)
26
+ AlternateDatabase = 'test2' unless defined?(AlternateDatabase)
27
+
28
+ MongoMapper.connection = Mongo::Connection.new('127.0.0.1', 27017)
29
+ MongoMapper.database = DefaultDatabase
@@ -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,144 @@
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
+ context "foreign_key" do
82
+ should "default to assocation_name_id" do
83
+ base = Base.new(:belongs_to, :foo)
84
+ base.foreign_key.should == 'foo_id'
85
+ end
86
+
87
+ should "be overridable with :foreign_key option" do
88
+ base = Base.new(:belongs_to, :foo, :foreign_key => 'foobar_id')
89
+ base.foreign_key.should == 'foobar_id'
90
+ end
91
+ end
92
+
93
+ should "have ivar that is association name" do
94
+ Base.new(:belongs_to, :foo).ivar.should == '@_foo'
95
+ end
96
+
97
+ context "embeddable?" do
98
+ should "be true if class is embeddable" do
99
+ base = Base.new(:many, :medias)
100
+ base.embeddable?.should be_true
101
+ end
102
+
103
+ should "be false if class is not embeddable" do
104
+ base = Base.new(:many, :statuses)
105
+ base.embeddable?.should be_false
106
+
107
+ base = Base.new(:belongs_to, :project)
108
+ base.embeddable?.should be_false
109
+ end
110
+ end
111
+
112
+ context "proxy_class" do
113
+ should "be ManyProxy for many" do
114
+ base = Base.new(:many, :statuses)
115
+ base.proxy_class.should == ManyProxy
116
+ end
117
+
118
+ should "be ManyPolymorphicProxy for polymorphic many" do
119
+ base = Base.new(:many, :messages, :polymorphic => true)
120
+ base.proxy_class.should == ManyPolymorphicProxy
121
+ end
122
+
123
+ should "be ManyEmbeddedProxy for many embedded" do
124
+ base = Base.new(:many, :medias)
125
+ base.proxy_class.should == ManyEmbeddedProxy
126
+ end
127
+
128
+ should "be ManyEmbeddedPolymorphicProxy for polymorphic many embedded" do
129
+ base = Base.new(:many, :medias, :polymorphic => true)
130
+ base.proxy_class.should == ManyEmbeddedPolymorphicProxy
131
+ end
132
+
133
+ should "be BelongsToProxy for belongs_to" do
134
+ base = Base.new(:belongs_to, :project)
135
+ base.proxy_class.should == BelongsToProxy
136
+ end
137
+
138
+ should "be BelongsToPolymorphicProxy for polymorphic belongs_to" do
139
+ base = Base.new(:belongs_to, :target, :polymorphic => true)
140
+ base.proxy_class.should == BelongsToPolymorphicProxy
141
+ end
142
+ end
143
+
144
+ end