mongo_mapper 0.11.2 → 0.12.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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