mongo_mapper 0.11.2 → 0.12.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.
@@ -4,21 +4,19 @@ require 'pp'
4
4
 
5
5
  MongoMapper.database = 'testing'
6
6
 
7
+ MongoMapper::Plugins::IdentityMap.enabled = true
8
+
7
9
  class User
8
10
  include MongoMapper::Document
9
- plugin MongoMapper::Plugins::IdentityMap
10
11
 
11
12
  key :name, String
12
13
  end
13
14
  User.delete_all
14
15
 
15
- user = User.create(:name => 'John')
16
-
17
16
  # User gets added to map on save
18
- pp User.identity_map[user.id]
17
+ user = User.create(:name => 'John')
19
18
 
20
19
  # Does not matter how you find user, it is always the same object until the identity map is cleared
21
- puts "#{User.identity_map[user.id].object_id} == #{user.object_id}"
22
20
  puts "#{User.find(user.id).object_id} == #{user.object_id}"
23
21
  puts "#{User.all[0].object_id} == #{user.object_id}"
24
22
 
@@ -28,6 +26,3 @@ puts "#{User.find(user.id).object_id} != #{user.object_id}"
28
26
  # User gets removed from map on destroy
29
27
  user = User.create
30
28
  user.destroy
31
- puts "Should be nil: " + User.identity_map[user.id].inspect
32
-
33
-
@@ -1,2 +1,2 @@
1
1
  # Using the following will turn on identity map for all models
2
- MongoMapper::Document.plugin(MongoMapper::Plugins::IdentityMap)
2
+ MongoMapper::Plugins::IdentityMap.enabled = true
@@ -70,7 +70,11 @@ module MongoMapper
70
70
  end
71
71
 
72
72
  MongoMapper.connection = if env['hosts']
73
- Mongo::ReplSetConnection.new( *env['hosts'].push(options) )
73
+ if env['hosts'].first.is_a?(String)
74
+ Mongo::ReplSetConnection.new( env['hosts'], options )
75
+ else
76
+ Mongo::ReplSetConnection.new( *env['hosts'].push(options) )
77
+ end
74
78
  else
75
79
  Mongo::Connection.new(env['host'], env['port'], options)
76
80
  end
@@ -33,6 +33,7 @@ module MongoMapper
33
33
  include Plugins::Validations
34
34
  include Plugins::EmbeddedCallbacks
35
35
  include Plugins::Callbacks # for now callbacks needs to be after validations
36
+ include Plugins::IdentityMap
36
37
 
37
38
  included do
38
39
  extend Plugins
@@ -1,16 +1,42 @@
1
1
  module MongoMapper
2
2
  module Middleware
3
+ # Usage:
4
+ #
5
+ # config.middleware.insert_after \
6
+ # ActionDispatch::Callbacks,
7
+ # MongoMapper::Middleware::IdentityMap
8
+ #
9
+ # You have to insert after callbacks so the entire request is wrapped.
3
10
  class IdentityMap
11
+ class Body
12
+ def initialize(target, original)
13
+ @target = target
14
+ @original = original
15
+ end
16
+
17
+ def each(&block)
18
+ @target.each(&block)
19
+ end
20
+
21
+ def close
22
+ @target.close if @target.respond_to?(:close)
23
+ ensure
24
+ MongoMapper::Plugins::IdentityMap.enabled = @original
25
+ MongoMapper::Plugins::IdentityMap.clear
26
+ end
27
+ end
28
+
4
29
  def initialize(app)
5
30
  @app = app
6
31
  end
7
32
 
8
33
  def call(env)
9
34
  MongoMapper::Plugins::IdentityMap.clear
10
- @app.call(env)
11
- ensure
12
- MongoMapper::Plugins::IdentityMap.clear
35
+ enabled = MongoMapper::Plugins::IdentityMap.enabled
36
+ MongoMapper::Plugins::IdentityMap.enabled = true
37
+ status, headers, body = @app.call(env)
38
+ [status, headers, Body.new(body, enabled)]
13
39
  end
14
40
  end
15
41
  end
16
- end
42
+ end
@@ -6,30 +6,55 @@ module MongoMapper
6
6
  module IdentityMap
7
7
  extend ActiveSupport::Concern
8
8
 
9
- def self.models
10
- @models ||= Set.new
9
+ def self.enabled=(flag)
10
+ Thread.current[:mongo_mapper_identity_map_enabled] = flag
11
+ end
12
+
13
+ def self.enabled
14
+ Thread.current[:mongo_mapper_identity_map_enabled]
15
+ end
16
+
17
+ def self.enabled?
18
+ enabled == true
19
+ end
20
+
21
+ def self.repository
22
+ Thread.current[:mongo_mapper_identity_map] ||= {}
11
23
  end
12
24
 
13
25
  def self.clear
14
- models.each { |m| m.identity_map.clear }
26
+ repository.clear
15
27
  end
16
28
 
17
- included do
18
- IdentityMap.models << self
29
+ def self.include?(document)
30
+ repository.key?(IdentityMap.key(document.class, document._id))
19
31
  end
20
32
 
21
- module ClassMethods
22
- def inherited(descendant)
23
- descendant.identity_map = identity_map
24
- super
25
- end
33
+ def self.key(model, id)
34
+ "#{model.single_collection_root.name}:#{id}"
35
+ end
26
36
 
27
- def identity_map
28
- @identity_map ||= {}
29
- end
37
+ def self.use
38
+ old, self.enabled = enabled, true
30
39
 
31
- def identity_map=(v)
32
- @identity_map = v
40
+ yield if block_given?
41
+ ensure
42
+ self.enabled = old
43
+ clear
44
+ end
45
+
46
+ def self.without
47
+ old, self.enabled = enabled, false
48
+
49
+ yield if block_given?
50
+ ensure
51
+ self.enabled = old
52
+ end
53
+
54
+ module ClassMethods
55
+ # Private - Looks for a document in the identity map
56
+ def get_from_identity_map(id)
57
+ IdentityMap.repository[IdentityMap.key(self, id)]
33
58
  end
34
59
 
35
60
  module IdentityMapQueryMethods
@@ -43,14 +68,22 @@ module MongoMapper
43
68
  def find_one(opts={})
44
69
  query = clone.amend(opts)
45
70
 
46
- if model.identity_map_on? && query.simple? && model.identity_map[query[:_id]]
47
- model.identity_map[query[:_id]]
71
+ if IdentityMap.enabled? && query.simple? && (document = model.get_from_identity_map(query[:_id]))
72
+ document
48
73
  else
49
74
  super.tap do |doc|
50
75
  model.remove_documents_from_map(doc) if query.fields?
51
76
  end
52
77
  end
53
78
  end
79
+
80
+ def find_each(opts={}, &block)
81
+ query = clone.amend(opts)
82
+ super(opts) do |doc|
83
+ model.remove_documents_from_map(doc) if query.fields?
84
+ block.call(doc) unless block.nil?
85
+ end
86
+ end
54
87
  end
55
88
 
56
89
  def query(opts={})
@@ -59,70 +92,44 @@ module MongoMapper
59
92
 
60
93
  def remove_documents_from_map(*documents)
61
94
  documents.flatten.compact.each do |document|
62
- identity_map.delete(document['_id'])
95
+ document.remove_from_identity_map
63
96
  end
64
97
  end
65
98
 
66
99
  def load(attrs)
67
100
  return nil if attrs.nil?
68
- document = identity_map[attrs['_id']]
101
+ document = get_from_identity_map(attrs['_id'])
69
102
 
70
- if document.nil? || identity_map_off?
103
+ if document.nil?
71
104
  document = super
72
- identity_map[document._id] = document if identity_map_on?
105
+ document.add_to_identity_map
73
106
  end
74
107
 
75
108
  document
76
109
  end
110
+ end
77
111
 
78
- def identity_map_status
79
- defined?(@identity_map_status) ? @identity_map_status : true
80
- end
81
-
82
- def identity_map_on
83
- @identity_map_status = true
84
- end
85
-
86
- def identity_map_off
87
- @identity_map_status = false
88
- end
89
-
90
- def identity_map_on?
91
- identity_map_status
92
- end
93
-
94
- def identity_map_off?
95
- !identity_map_on?
96
- end
97
-
98
- def without_identity_map(&block)
99
- identity_map_off
100
- yield
101
- ensure
102
- identity_map_on
103
- end
104
-
105
- private
106
- def selecting_fields?(options)
107
- !options[:fields].nil?
108
- end
112
+ def save(*args)
113
+ super.tap { |result| add_to_identity_map if result }
109
114
  end
110
115
 
111
- def identity_map
112
- self.class.identity_map
116
+ def delete
117
+ super.tap { remove_from_identity_map }
113
118
  end
114
119
 
115
- def save(*args)
116
- if result = super
117
- identity_map[_id] = self if self.class.identity_map_on?
120
+ def add_to_identity_map
121
+ if IdentityMap.enabled?
122
+ key = IdentityMap.key(self.class, _id)
123
+ IdentityMap.repository[key] = self
118
124
  end
119
- result
120
125
  end
121
126
 
122
- def delete
123
- identity_map.delete(_id) if self.class.identity_map_on?
124
- super
127
+ def remove_from_identity_map
128
+ if IdentityMap.enabled?
129
+ key = IdentityMap.key(self.class, _id)
130
+ IdentityMap.repository.delete(key)
131
+ end
125
132
  end
126
133
  end
127
134
  end
128
- end
135
+ end
@@ -13,9 +13,30 @@ module MongoMapper
13
13
  key :_type, String unless key?(:_type)
14
14
  subclass.instance_variable_set("@single_collection_inherited", true)
15
15
  subclass.set_collection_name(collection_name) unless subclass.embeddable?
16
+ subclass.single_collection_parent = self
16
17
  super
17
18
  end
18
19
 
20
+ def single_collection_root
21
+ parent = single_collection_parent || self
22
+ root = parent
23
+
24
+ while parent
25
+ parent = parent.single_collection_parent
26
+ root = parent unless parent.nil?
27
+ end
28
+
29
+ root
30
+ end
31
+
32
+ def single_collection_parent
33
+ @single_collection_parent
34
+ end
35
+
36
+ def single_collection_parent=(parent)
37
+ @single_collection_parent = parent
38
+ end
39
+
19
40
  def single_collection_inherited?
20
41
  @single_collection_inherited == true
21
42
  end
@@ -28,21 +28,5 @@ module MongoMapper
28
28
  MongoMapper.setup(config, Rails.env, :logger => Rails.logger)
29
29
  end
30
30
  end
31
-
32
- # Clear the identity map after each request
33
- initializer "mongo_mapper.clear_identity_map" do |app|
34
- app.config.middleware.use 'MongoMapper::Middleware::IdentityMap'
35
- end
36
-
37
- initializer "mongo_mapper.prepare_dispatcher" do |app|
38
- # See http://groups.google.com/group/mongomapper/browse_thread/thread/68f62e8eda43b43a/4841dba76938290c
39
- # to_prepare is called before each request in development mode and the first request in production.
40
-
41
- ActionDispatch::Callbacks.to_prepare do
42
- unless app.config.cache_classes
43
- MongoMapper::Plugins::IdentityMap.models.clear
44
- end
45
- end
46
- end
47
31
  end
48
32
  end
@@ -1,4 +1,4 @@
1
1
  # encoding: UTF-8
2
2
  module MongoMapper
3
- Version = '0.11.2'
3
+ Version = '0.12.0'
4
4
  end
@@ -3,15 +3,13 @@ require 'test_helper'
3
3
  class IdentityMapTest < Test::Unit::TestCase
4
4
  def assert_in_map(*resources)
5
5
  [resources].flatten.each do |resource|
6
- resource.identity_map.keys.should include(resource._id)
7
- mapped_resource = resource.identity_map[resource._id]
8
- resource.should equal(mapped_resource)
6
+ MongoMapper::Plugins::IdentityMap.include?(resource).should be_true
9
7
  end
10
8
  end
11
9
 
12
10
  def assert_not_in_map(*resources)
13
11
  [resources].flatten.each do |resource|
14
- resource.identity_map.keys.should_not include(resource._id)
12
+ MongoMapper::Plugins::IdentityMap.include?(resource).should be_false
15
13
  end
16
14
  end
17
15
 
@@ -24,19 +22,24 @@ class IdentityMapTest < Test::Unit::TestCase
24
22
  Mongo::Collection.any_instance.expects(:find_one).once.returns({})
25
23
  end
26
24
 
25
+ def clear_identity_map
26
+ MongoMapper::Plugins::IdentityMap.clear
27
+ end
28
+
29
+ should "default identity map to off" do
30
+ MongoMapper::Plugins::IdentityMap.enabled?.should be_false
31
+ end
32
+
27
33
  context "Document" do
28
34
  setup do
29
- MongoMapper::Plugins::IdentityMap.models.clear
35
+ @original_identity_map_enabled = MongoMapper::Plugins::IdentityMap.enabled
36
+ MongoMapper::Plugins::IdentityMap.enabled = true
30
37
 
31
38
  @person_class = Doc('Person') do
32
- plugin MongoMapper::Plugins::IdentityMap
33
-
34
39
  key :name, String
35
40
  end
36
41
 
37
42
  @post_class = Doc('Post') do
38
- plugin MongoMapper::Plugins::IdentityMap
39
-
40
43
  key :title, String
41
44
  key :person_id, ObjectId
42
45
  end
@@ -44,58 +47,80 @@ class IdentityMapTest < Test::Unit::TestCase
44
47
  @post_class.belongs_to :person, :class => @person_class
45
48
  @person_class.many :posts, :class => @post_class
46
49
 
47
- @post_class.identity_map_on
48
- @person_class.identity_map_on
49
- MongoMapper::Plugins::IdentityMap.clear
50
+ clear_identity_map
50
51
  end
51
52
 
52
- should "track identity mapped models" do
53
- MongoMapper::Plugins::IdentityMap.models.should == [@person_class, @post_class].to_set
53
+ teardown do
54
+ MongoMapper::Plugins::IdentityMap.enabled = @original_identity_map_enabled
54
55
  end
55
56
 
56
57
  should "be able to clear the map of all models" do
57
58
  person = @person_class.create(:name => 'John')
58
59
  post = @post_class.create(:title => 'IM 4eva')
59
- assert_in_map(person, post)
60
-
61
- MongoMapper::Plugins::IdentityMap.clear
62
60
 
61
+ assert_in_map(person, post)
62
+ clear_identity_map
63
63
  assert_not_in_map(person, post)
64
64
 
65
- [@person_class, @post_class].each { |klass| klass.identity_map.should == {} }
65
+ MongoMapper::Plugins::IdentityMap.repository.should be_empty
66
66
  end
67
67
 
68
- context "IM on off status" do
69
- teardown do
70
- @post_class.identity_map_on
71
- @person_class.identity_map_on
68
+ context ".use" do
69
+ setup do
70
+ @person = @person_class.create
71
+ clear_identity_map
72
72
  end
73
73
 
74
- should "default identity map status to on" do
75
- Doc { plugin MongoMapper::Plugins::IdentityMap }.identity_map_status.should be_true
74
+ should "use the identity map" do
75
+ MongoMapper::Plugins::IdentityMap.enabled = false
76
+ MongoMapper::Plugins::IdentityMap.use do
77
+ @person_class.find(@person.id).should equal(@person_class.find(@person.id))
78
+ end
79
+ end
80
+
81
+ should "clear the map" do
82
+ MongoMapper::Plugins::IdentityMap.enabled = false
83
+ MongoMapper::Plugins::IdentityMap.repository['hello'] = 'world'
84
+ MongoMapper::Plugins::IdentityMap.use do
85
+ @person_class.find(@person.id)
86
+ end
87
+ MongoMapper::Plugins::IdentityMap.repository.empty?.should be_true
76
88
  end
77
89
 
78
- should "be true if on" do
79
- @post_class.identity_map_on
80
- @post_class.should be_identity_map_on
81
- @post_class.should_not be_identity_map_off
90
+ should "set enabled back to original status" do
91
+ MongoMapper::Plugins::IdentityMap.enabled = false
92
+ MongoMapper::Plugins::IdentityMap.enabled?.should be_false
93
+ MongoMapper::Plugins::IdentityMap.use do
94
+ MongoMapper::Plugins::IdentityMap.enabled?.should be_true
95
+ end
96
+ MongoMapper::Plugins::IdentityMap.enabled?.should be_false
82
97
  end
98
+ end
83
99
 
84
- should "be false if off" do
85
- @post_class.identity_map_off
86
- @post_class.should be_identity_map_off
87
- @post_class.should_not be_identity_map_on
100
+ context ".without" do
101
+ setup do
102
+ @person = @person_class.create
103
+ clear_identity_map
88
104
  end
89
105
 
90
- should "not share with other classes" do
91
- @post_class.identity_map_off
92
- @person_class.identity_map_on
93
- @post_class.identity_map_status.should_not == @person_class.identity_map_status
106
+ should "skip the map" do
107
+ MongoMapper::Plugins::IdentityMap.without do
108
+ @person_class.find(@person.id).should_not equal(@person_class.find(@person.id))
109
+ end
110
+ end
111
+
112
+ should "set enabled back to original value" do
113
+ MongoMapper::Plugins::IdentityMap.enabled = true
114
+ MongoMapper::Plugins::IdentityMap.enabled?.should be_true
115
+ MongoMapper::Plugins::IdentityMap.without do
116
+ MongoMapper::Plugins::IdentityMap.enabled?.should be_false
117
+ end
118
+ MongoMapper::Plugins::IdentityMap.enabled?.should be_true
94
119
  end
95
120
  end
96
121
 
97
122
  should "default identity map to hash" do
98
- Doc { plugin MongoMapper::Plugins::IdentityMap }.identity_map.should == {}
123
+ MongoMapper::Plugins::IdentityMap.repository.should == {}
99
124
  end
100
125
 
101
126
  should "add key to map when saved" do
@@ -107,35 +132,35 @@ class IdentityMapTest < Test::Unit::TestCase
107
132
 
108
133
  should "allow saving with options" do
109
134
  person = @person_class.new
110
- assert_nothing_raised do
111
- person.save(:validate => false).should be_true
112
- end
135
+ assert_not_in_map(person)
136
+ person.save(:validate => false).should be_true
137
+ assert_in_map(person)
113
138
  end
114
139
 
115
140
  should "remove key from map when deleted" do
141
+ person = @person_class.create(:name => 'Fred')
142
+ assert_in_map(person)
143
+ person.delete
144
+ assert_not_in_map(person)
145
+ end
146
+
147
+ should "remove key from map when destroyed" do
116
148
  person = @person_class.create(:name => 'Fred')
117
149
  assert_in_map(person)
118
150
  person.destroy
119
151
  assert_not_in_map(person)
120
152
  end
121
153
 
122
- context "reload" do
154
+ context "#reload" do
123
155
  setup do
124
156
  @person = @person_class.create(:name => 'Fred')
125
157
  end
126
158
 
127
- should "remove object from identity and re-query" do
159
+ should "re-query the object" do
128
160
  assert_in_map(@person)
129
161
  expects_one_query
130
162
  @person.reload
131
163
  end
132
-
133
- should "add object back into map" do
134
- assert_in_map(@person)
135
- before_reload = @person
136
- @person.reload.should equal(before_reload)
137
- assert_in_map(@person)
138
- end
139
164
  end
140
165
 
141
166
  context "#load" do
@@ -144,23 +169,26 @@ class IdentityMapTest < Test::Unit::TestCase
144
169
  end
145
170
 
146
171
  should "add document to map" do
147
- loaded = @person_class.load({'_id' => @id, 'name' => 'Frank'})
172
+ loaded = @person_class.load('_id' => @id, 'name' => 'Frank')
148
173
  assert_in_map(loaded)
149
174
  end
150
175
 
151
176
  should "return document if already in map" do
152
- first_load = @person_class.load({'_id' => @id, 'name' => 'Frank'})
153
- @person_class.identity_map.expects(:[]=).never
154
- second_load = @person_class.load({'_id' => @id, 'name' => 'Frank'})
177
+ first_load = @person_class.load('_id' => @id, 'name' => 'Frank')
178
+ second_load = @person_class.load('_id' => @id, 'name' => 'Frank')
155
179
  first_load.should equal(second_load)
156
180
  end
157
181
  end
158
182
 
159
183
  context "#find (with one id)" do
184
+ should "return nil if not found " do
185
+ @person_class.find(1234).should be_nil
186
+ end
187
+
160
188
  context "for object not in map" do
161
189
  setup do
162
190
  @person = @person_class.create(:name => 'Fred')
163
- @person_class.identity_map.clear
191
+ clear_identity_map
164
192
  end
165
193
 
166
194
  should "query the database" do
@@ -173,10 +201,6 @@ class IdentityMapTest < Test::Unit::TestCase
173
201
  found_person = @person_class.find(@person.id)
174
202
  assert_in_map(found_person)
175
203
  end
176
-
177
- should "return nil if not found " do
178
- @person_class.find(1234).should be_nil
179
- end
180
204
  end
181
205
 
182
206
  context "for object in map" do
@@ -218,8 +242,8 @@ class IdentityMapTest < Test::Unit::TestCase
218
242
 
219
243
  should "return exact object" do
220
244
  assert_in_map(@post1)
221
- @person.posts.find(@post1.id)
222
- assert_in_map(@post1)
245
+ post = @person.posts.find(@post1.id)
246
+ post.should equal(@post1)
223
247
  end
224
248
 
225
249
  should "return nil if not found " do
@@ -232,7 +256,7 @@ class IdentityMapTest < Test::Unit::TestCase
232
256
  person1 = @person_class.create(:name => 'Fred')
233
257
  person2 = @person_class.create(:name => 'Bill')
234
258
  person3 = @person_class.create(:name => 'Jesse')
235
- @person_class.identity_map.clear
259
+ clear_identity_map
236
260
 
237
261
  people = @person_class.find(person1.id, person2.id, person3.id)
238
262
  assert_in_map(people)
@@ -240,7 +264,7 @@ class IdentityMapTest < Test::Unit::TestCase
240
264
 
241
265
  should "add missing documents to map and return existing ones" do
242
266
  person1 = @person_class.create(:name => 'Fred')
243
- @person_class.identity_map.clear
267
+ clear_identity_map
244
268
  person2 = @person_class.create(:name => 'Bill')
245
269
  person3 = @person_class.create(:name => 'Jesse')
246
270
 
@@ -248,6 +272,7 @@ class IdentityMapTest < Test::Unit::TestCase
248
272
  assert_in_map(person2, person3)
249
273
 
250
274
  people = @person_class.find(person1.id, person2.id, person3.id)
275
+
251
276
  assert_in_map(people.first) # making sure one that wasn't mapped now is
252
277
  assert_in_map(person2, person3)
253
278
  end
@@ -257,7 +282,7 @@ class IdentityMapTest < Test::Unit::TestCase
257
282
  context "for object not in map" do
258
283
  setup do
259
284
  @person = @person_class.create(:name => 'Fred')
260
- @person_class.identity_map.clear
285
+ clear_identity_map
261
286
  end
262
287
 
263
288
  should "query the database" do
@@ -299,7 +324,7 @@ class IdentityMapTest < Test::Unit::TestCase
299
324
  person1 = @person_class.create(:name => 'Fred')
300
325
  person2 = @person_class.create(:name => 'Bill')
301
326
  person3 = @person_class.create(:name => 'Jesse')
302
- @person_class.identity_map.clear
327
+ clear_identity_map
303
328
 
304
329
  people = @person_class.all(:_id => [person1.id, person2.id, person3.id])
305
330
  assert_in_map(people)
@@ -307,7 +332,7 @@ class IdentityMapTest < Test::Unit::TestCase
307
332
 
308
333
  should "add missing documents to map and return existing ones" do
309
334
  person1 = @person_class.create(:name => 'Fred')
310
- @person_class.identity_map.clear
335
+ clear_identity_map
311
336
  person2 = @person_class.create(:name => 'Bill')
312
337
  person3 = @person_class.create(:name => 'Jesse')
313
338
 
@@ -334,7 +359,7 @@ class IdentityMapTest < Test::Unit::TestCase
334
359
  context "querying and selecting certain fields" do
335
360
  setup do
336
361
  @person = @person_class.create(:name => 'Bill')
337
- @person_class.identity_map.clear
362
+ clear_identity_map
338
363
  end
339
364
 
340
365
  should "not add to map" do
@@ -347,6 +372,13 @@ class IdentityMapTest < Test::Unit::TestCase
347
372
  assert_not_in_map(@person)
348
373
  end
349
374
 
375
+ should "not add to map using where and each" do
376
+ @person_class.where(:id => @person.id).each{|_|}
377
+ assert_in_map(@person)
378
+ @person_class.where(:id => @person.id).only(:id).each{|_|}
379
+ assert_not_in_map(@person)
380
+ end
381
+
350
382
  should "return nil if not found" do
351
383
  @person_class.fields(:name).find(BSON::ObjectId.new).should be_nil
352
384
  end
@@ -356,7 +388,6 @@ class IdentityMapTest < Test::Unit::TestCase
356
388
  setup do
357
389
  class ::Item
358
390
  include MongoMapper::Document
359
- plugin MongoMapper::Plugins::IdentityMap
360
391
 
361
392
  key :title, String
362
393
  key :parent_id, ObjectId
@@ -375,21 +406,15 @@ class IdentityMapTest < Test::Unit::TestCase
375
406
  end
376
407
 
377
408
  teardown do
378
- Object.send :remove_const, 'Item' if defined?(::Item)
409
+ Object.send :remove_const, 'Item' if defined?(::Item)
379
410
  Object.send :remove_const, 'Blog' if defined?(::Blog)
380
411
  Object.send :remove_const, 'BlogPost' if defined?(::BlogPost)
381
412
  end
382
413
 
383
- should "share the same identity map" do
384
- blog = Blog.create(:title => 'Jill')
385
- assert_in_map(blog)
386
- Item.identity_map.should equal(Blog.identity_map)
387
- end
388
-
389
414
  should "not query when finding by _id and _type" do
390
415
  blog = Blog.create(:title => 'Blog')
391
416
  post = BlogPost.create(:title => 'Mongo Rocks', :blog => blog)
392
- Item.identity_map.clear
417
+ clear_identity_map
393
418
 
394
419
  blog = Item.find(blog.id)
395
420
  post = Item.find(post.id)
@@ -429,85 +454,5 @@ class IdentityMapTest < Test::Unit::TestCase
429
454
  blog.parent.should equal(root)
430
455
  end
431
456
  end
432
-
433
- context "without identity map" do
434
- should "not add to map on save" do
435
- @post_class.without_identity_map do
436
- post = @post_class.create(:title => 'Bill')
437
- assert_not_in_map(post)
438
- end
439
- end
440
-
441
- should "not remove from map on delete" do
442
- post = @post_class.create(:title => 'Bill')
443
- assert_in_map(post)
444
-
445
- @post_class.without_identity_map do
446
- post.destroy
447
- end
448
-
449
- assert_in_map(post)
450
- end
451
-
452
- should "not add to map when loading" do
453
- @post_class.without_identity_map do
454
- post = @post_class.load({'_id' => BSON::ObjectId.new, 'title' => 'Awesome!'})
455
- assert_not_in_map(post)
456
- end
457
- end
458
-
459
- should "not load from map when loading" do
460
- post = @post_class.create(:title => 'Awesome!')
461
-
462
- @post_class.without_identity_map do
463
- loaded = @post_class.load('_id' => post._id, 'title' => 'Awesome!')
464
- loaded.should_not equal(post)
465
- end
466
- end
467
-
468
- should "not load attributes from map when finding" do
469
- post = @post_class.create(:title => 'Awesome!')
470
- post.title = 'Temporary'
471
- @post_class.without_identity_map do
472
- @post_class.find(post.id).title.should == 'Awesome!'
473
- end
474
- end
475
-
476
- context "all" do
477
- should "not add to map" do
478
- @post_class.without_identity_map do
479
- post1 = @post_class.create(:title => 'Foo')
480
- post2 = @post_class.create(:title => 'Bar')
481
- @post_class.identity_map.clear
482
-
483
- assert_not_in_map(@post_class.all)
484
- end
485
- end
486
- end
487
-
488
- context "first" do
489
- should "not add to map" do
490
- @post_class.without_identity_map do
491
- post1 = @post_class.create(:title => 'Foo')
492
- post2 = @post_class.create(:title => 'Bar')
493
- @post_class.identity_map.clear
494
-
495
- assert_not_in_map(@post_class.first)
496
- end
497
- end
498
- end
499
-
500
- context "last" do
501
- should "not add to map" do
502
- @post_class.without_identity_map do
503
- post1 = @post_class.create(:title => 'Foo')
504
- post2 = @post_class.create(:title => 'Bar')
505
- @post_class.identity_map.clear
506
-
507
- assert_not_in_map(@post_class.last(:order => 'title'))
508
- end
509
- end
510
- end
511
- end
512
457
  end
513
- end
458
+ end
@@ -36,6 +36,22 @@ class SciTest < Test::Unit::TestCase
36
36
  DocDaughter.collection.name.should == DocParent.collection.name
37
37
  end
38
38
 
39
+ should "know single_collection_parent" do
40
+ DocParent.single_collection_parent.should be_nil
41
+ DocDaughter.single_collection_parent.should == DocParent
42
+ DocSon.single_collection_parent.should == DocParent
43
+ DocGrandSon.single_collection_parent.should == DocSon
44
+ DocGrandGrandSon.single_collection_parent.should == DocGrandSon
45
+ end
46
+
47
+ should "know single_collection_root" do
48
+ DocParent.single_collection_root.should == DocParent
49
+ DocDaughter.single_collection_root.should == DocParent
50
+ DocSon.single_collection_root.should == DocParent
51
+ DocGrandSon.single_collection_root.should == DocParent
52
+ DocGrandGrandSon.single_collection_root.should == DocParent
53
+ end
54
+
39
55
  context ".single_collection_inherited?" do
40
56
  should "be false if has not inherited" do
41
57
  DocParent.should_not be_single_collection_inherited
@@ -7,28 +7,126 @@ class IdentityMapMiddlewareTest < Test::Unit::TestCase
7
7
  def app
8
8
  @app ||= Rack::Builder.new do
9
9
  use MongoMapper::Middleware::IdentityMap
10
+
10
11
  map "/" do
11
12
  run lambda {|env| [200, {}, []] }
12
13
  end
14
+
13
15
  map "/fail" do
14
16
  run lambda {|env| raise "FAIL!" }
15
17
  end
16
18
  end.to_app
17
19
  end
18
20
 
19
- context "with a successful request" do
20
- should "clear the identity map" do
21
- MongoMapper::Plugins::IdentityMap.expects(:clear).twice
22
- get '/'
21
+ context "" do
22
+ setup do
23
+ @enabled = MongoMapper::Plugins::IdentityMap.enabled
24
+ MongoMapper::Plugins::IdentityMap.enabled = false
23
25
  end
24
- end
25
26
 
26
- context "when the request raises an error" do
27
- should "clear the identity map" do
28
- MongoMapper::Plugins::IdentityMap.expects(:clear).twice
29
- get '/fail' rescue nil
27
+ teardown do
28
+ MongoMapper::Plugins::IdentityMap.enabled = @enabled
30
29
  end
31
- end
32
30
 
31
+ should "delegate" do
32
+ called = false
33
+ mw = MongoMapper::Middleware::IdentityMap.new lambda { |env|
34
+ called = true
35
+ [200, {}, nil]
36
+ }
37
+ mw.call({})
38
+ called.should be_true
39
+ end
40
+
41
+ should "enable identity map during delegation" do
42
+ mw = MongoMapper::Middleware::IdentityMap.new lambda { |env|
43
+ MongoMapper::Plugins::IdentityMap.should be_enabled
44
+ [200, {}, nil]
45
+ }
46
+ mw.call({})
47
+ end
48
+
49
+ class Enum < Struct.new(:iter)
50
+ def each(&b)
51
+ iter.call(&b)
52
+ end
53
+ end
54
+
55
+ should "enable IM for body each" do
56
+ mw = MongoMapper::Middleware::IdentityMap.new lambda { |env|
57
+ [200, {}, Enum.new(lambda { |&b|
58
+ MongoMapper::Plugins::IdentityMap.should be_enabled
59
+ b.call "hello"
60
+ })]
61
+ }
62
+ body = mw.call({}).last
63
+ body.each { |x| x.should eql('hello') }
64
+ end
65
+
66
+ should "disable IM after body close" do
67
+ mw = MongoMapper::Middleware::IdentityMap.new lambda { |env| [200, {}, []] }
68
+ body = mw.call({}).last
69
+ MongoMapper::Plugins::IdentityMap.should be_enabled
70
+ body.close
71
+ MongoMapper::Plugins::IdentityMap.should_not be_enabled
72
+ end
73
+
74
+ should "clear IM after body close" do
75
+ mw = MongoMapper::Middleware::IdentityMap.new lambda { |env| [200, {}, []] }
76
+ body = mw.call({}).last
77
+
78
+ MongoMapper::Plugins::IdentityMap.repository['hello'] = 'world'
79
+ MongoMapper::Plugins::IdentityMap.repository.should_not be_empty
33
80
 
81
+ body.close
82
+
83
+ MongoMapper::Plugins::IdentityMap.repository.should be_empty
84
+ end
85
+
86
+ context "with a successful request" do
87
+ should "clear the identity map" do
88
+ MongoMapper::Plugins::IdentityMap.expects(:clear).twice
89
+ get '/'
90
+ end
91
+ end
92
+
93
+ context "when the request raises an error" do
94
+ should "clear the identity map" do
95
+ MongoMapper::Plugins::IdentityMap.expects(:clear).once
96
+ get '/fail' rescue nil
97
+ end
98
+ end
99
+ end
34
100
  end
101
+
102
+ # class IdentityMapMiddlewareTest < Test::Unit::TestCase
103
+ # include Rack::Test::Methods
104
+
105
+ # def app
106
+ # @app ||= Rack::Builder.new do
107
+ # use MongoMapper::Middleware::IdentityMap
108
+ # map "/" do
109
+ # run lambda {|env| [200, {}, []] }
110
+ # end
111
+ # map "/fail" do
112
+ # run lambda {|env| raise "FAIL!" }
113
+ # end
114
+ # end.to_app
115
+ # end
116
+
117
+ # context "with a successful request" do
118
+ # should "clear the identity map" do
119
+ # MongoMapper::Plugins::IdentityMap.expects(:clear).twice
120
+ # get '/'
121
+ # end
122
+ # end
123
+
124
+ # context "when the request raises an error" do
125
+ # should "clear the identity map" do
126
+ # MongoMapper::Plugins::IdentityMap.expects(:clear).twice
127
+ # get '/fail' rescue nil
128
+ # end
129
+ # end
130
+
131
+
132
+ # end
@@ -116,7 +116,7 @@ class MongoMapperTest < Test::Unit::TestCase
116
116
  assert_raises(MongoMapper::InvalidScheme) { MongoMapper.connect('development') }
117
117
  end
118
118
 
119
- should "create a replica set connection if config contains multiple hosts" do
119
+ should "create a replica set connection if config contains multiple hosts in the old format" do
120
120
  MongoMapper.config = {
121
121
  'development' => {
122
122
  'hosts' => [ ['127.0.0.1', 27017], ['localhost', 27017] ],
@@ -129,6 +129,20 @@ class MongoMapperTest < Test::Unit::TestCase
129
129
  Mongo::DB.any_instance.expects(:authenticate).never
130
130
  MongoMapper.connect('development', 'read_secondary' => true)
131
131
  end
132
+
133
+ should "create a replica set connection if config contains multiple hosts in the new format" do
134
+ MongoMapper.config = {
135
+ 'development' => {
136
+ 'hosts' => ['127.0.0.1:27017', 'localhost:27017'],
137
+ 'database' => 'test'
138
+ }
139
+ }
140
+
141
+ Mongo::ReplSetConnection.expects(:new).with( ['127.0.0.1:27017', 'localhost:27017'], {'read_secondary' => true} )
142
+ MongoMapper.expects(:database=).with('test')
143
+ Mongo::DB.any_instance.expects(:authenticate).never
144
+ MongoMapper.connect('development', 'read_secondary' => true)
145
+ end
132
146
  end
133
147
 
134
148
  context "setup" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mongo_mapper
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.11.2
4
+ version: 0.12.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-07-21 00:00:00.000000000 Z
12
+ date: 2012-09-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activemodel
16
- requirement: &70319139905420 !ruby/object:Gem::Requirement
16
+ requirement: &70247394230160 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '3.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70319139905420
24
+ version_requirements: *70247394230160
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: activesupport
27
- requirement: &70319139904940 !ruby/object:Gem::Requirement
27
+ requirement: &70247394229680 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,18 +32,18 @@ dependencies:
32
32
  version: '3.0'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70319139904940
35
+ version_requirements: *70247394229680
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: plucky
38
- requirement: &70319139904480 !ruby/object:Gem::Requirement
38
+ requirement: &70247394229220 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
42
42
  - !ruby/object:Gem::Version
43
- version: 0.5.1
43
+ version: 0.5.2
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70319139904480
46
+ version_requirements: *70247394229220
47
47
  description:
48
48
  email:
49
49
  - nunemaker@gmail.com
@@ -219,7 +219,6 @@ files:
219
219
  - test/unit/test_rails.rb
220
220
  - test/unit/test_rails_compatibility.rb
221
221
  - test/unit/test_rails_reflect_on_association.rb
222
- - test/unit/test_railtie.rb
223
222
  - test/unit/test_serialization.rb
224
223
  - test/unit/test_time_zones.rb
225
224
  - test/unit/test_translation.rb
@@ -227,7 +226,7 @@ files:
227
226
  - LICENSE
228
227
  - UPGRADES
229
228
  - README.rdoc
230
- homepage: http://github.com/jnunemaker/mongomapper
229
+ homepage: http://mongomapper.com
231
230
  licenses: []
232
231
  post_install_message:
233
232
  rdoc_options: []
@@ -241,7 +240,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
241
240
  version: '0'
242
241
  segments:
243
242
  - 0
244
- hash: -1944766579498551313
243
+ hash: -335898215056105210
245
244
  required_rubygems_version: !ruby/object:Gem::Requirement
246
245
  none: false
247
246
  requirements:
@@ -250,7 +249,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
250
249
  version: '0'
251
250
  segments:
252
251
  - 0
253
- hash: -1944766579498551313
252
+ hash: -335898215056105210
254
253
  requirements: []
255
254
  rubyforge_project:
256
255
  rubygems_version: 1.8.10
@@ -1,66 +0,0 @@
1
- require 'test_helper'
2
- require "rails"
3
- require 'mongo_mapper/railtie'
4
-
5
- class TestRailtie < Test::Unit::TestCase
6
-
7
- def expect_descendants(expectation)
8
- # Keep expectation a string so we don't accidentally load in a class
9
- Railtie::Parent.descendants.map(&:to_s).sort.should == expectation.sort
10
- end
11
-
12
- def run_initializer(mod, name)
13
- initializer = mod.initializers.detect do |i|
14
- i.name == name
15
- end
16
-
17
- if initializer.nil?
18
- raise 'Initializer not found'
19
- end
20
-
21
- initializer.block.arity == -1 ? initializer.run : initializer.run(FakeRails)
22
- # mongo_mapper.prepare_dispatcher takes a Rails app as its one arg,
23
- # set_clear_dependencies_hook takes no args
24
- end
25
-
26
- def load_autoloaded_class
27
- Railtie::Autoloaded.presence
28
- end
29
-
30
- class FakeRails
31
- def self.config
32
- return Class.new { def cache_classes ; false ; end }.new
33
- end
34
- end
35
-
36
- context "Railtie" do
37
- include Rails::Application::Bootstrap
38
-
39
- setup do
40
- require 'support/railtie'
41
- require 'support/railtie/parent'
42
- require 'support/railtie/not_autoloaded'
43
-
44
- ActiveSupport::Dependencies.autoload_paths << File.join(File.dirname(__FILE__), '..', 'support')
45
-
46
- # These initializers don't actually run anything, they just register cleanup and prepare hooks
47
- run_initializer Rails::Application::Bootstrap, :set_clear_dependencies_hook
48
- run_initializer MongoMapper::Railtie, 'mongo_mapper.prepare_dispatcher'
49
- end
50
-
51
- should "not clear ActiveSupport::DescendantsTracker" do
52
- expect_descendants %w( Railtie::NotAutoloaded )
53
- load_autoloaded_class
54
- expect_descendants %w( Railtie::NotAutoloaded Railtie::Autoloaded )
55
-
56
- ActionDispatch::Reloader.cleanup! # cleanup 'last request'
57
-
58
- expect_descendants %w( Railtie::NotAutoloaded )
59
- load_autoloaded_class
60
- expect_descendants %w( Railtie::NotAutoloaded Railtie::Autoloaded )
61
-
62
- ActionDispatch::Reloader.prepare! # prepare 'next request'
63
- expect_descendants %w( Railtie::NotAutoloaded Railtie::Autoloaded )
64
- end
65
- end
66
- end