Skip to content
This repository
Browse code

Merge pull request #5261 from carlosantoniodasilva/identity-map-removal

Remove IdentityMap
  • Loading branch information...
commit 795062282e072f289918688e978a0cf24e6d3aa5 2 parents c1f397f + dde3058
José Valim josevalim authored

Showing 34 changed files with 49 additions and 966 deletions. Show diff stats Hide diff stats

  1. +11 0 activerecord/CHANGELOG.md
  2. +0 7 activerecord/RUNNING_UNIT_TESTS
  3. +0 1  activerecord/lib/active_record.rb
  4. +1 12 activerecord/lib/active_record/associations/association.rb
  5. +0 5 activerecord/lib/active_record/attribute_methods/dirty.rb
  6. +0 6 activerecord/lib/active_record/autosave_association.rb
  7. +0 2  activerecord/lib/active_record/counter_cache.rb
  8. +1 3 activerecord/lib/active_record/fixtures.rb
  9. +0 144 activerecord/lib/active_record/identity_map.rb
  10. +3 20 activerecord/lib/active_record/inheritance.rb
  11. +0 1  activerecord/lib/active_record/model.rb
  12. +4 12 activerecord/lib/active_record/persistence.rb
  13. +0 5 activerecord/lib/active_record/railtie.rb
  14. +1 10 activerecord/lib/active_record/relation.rb
  15. +0 10 activerecord/lib/active_record/relation/finder_methods.rb
  16. +0 10 activerecord/lib/active_record/test_case.rb
  17. +0 2  activerecord/lib/active_record/transactions.rb
  18. +0 1  activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
  19. +10 10 activerecord/test/cases/associations/eager_test.rb
  20. +0 137 activerecord/test/cases/associations/identity_map_test.rb
  21. +2 6 activerecord/test/cases/autosave_association_test.rb
  22. +2 2 activerecord/test/cases/base_test.rb
  23. +0 3  activerecord/test/cases/helper.rb
  24. +0 74 activerecord/test/cases/identity_map/middleware_test.rb
  25. +0 439 activerecord/test/cases/identity_map_test.rb
  26. +0 12 activerecord/test/cases/log_subscriber_test.rb
  27. +1 1  activerecord/test/cases/query_cache_test.rb
  28. +7 9 activerecord/test/cases/relations_test.rb
  29. +1 1  activerecord/test/support/connection.rb
  30. +1 8 ci/travis.rb
  31. +0 2  railties/guides/source/configuring.textile
  32. +4 0 railties/guides/source/upgrading_ruby_on_rails.textile
  33. +0 4 railties/lib/rails/test_help.rb
  34. +0 7 railties/test/application/middleware_test.rb
11 activerecord/CHANGELOG.md
Source Rendered
... ... @@ -1,5 +1,16 @@
1 1 ## Rails 4.0.0 (unreleased) ##
2 2
  3 +* Remove IdentityMap
  4 +
  5 + IdentityMap has never graduated to be an "enabled-by-default" feature, due
  6 + to some inconsistencies with associations, as described in this commit:
  7 +
  8 + https://github.com/rails/rails/commit/302c912bf6bcd0fa200d964ec2dc4a44abe328a6
  9 +
  10 + Hence the removal from the codebase, until such issues are fixed.
  11 +
  12 + *Carlos Antonio da Silva*
  13 +
3 14 * Added the schema cache dump feature.
4 15
5 16 `Schema cache dump` feature was implemetend. This feature can dump/load internal state of `SchemaCache` instance
7 activerecord/RUNNING_UNIT_TESTS
@@ -26,13 +26,6 @@ You can run all the tests for a given database via rake:
26 26
27 27 The 'rake test' task will run all the tests for mysql, mysql2, sqlite3 and postgresql.
28 28
29   -== Identity Map
30   -
31   -By default the tests run with the Identity Map turned off. But all tests should pass whether or
32   -not the identity map is on or off. You can turn it on using the IM env variable:
33   -
34   - $ IM=true ruby -Itest test/case/base_test.rb
35   -
36 29 == Config file
37 30
38 31 By default, the config file is expected to be at the path test/config.yml. You can specify a
1  activerecord/lib/active_record.rb
@@ -65,7 +65,6 @@ module ActiveRecord
65 65 autoload :DynamicFinderMatch
66 66 autoload :DynamicScopeMatch
67 67 autoload :Explain
68   - autoload :IdentityMap
69 68 autoload :Inheritance
70 69 autoload :Integration
71 70 autoload :Migration
13 activerecord/lib/active_record/associations/association.rb
@@ -45,7 +45,6 @@ def aliased_table_name
45 45 # Resets the \loaded flag to +false+ and sets the \target to +nil+.
46 46 def reset
47 47 @loaded = false
48   - IdentityMap.remove(target) if IdentityMap.enabled? && target
49 48 @target = nil
50 49 end
51 50
@@ -135,17 +134,7 @@ def target_scope
135 134 # ActiveRecord::RecordNotFound is rescued within the method, and it is
136 135 # not reraised. The proxy is \reset and +nil+ is the return value.
137 136 def load_target
138   - if find_target?
139   - begin
140   - if IdentityMap.enabled? && association_class && association_class.respond_to?(:base_class)
141   - @target = IdentityMap.get(association_class, owner[reflection.foreign_key])
142   - end
143   - rescue NameError
144   - nil
145   - ensure
146   - @target ||= find_target
147   - end
148   - end
  137 + @target ||= find_target if find_target?
149 138 loaded! unless loaded?
150 139 target
151 140 rescue ActiveRecord::RecordNotFound
5 activerecord/lib/active_record/attribute_methods/dirty.rb
@@ -22,8 +22,6 @@ def save(*) #:nodoc:
22 22 if status = super
23 23 @previously_changed = changes
24 24 @changed_attributes.clear
25   - elsif IdentityMap.enabled?
26   - IdentityMap.remove(self)
27 25 end
28 26 status
29 27 end
@@ -34,9 +32,6 @@ def save!(*) #:nodoc:
34 32 @previously_changed = changes
35 33 @changed_attributes.clear
36 34 end
37   - rescue
38   - IdentityMap.remove(self) if IdentityMap.enabled?
39   - raise
40 35 end
41 36
42 37 # <tt>reload</tt> the record and clears changed attributes.
6 activerecord/lib/active_record/autosave_association.rb
@@ -328,7 +328,6 @@ def save_collection_association(reflection)
328 328 autosave = reflection.options[:autosave]
329 329
330 330 if records = associated_records_to_validate_or_save(association, @new_record_before_save, autosave)
331   - begin
332 331 records.each do |record|
333 332 next if record.destroyed?
334 333
@@ -348,11 +347,6 @@ def save_collection_association(reflection)
348 347
349 348 raise ActiveRecord::Rollback unless saved
350 349 end
351   - rescue
352   - records.each {|x| IdentityMap.remove(x) } if IdentityMap.enabled?
353   - raise
354   - end
355   -
356 350 end
357 351
358 352 # reconstruct the scope now that we know the owner's id
2  activerecord/lib/active_record/counter_cache.rb
@@ -69,8 +69,6 @@ def update_counters(id, counters)
69 69 "#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}"
70 70 end
71 71
72   - IdentityMap.remove_by_id(symbolized_base_class, id) if IdentityMap.enabled?
73   -
74 72 update_all(updates.join(', '), primary_key => id)
75 73 end
76 74
4 activerecord/lib/active_record/fixtures.rb
@@ -796,9 +796,7 @@ def setup_fixture_accessors(fixture_names = nil)
796 796 @fixture_cache[fixture_name].delete(fixture) if force_reload
797 797
798 798 if @loaded_fixtures[fixture_name][fixture.to_s]
799   - ActiveRecord::IdentityMap.without do
800   - @fixture_cache[fixture_name][fixture] ||= @loaded_fixtures[fixture_name][fixture.to_s].find
801   - end
  799 + @fixture_cache[fixture_name][fixture] ||= @loaded_fixtures[fixture_name][fixture.to_s].find
802 800 else
803 801 raise StandardError, "No entry named '#{fixture}' found for fixture collection '#{fixture_name}'"
804 802 end
144 activerecord/lib/active_record/identity_map.rb
... ... @@ -1,144 +0,0 @@
1   -module ActiveRecord
2   - # = Active Record Identity Map
3   - #
4   - # Ensures that each object gets loaded only once by keeping every loaded
5   - # object in a map. Looks up objects using the map when referring to them.
6   - #
7   - # More information on Identity Map pattern:
8   - # http://www.martinfowler.com/eaaCatalog/identityMap.html
9   - #
10   - # == Configuration
11   - #
12   - # In order to enable IdentityMap, set <tt>config.active_record.identity_map = true</tt>
13   - # in your <tt>config/application.rb</tt> file.
14   - #
15   - # IdentityMap is disabled by default and still in development (i.e. use it with care).
16   - #
17   - # == Associations
18   - #
19   - # Active Record Identity Map does not track associations yet. For example:
20   - #
21   - # comment = @post.comments.first
22   - # comment.post = nil
23   - # @post.comments.include?(comment) #=> true
24   - #
25   - # Ideally, the example above would return false, removing the comment object from the
26   - # post association when the association is nullified. This may cause side effects, as
27   - # in the situation below, if Identity Map is enabled:
28   - #
29   - # Post.has_many :comments, :dependent => :destroy
30   - #
31   - # comment = @post.comments.first
32   - # comment.post = nil
33   - # comment.save
34   - # Post.destroy(@post.id)
35   - #
36   - # Without using Identity Map, the code above will destroy the @post object leaving
37   - # the comment object intact. However, once we enable Identity Map, the post loaded
38   - # by Post.destroy is exactly the same object as the object @post. As the object @post
39   - # still has the comment object in @post.comments, once Identity Map is enabled, the
40   - # comment object will be accidently removed.
41   - #
42   - # This inconsistency is meant to be fixed in future Rails releases.
43   - #
44   - module IdentityMap
45   -
46   - class << self
47   - def enabled=(flag)
48   - Thread.current[:identity_map_enabled] = flag
49   - end
50   -
51   - def enabled
52   - Thread.current[:identity_map_enabled]
53   - end
54   - alias enabled? enabled
55   -
56   - def repository
57   - Thread.current[:identity_map] ||= Hash.new { |h,k| h[k] = {} }
58   - end
59   -
60   - def use
61   - old, self.enabled = enabled, true
62   -
63   - yield if block_given?
64   - ensure
65   - self.enabled = old
66   - clear
67   - end
68   -
69   - def without
70   - old, self.enabled = enabled, false
71   -
72   - yield if block_given?
73   - ensure
74   - self.enabled = old
75   - end
76   -
77   - def get(klass, primary_key)
78   - record = repository[klass.symbolized_sti_name][primary_key]
79   -
80   - if record.is_a?(klass)
81   - ActiveSupport::Notifications.instrument("identity.active_record",
82   - :line => "From Identity Map (id: #{primary_key})",
83   - :name => "#{klass} Loaded",
84   - :connection_id => object_id)
85   -
86   - record
87   - else
88   - nil
89   - end
90   - end
91   -
92   - def add(record)
93   - repository[record.class.symbolized_sti_name][record.id] = record
94   - end
95   -
96   - def remove(record)
97   - repository[record.class.symbolized_sti_name].delete(record.id)
98   - end
99   -
100   - def remove_by_id(symbolized_sti_name, id)
101   - repository[symbolized_sti_name].delete(id)
102   - end
103   -
104   - def clear
105   - repository.clear
106   - end
107   - end
108   -
109   - # Reinitialize an Identity Map model object from +coder+.
110   - # +coder+ must contain the attributes necessary for initializing an empty
111   - # model object.
112   - def reinit_with(coder)
113   - @attributes_cache = {}
114   - dirty = @changed_attributes.keys
115   - attributes = self.class.initialize_attributes(coder['attributes'].except(*dirty))
116   - @attributes.update(attributes)
117   - @changed_attributes.update(coder['attributes'].slice(*dirty))
118   - @changed_attributes.delete_if{|k,v| v.eql? @attributes[k]}
119   -
120   - run_callbacks :find
121   -
122   - self
123   - end
124   -
125   - class Middleware
126   - def initialize(app)
127   - @app = app
128   - end
129   -
130   - def call(env)
131   - enabled = IdentityMap.enabled
132   - IdentityMap.enabled = true
133   -
134   - response = @app.call(env)
135   - response[2] = Rack::BodyProxy.new(response[2]) do
136   - IdentityMap.enabled = enabled
137   - IdentityMap.clear
138   - end
139   -
140   - response
141   - end
142   - end
143   - end
144   -end
23 activerecord/lib/active_record/inheritance.rb
@@ -63,26 +63,9 @@ def sti_name
63 63 # single-table inheritance model that makes it possible to create
64 64 # objects of different types from the same table.
65 65 def instantiate(record, column_types = {})
66   - sti_class = find_sti_class(record[inheritance_column])
67   - record_id = sti_class.primary_key && record[sti_class.primary_key]
68   -
69   - if ActiveRecord::IdentityMap.enabled? && record_id
70   - if (column = sti_class.columns_hash[sti_class.primary_key]) && column.number?
71   - record_id = record_id.to_i
72   - end
73   - if instance = IdentityMap.get(sti_class, record_id)
74   - instance.reinit_with('attributes' => record)
75   - else
76   - instance = sti_class.allocate.init_with('attributes' => record)
77   - IdentityMap.add(instance)
78   - end
79   - else
80   - column_types = sti_class.decorate_columns(column_types)
81   - instance = sti_class.allocate.init_with('attributes' => record,
82   - 'column_types' => column_types)
83   - end
84   -
85   - instance
  66 + sti_class = find_sti_class(record[inheritance_column])
  67 + column_types = sti_class.decorate_columns(column_types)
  68 + sti_class.allocate.init_with('attributes' => record, 'column_types' => column_types)
86 69 end
87 70
88 71 # For internal use.
1  activerecord/lib/active_record/model.rb
@@ -60,7 +60,6 @@ def self.extend(*modules)
60 60 include AttributeMethods
61 61 include Callbacks, ActiveModel::Observing, Timestamp
62 62 include Associations
63   - include IdentityMap
64 63 include ActiveModel::SecurePassword
65 64 include AutosaveAssociation, NestedAttributes
66 65 include Aggregations, Transactions, Reflection, Serialization, Store
16 activerecord/lib/active_record/persistence.rb
@@ -115,10 +115,7 @@ def save!(*)
115 115 # callbacks, Observer methods, or any <tt>:dependent</tt> association
116 116 # options, use <tt>#destroy</tt>.
117 117 def delete
118   - if persisted?
119   - self.class.delete(id)
120   - IdentityMap.remove(self) if IdentityMap.enabled?
121   - end
  118 + self.class.delete(id) if persisted?
122 119 @destroyed = true
123 120 freeze
124 121 end
@@ -129,7 +126,6 @@ def destroy
129 126 destroy_associations
130 127
131 128 if persisted?
132   - IdentityMap.remove(self) if IdentityMap.enabled?
133 129 pk = self.class.primary_key
134 130 column = self.class.columns_hash[pk]
135 131 substitute = connection.substitute_at(column, 0)
@@ -284,11 +280,9 @@ def reload(options = nil)
284 280 clear_aggregation_cache
285 281 clear_association_cache
286 282
287   - IdentityMap.without do
288   - fresh_object = self.class.unscoped { self.class.find(id, options) }
289   - @attributes.update(fresh_object.instance_variable_get('@attributes'))
290   - @columns_hash = fresh_object.instance_variable_get('@columns_hash')
291   - end
  283 + fresh_object = self.class.unscoped { self.class.find(id, options) }
  284 + @attributes.update(fresh_object.instance_variable_get('@attributes'))
  285 + @columns_hash = fresh_object.instance_variable_get('@columns_hash')
292 286
293 287 @attributes_cache = {}
294 288 self
@@ -363,10 +357,8 @@ def create
363 357 attributes_values = arel_attributes_with_values_for_create(!id.nil?)
364 358
365 359 new_id = self.class.unscoped.insert attributes_values
366   -
367 360 self.id ||= new_id if self.class.primary_key
368 361
369   - IdentityMap.add(self) if IdentityMap.enabled?
370 362 @new_record = false
371 363 id
372 364 end
5 activerecord/lib/active_record/railtie.rb
@@ -53,11 +53,6 @@ class Railtie < Rails::Railtie
53 53 ActiveSupport.on_load(:active_record) { self.logger ||= ::Rails.logger }
54 54 end
55 55
56   - initializer "active_record.identity_map" do |app|
57   - config.app_middleware.insert_after "::ActionDispatch::Callbacks",
58   - "ActiveRecord::IdentityMap::Middleware" if config.active_record.delete(:identity_map)
59   - end
60   -
61 56 initializer "active_record.set_configs" do |app|
62 57 ActiveSupport.on_load(:active_record) do
63 58 if app.config.active_record.delete(:whitelist_attributes)
11 activerecord/lib/active_record/relation.rb
@@ -168,13 +168,7 @@ def exec_queries
168 168 default_scoped = with_default_scope
169 169
170 170 if default_scoped.equal?(self)
171   - @records = if @readonly_value.nil? && !@klass.locking_enabled?
172   - eager_loading? ? find_with_associations : @klass.find_by_sql(arel, @bind_values)
173   - else
174   - IdentityMap.without do
175   - eager_loading? ? find_with_associations : @klass.find_by_sql(arel, @bind_values)
176   - end
177   - end
  171 + @records = eager_loading? ? find_with_associations : @klass.find_by_sql(arel, @bind_values)
178 172
179 173 preload = @preload_values
180 174 preload += @includes_values unless eager_loading?
@@ -274,7 +268,6 @@ def scoping
274 268 # # The same idea applies to limit and order
275 269 # Book.where('title LIKE ?', '%Rails%').order(:created_at).limit(5).update_all(:author => 'David')
276 270 def update_all(updates, conditions = nil, options = {})
277   - IdentityMap.repository[symbolized_base_class].clear if IdentityMap.enabled?
278 271 if conditions || options.present?
279 272 where(conditions).apply_finder_options(options.slice(:limit, :order)).update_all(updates)
280 273 else
@@ -404,7 +397,6 @@ def destroy(id)
404 397 # If you need to destroy dependent associations or call your <tt>before_*</tt> or
405 398 # +after_destroy+ callbacks, use the +destroy_all+ method instead.
406 399 def delete_all(conditions = nil)
407   - IdentityMap.repository[symbolized_base_class] = {} if IdentityMap.enabled?
408 400 if conditions
409 401 where(conditions).delete_all
410 402 else
@@ -437,7 +429,6 @@ def delete_all(conditions = nil)
437 429 # # Delete multiple rows
438 430 # Todo.delete([2,3,4])
439 431 def delete(id_or_array)
440   - IdentityMap.remove_by_id(self.symbolized_base_class, id_or_array) if IdentityMap.enabled?
441 432 where(primary_key => id_or_array).delete_all
442 433 end
443 434
10 activerecord/lib/active_record/relation/finder_methods.rb
@@ -318,17 +318,7 @@ def find_with_ids(*ids)
318 318 def find_one(id)
319 319 id = id.id if ActiveRecord::Base === id
320 320
321   - if IdentityMap.enabled? && where_values.blank? &&
322   - limit_value.blank? && order_values.blank? &&
323   - includes_values.blank? && preload_values.blank? &&
324   - readonly_value.nil? && joins_values.blank? &&
325   - !@klass.locking_enabled? &&
326   - record = IdentityMap.get(@klass, id)
327   - return record
328   - end
329   -
330 321 column = columns_hash[primary_key]
331   -
332 322 substitute = connection.substitute_at(column, @bind_values.length)
333 323 relation = where(table[primary_key].eq(substitute))
334 324 relation.bind_values += [[column, id]]
10 activerecord/lib/active_record/test_case.rb
@@ -7,20 +7,10 @@ module ActiveRecord
7 7 #
8 8 # Defines some test assertions to test against SQL queries.
9 9 class TestCase < ActiveSupport::TestCase #:nodoc:
10   - setup :cleanup_identity_map
11   -
12   - def setup
13   - cleanup_identity_map
14   - end
15   -
16 10 def teardown
17 11 SQLCounter.log.clear
18 12 end
19 13
20   - def cleanup_identity_map
21   - ActiveRecord::IdentityMap.clear
22   - end
23   -
24 14 def assert_date_from_db(expected, actual, message = nil)
25 15 # SybaseAdapter doesn't have a separate column type just for dates,
26 16 # so the time is in the string and incorrectly formatted
2  activerecord/lib/active_record/transactions.rb
@@ -251,7 +251,6 @@ def rollback_active_record_state!
251 251 remember_transaction_record_state
252 252 yield
253 253 rescue Exception
254   - IdentityMap.remove(self) if IdentityMap.enabled?
255 254 restore_transaction_record_state
256 255 raise
257 256 ensure
@@ -270,7 +269,6 @@ def committed! #:nodoc:
270 269 def rolledback!(force_restore_state = false) #:nodoc:
271 270 run_callbacks :rollback
272 271 ensure
273   - IdentityMap.remove(self) if IdentityMap.enabled?
274 272 restore_transaction_record_state(force_restore_state)
275 273 end
276 274
1  activerecord/test/cases/associations/eager_load_includes_full_sti_class_test.rb
@@ -27,7 +27,6 @@ def test_class_names
27 27 post = Namespaced::Post.find_by_title( 'Great stuff', :include => :tagging )
28 28 assert_nil post.tagging
29 29
30   - ActiveRecord::IdentityMap.clear
31 30 ActiveRecord::Base.store_full_sti_class = true
32 31 post = Namespaced::Post.find_by_title( 'Great stuff', :include => :tagging )
33 32 assert_instance_of Tagging, post.tagging
20 activerecord/test/cases/associations/eager_test.rb
@@ -197,7 +197,7 @@ def test_finding_with_includes_on_has_one_assocation_with_same_include_includes_
197 197 author = authors(:david)
198 198 post = author.post_about_thinking_with_last_comment
199 199 last_comment = post.last_comment
200   - author = assert_queries(ActiveRecord::IdentityMap.enabled? ? 2 : 3) { Author.find(author.id, :include => {:post_about_thinking_with_last_comment => :last_comment})} # find the author, then find the posts, then find the comments
  200 + author = assert_queries(3) { Author.find(author.id, :include => {:post_about_thinking_with_last_comment => :last_comment})} # find the author, then find the posts, then find the comments
201 201 assert_no_queries do
202 202 assert_equal post, author.post_about_thinking_with_last_comment
203 203 assert_equal last_comment, author.post_about_thinking_with_last_comment.last_comment
@@ -208,7 +208,7 @@ def test_finding_with_includes_on_belongs_to_association_with_same_include_inclu
208 208 post = posts(:welcome)
209 209 author = post.author
210 210 author_address = author.author_address
211   - post = assert_queries(ActiveRecord::IdentityMap.enabled? ? 2 : 3) { Post.find(post.id, :include => {:author_with_address => :author_address}) } # find the post, then find the author, then find the address
  211 + post = assert_queries(3) { Post.find(post.id, :include => {:author_with_address => :author_address}) } # find the post, then find the author, then find the address
212 212 assert_no_queries do
213 213 assert_equal author, post.author_with_address
214 214 assert_equal author_address, post.author_with_address.author_address
@@ -611,9 +611,9 @@ def test_eager_with_has_and_belongs_to_many_and_limit
611 611 assert posts[1].categories.include?(categories(:general))
612 612 end
613 613
614   - # This is only really relevant when the identity map is off. Since the preloader for habtm
615   - # gets raw row hashes from the database and then instantiates them, this test ensures that
616   - # it only instantiates one actual object per record from the database.
  614 + # Since the preloader for habtm gets raw row hashes from the database and then
  615 + # instantiates them, this test ensures that it only instantiates one actual
  616 + # object per record from the database.
617 617 def test_has_and_belongs_to_many_should_not_instantiate_same_records_multiple_times
618 618 welcome = posts(:welcome)
619 619 categories = Category.includes(:posts)
@@ -1002,18 +1002,18 @@ def test_eager_loading_with_conditions_on_joined_table_preloads
1002 1002 assert_equal [posts(:welcome)], posts
1003 1003 assert_equal authors(:david), assert_no_queries { posts[0].author}
1004 1004
1005   - posts = assert_queries(ActiveRecord::IdentityMap.enabled? ? 1 : 2) do
  1005 + posts = assert_queries(2) do
1006 1006 Post.find(:all, :select => 'distinct posts.*', :include => :author, :joins => [:comments], :conditions => "comments.body like 'Thank you%'", :order => 'posts.id')
1007 1007 end
1008 1008 assert_equal [posts(:welcome)], posts
1009 1009 assert_equal authors(:david), assert_no_queries { posts[0].author}
1010 1010
1011   - posts = assert_queries(ActiveRecord::IdentityMap.enabled? ? 1 : 2) do
  1011 + posts = assert_queries(2) do
1012 1012 Post.find(:all, :include => :author, :joins => {:taggings => :tag}, :conditions => "tags.name = 'General'", :order => 'posts.id')
1013 1013 end
1014 1014 assert_equal posts(:welcome, :thinking), posts
1015 1015
1016   - posts = assert_queries(ActiveRecord::IdentityMap.enabled? ? 1 : 2) do
  1016 + posts = assert_queries(2) do
1017 1017 Post.find(:all, :include => :author, :joins => {:taggings => {:tag => :taggings}}, :conditions => "taggings_tags.super_tag_id=2", :order => 'posts.id')
1018 1018 end
1019 1019 assert_equal posts(:welcome, :thinking), posts
@@ -1027,7 +1027,7 @@ def test_eager_loading_with_conditions_on_string_joined_table_preloads
1027 1027 assert_equal [posts(:welcome)], posts
1028 1028 assert_equal authors(:david), assert_no_queries { posts[0].author}
1029 1029
1030   - posts = assert_queries(ActiveRecord::IdentityMap.enabled? ? 1 : 2) do
  1030 + posts = assert_queries(2) do
1031 1031 Post.find(:all, :select => 'distinct posts.*', :include => :author, :joins => ["INNER JOIN comments on comments.post_id = posts.id"], :conditions => "comments.body like 'Thank you%'", :order => 'posts.id')
1032 1032 end
1033 1033 assert_equal [posts(:welcome)], posts
@@ -1116,7 +1116,7 @@ def test_preloading_empty_belongs_to
1116 1116 def test_preloading_empty_belongs_to_polymorphic
1117 1117 t = Tagging.create!(:taggable_type => 'Post', :taggable_id => Post.maximum(:id) + 1, :tag => tags(:general))
1118 1118
1119   - tagging = assert_queries(ActiveRecord::IdentityMap.enabled? ? 1 : 2) { Tagging.preload(:taggable).find(t.id) }
  1119 + tagging = assert_queries(2) { Tagging.preload(:taggable).find(t.id) }
1120 1120 assert_no_queries { assert_nil tagging.taggable }
1121 1121 end
1122 1122
137 activerecord/test/cases/associations/identity_map_test.rb
... ... @@ -1,137 +0,0 @@
1   -require "cases/helper"
2   -require 'models/author'
3   -require 'models/post'
4   -
5   -if ActiveRecord::IdentityMap.enabled?
6   -class InverseHasManyIdentityMapTest < ActiveRecord::TestCase
7   - fixtures :authors, :posts
8   -
9   - def test_parent_instance_should_be_shared_with_every_child_on_find
10   - m = Author.first
11   - is = m.posts
12   - is.each do |i|
13   - assert_equal m.name, i.author.name, "Name of man should be the same before changes to parent instance"
14   - m.name = 'Bongo'
15   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to parent instance"
16   - i.author.name = 'Mungo'
17   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to child-owned instance"
18   - end
19   - end
20   -
21   - def test_parent_instance_should_be_shared_with_eager_loaded_children
22   - m = Author.find(:first, :include => :posts)
23   - is = m.posts
24   - is.each do |i|
25   - assert_equal m.name, i.author.name, "Name of man should be the same before changes to parent instance"
26   - m.name = 'Bongo'
27   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to parent instance"
28   - i.author.name = 'Mungo'
29   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to child-owned instance"
30   - end
31   -
32   - m = Author.find(:first, :include => :posts, :order => 'posts.id')
33   - is = m.posts
34   - is.each do |i|
35   - assert_equal m.name, i.author.name, "Name of man should be the same before changes to parent instance"
36   - m.name = 'Bongo'
37   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to parent instance"
38   - i.author.name = 'Mungo'
39   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to child-owned instance"
40   - end
41   - end
42   -
43   - def test_parent_instance_should_be_shared_with_newly_built_child
44   - m = Author.first
45   - i = m.posts.build(:title => 'Industrial Revolution Re-enactment', :body => 'Lorem ipsum')
46   - assert_not_nil i.author
47   - assert_equal m.name, i.author.name, "Name of man should be the same before changes to parent instance"
48   - m.name = 'Bongo'
49   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to parent instance"
50   - i.author.name = 'Mungo'
51   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to just-built-child-owned instance"
52   - end
53   -
54   - def test_parent_instance_should_be_shared_with_newly_block_style_built_child
55   - m = Author.first
56   - i = m.posts.build {|ii| ii.title = 'Industrial Revolution Re-enactment'; ii.body = 'Lorem ipsum'}
57   - assert_not_nil i.title, "Child attributes supplied to build via blocks should be populated"
58   - assert_not_nil i.author
59   - assert_equal m.name, i.author.name, "Name of man should be the same before changes to parent instance"
60   - m.name = 'Bongo'
61   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to parent instance"
62   - i.author.name = 'Mungo'
63   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to just-built-child-owned instance"
64   - end
65   -
66   - def test_parent_instance_should_be_shared_with_newly_created_child
67   - m = Author.first
68   - i = m.posts.create(:title => 'Industrial Revolution Re-enactment', :body => 'Lorem ipsum')
69   - assert_not_nil i.author
70   - assert_equal m.name, i.author.name, "Name of man should be the same before changes to parent instance"
71   - m.name = 'Bongo'
72   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to parent instance"
73   - i.author.name = 'Mungo'
74   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to newly-created-child-owned instance"
75   - end
76   -
77   - def test_parent_instance_should_be_shared_with_newly_created_via_bang_method_child
78   - m = Author.first
79   - i = m.posts.create!(:title => 'Industrial Revolution Re-enactment', :body => 'Lorem ipsum')
80   - assert_not_nil i.author
81   - assert_equal m.name, i.author.name, "Name of man should be the same before changes to parent instance"
82   - m.name = 'Bongo'
83   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to parent instance"
84   - i.author.name = 'Mungo'
85   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to newly-created-child-owned instance"
86   - end
87   -
88   - def test_parent_instance_should_be_shared_with_newly_block_style_created_child
89   - m = Author.first
90   - i = m.posts.create {|ii| ii.title = 'Industrial Revolution Re-enactment'; ii.body = 'Lorem ipsum'}
91   - assert_not_nil i.title, "Child attributes supplied to create via blocks should be populated"
92   - assert_not_nil i.author
93   - assert_equal m.name, i.author.name, "Name of man should be the same before changes to parent instance"
94   - m.name = 'Bongo'
95   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to parent instance"
96   - i.author.name = 'Mungo'
97   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to newly-created-child-owned instance"
98   - end
99   -
100   - def test_parent_instance_should_be_shared_with_poked_in_child
101   - m = Author.first
102   - i = Post.create(:title => 'Industrial Revolution Re-enactment', :body => 'Lorem ipsum')
103   - m.posts << i
104   - assert_not_nil i.author
105   - assert_equal m.name, i.author.name, "Name of man should be the same before changes to parent instance"
106   - m.name = 'Bongo'
107   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to parent instance"
108   - i.author.name = 'Mungo'
109   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to newly-created-child-owned instance"
110   - end
111   -
112   - def test_parent_instance_should_be_shared_with_replaced_via_accessor_children
113   - m = Author.first
114   - i = Post.new(:title => 'Industrial Revolution Re-enactment', :body => 'Lorem ipsum')
115   - m.posts = [i]
116   - assert_same m, i.author
117   - assert_not_nil i.author
118   - assert_equal m.name, i.author.name, "Name of man should be the same before changes to parent instance"
119   - m.name = 'Bongo'
120   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to parent instance"
121   - i.author.name = 'Mungo'
122   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to replaced-child-owned instance"
123   - end
124   -
125   - def test_parent_instance_should_be_shared_with_replaced_via_method_children
126   - m = Author.first
127   - i = Post.new(:title => 'Industrial Revolution Re-enactment', :body => 'Lorem ipsum')
128   - m.posts = [i]
129   - assert_not_nil i.author
130   - assert_equal m.name, i.author.name, "Name of man should be the same before changes to parent instance"
131   - m.name = 'Bongo'
132   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to parent instance"
133   - i.author.name = 'Mungo'
134   - assert_equal m.name, i.author.name, "Name of man should be the same after changes to replaced-child-owned instance"
135   - end
136   -end
137   -end
8 activerecord/test/cases/autosave_association_test.rb
@@ -978,10 +978,7 @@ def test_should_allow_to_bypass_validations_on_associated_models_at_any_depth
978 978 values = [@pirate.reload.catchphrase, @pirate.ship.name, *@pirate.ship.parts.map(&:name)]
979 979 # Oracle saves empty string as NULL
980 980 if current_adapter?(:OracleAdapter)
981   - expected = ActiveRecord::IdentityMap.enabled? ?
982   - [nil, nil, '', ''] :
983   - [nil, nil, nil, nil]
984   - assert_equal expected, values
  981 + assert_equal [nil, nil, nil, nil], values
985 982 else
986 983 assert_equal ['', '', '', ''], values
987 984 end
@@ -1077,8 +1074,7 @@ def test_should_still_allow_to_bypass_validations_on_the_associated_model
1077 1074 @ship.save(:validate => false)
1078 1075 # Oracle saves empty string as NULL
1079 1076 if current_adapter?(:OracleAdapter)
1080   - expected = ActiveRecord::IdentityMap.enabled? ? [nil, ''] : [nil, nil]
1081   - assert_equal expected, [@ship.reload.name, @ship.pirate.catchphrase]
  1077 + assert_equal [nil, nil], [@ship.reload.name, @ship.pirate.catchphrase]
1082 1078 else
1083 1079 assert_equal ['', ''], [@ship.reload.name, @ship.pirate.catchphrase]
1084 1080 end
4 activerecord/test/cases/base_test.rb
@@ -1139,7 +1139,7 @@ def test_geometric_content
1139 1139 assert g.save
1140 1140
1141 1141 # Reload and check that we have all the geometric attributes.
1142   - h = ActiveRecord::IdentityMap.without { Geometric.find(g.id) }
  1142 + h = Geometric.find(g.id)
1143 1143
1144 1144 assert_equal '(5,6.1)', h.a_point
1145 1145 assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
@@ -1168,7 +1168,7 @@ def test_geometric_content
1168 1168 assert g.save
1169 1169
1170 1170 # Reload and check that we have all the geometric attributes.
1171   - h = ActiveRecord::IdentityMap.without { Geometric.find(g.id) }
  1171 + h = Geometric.find(g.id)
1172 1172
1173 1173 assert_equal '(5,6.1)', h.a_point
1174 1174 assert_equal '[(2,3),(5.5,7)]', h.a_line_segment
3  activerecord/test/cases/helper.rb
@@ -19,9 +19,6 @@
19 19 # Show backtraces for deprecated behavior for quicker cleanup.
20 20 ActiveSupport::Deprecation.debug = true
21 21
22   -# Enable Identity Map only when ENV['IM'] is set to "true"
23   -ActiveRecord::IdentityMap.enabled = (ENV['IM'] == "true")
24   -
25 22 # Avoid deprecation warning setting dependent_restrict_raises to false. The default is true
26 23 ActiveRecord::Base.dependent_restrict_raises = false
27 24
74 activerecord/test/cases/identity_map/middleware_test.rb
... ... @@ -1,74 +0,0 @@
1   -require "cases/helper"
2   -require "rack"
3   -
4   -module ActiveRecord
5   - module IdentityMap
6   - class MiddlewareTest < ActiveRecord::TestCase
7   - def setup
8   - super
9   - @enabled = IdentityMap.enabled
10   - IdentityMap.enabled = false
11   - end
12   -
13   - def teardown
14   - super
15   - IdentityMap.enabled = @enabled
16   - IdentityMap.clear
17   - end
18   -
19   - def test_delegates
20   - called = false
21   - mw = Middleware.new lambda { |env|
22   - called = true
23   - [200, {}, nil]
24   - }
25   - mw.call({})
26   - assert called, 'middleware delegated'
27   - end
28   -
29   - def test_im_enabled_during_delegation
30   - mw = Middleware.new lambda { |env|
31   - assert IdentityMap.enabled?, 'identity map should be enabled'
32   - [200, {}, nil]
33   - }
34   - mw.call({})
35   - end
36   -
37   - class Enum < Struct.new(:iter)
38   - def each(&b)
39   - iter.call(&b)
40   - end
41   - end
42   -
43   - def test_im_enabled_during_body_each
44   - mw = Middleware.new lambda { |env|
45   - [200, {}, Enum.new(lambda { |&b|
46   - assert IdentityMap.enabled?, 'identity map should be enabled'
47   - b.call "hello"
48   - })]
49   - }
50   - body = mw.call({}).last
51   - body.each { |x| assert_equal 'hello', x }
52   - end
53   -
54   - def test_im_disabled_after_body_close
55   - mw = Middleware.new lambda { |env| [200, {}, []] }
56   - body = mw.call({}).last
57   - assert IdentityMap.enabled?, 'identity map should be enabled'
58   - body.close
59   - assert !IdentityMap.enabled?, 'identity map should be disabled'
60   - end
61   -
62   - def test_im_cleared_after_body_close
63   - mw = Middleware.new lambda { |env| [200, {}, []] }
64   - body = mw.call({}).last
65   -
66   - IdentityMap.repository['hello'] = 'world'
67   - assert !IdentityMap.repository.empty?, 'repo should not be empty'
68   -
69   - body.close
70   - assert IdentityMap.repository.empty?, 'repo should be empty'
71   - end
72   - end
73   - end
74   -end
439 activerecord/test/cases/identity_map_test.rb
... ... @@ -1,439 +0,0 @@
1   -require "cases/helper"
2   -
3   -require 'models/developer'
4   -require 'models/project'
5   -require 'models/company'
6   -require 'models/topic'
7   -require 'models/reply'
8   -require 'models/computer'
9   -require 'models/customer'
10   -require 'models/order'
11   -require 'models/post'
12   -require 'models/author'
13   -require 'models/tag'
14   -require 'models/tagging'
15   -require 'models/comment'
16   -require 'models/sponsor'
17   -require 'models/member'
18   -require 'models/essay'
19   -require 'models/subscriber'
20   -require "models/pirate"
21   -require "models/bird"
22   -require "models/parrot"
23   -
24   -if ActiveRecord::IdentityMap.enabled?
25   -class IdentityMapTest < ActiveRecord::TestCase
26   - fixtures :accounts, :companies, :developers, :projects, :topics,
27   - :developers_projects, :computers, :authors, :author_addresses,
28   - :posts, :tags, :taggings, :comments, :subscribers
29   -
30   - ##############################################################################
31   - # Basic tests checking if IM is functioning properly on basic find operations#
32   - ##############################################################################
33   -
34   - def test_find_id
35   - assert_same(Client.find(3), Client.find(3))
36   - end
37   -
38   - def test_find_id_without_identity_map
39   - ActiveRecord::IdentityMap.without do
40   - assert_not_same(Client.find(3), Client.find(3))
41   - end
42   - end
43   -
44   - def test_find_id_use_identity_map
45   - ActiveRecord::IdentityMap.enabled = false
46   - ActiveRecord::IdentityMap.use do
47   - assert_same(Client.find(3), Client.find(3))
48   - end
49   - ActiveRecord::IdentityMap.enabled = true
50   - end
51   -
52   - def test_find_pkey
53   - assert_same(
54   - Subscriber.find('swistak'),
55   - Subscriber.find('swistak')
56   - )
57   - end
58   -
59   - def test_find_by_id
60   - assert_same(
61   - Client.find_by_id(3),
62   - Client.find_by_id(3)
63   - )
64   - end
65   -
66   - def test_find_by_string_and_numeric_id
67   - assert_same(
68   - Client.find_by_id("3"),
69   - Client.find_by_id(3)
70   - )
71   - end
72   -
73   - def test_find_by_pkey
74   - assert_same(
75   - Subscriber.find_by_nick('swistak'),
76   - Subscriber.find_by_nick('swistak')
77   - )
78   - end
79   -
80   - def test_find_first_id
81   - assert_same(
82   - Client.find(:first, :conditions => {:id => 1}),
83   - Client.find(:first, :conditions => {:id => 1})
84   - )
85   - end
86   -
87   - def test_find_first_pkey
88   - assert_same(
89   - Subscriber.find(:first, :conditions => {:nick => 'swistak'}),
90   - Subscriber.find(:first, :conditions => {:nick => 'swistak'})
91   - )
92   - end
93   -
94   - def test_queries_are_not_executed_when_finding_by_id
95   - Post.find 1
96   - assert_no_queries do
97   - Post.find 1
98   - end
99   - end
100   -
101   - ##############################################################################
102   - # Tests checking if IM is functioning properly on more advanced finds #
103   - # and associations #
104   - ##############################################################################
105   -
106   - def test_owner_object_is_associated_from_identity_map
107   - post = Post.find(1)
108   - comment = post.comments.first
109   -
110   - assert_no_queries do
111   - comment.post
112   - end
113   - assert_same post, comment.post
114   - end
115   -
116   - def test_associated_object_are_assigned_from_identity_map
117   - post = Post.find(1)
118   -
119   - post.comments.each do |comment|
120   - assert_same post, comment.post
121   - assert_equal post.object_id, comment.post.object_id
122   - end
123   - end
124   -
125   - def test_creation
126   - t1 = Topic.create("title" => "t1")
127   - t2 = Topic.find(t1.id)
128   - assert_same(t1, t2)
129   - end
130   -
131   - ##############################################################################
132   - # Tests checking if IM is functioning properly on classes with multiple #
133   - # types of inheritance #
134   - ##############################################################################
135   -
136   - def test_inherited_without_type_attribute_without_identity_map
137   - ActiveRecord::IdentityMap.without do
138   - p1 = DestructivePirate.create!(:catchphrase => "I'm not a regular Pirate")
139   - p2 = Pirate.find(p1.id)
140   - assert_not_same(p1, p2)
141   - end
142   - end
143   -
144   - def test_inherited_with_type_attribute_without_identity_map
145   - ActiveRecord::IdentityMap.without do
146   - c = comments(:sub_special_comment)
147   - c1 = SubSpecialComment.find(c.id)
148   - c2 = Comment.find(c.id)
149   - assert_same(c1.class, c2.class)
150   - end
151   - end
152   -
153   - def test_inherited_without_type_attribute
154   - p1 = DestructivePirate.create!(:catchphrase => "I'm not a regular Pirate")
155   - p2 = Pirate.find(p1.id)
156   - assert_not_same(p1, p2)
157   - end
158   -
159   - def test_inherited_with_type_attribute
160   - c = comments(:sub_special_comment)
161   - c1 = SubSpecialComment.find(c.id)
162   - c2 = Comment.find(c.id)
163   - assert_same(c1, c2)
164   - end
165   -
166   - ##############################################################################
167   - # Tests checking dirty attribute behavior with IM #
168   - ##############################################################################
169   -
170   - def test_loading_new_instance_should_not_update_dirty_attributes
171   - swistak = Subscriber.find(:first, :conditions => {:nick => 'swistak'})
172   - swistak.name = "Swistak Sreberkowiec"
173   - assert_equal(["name"], swistak.changed)
174   - assert_equal({"name" => ["Marcin Raczkowski", "Swistak Sreberkowiec"]}, swistak.changes)
175   -
176   - assert swistak.name_changed?
177   - assert_equal("Swistak Sreberkowiec", swistak.name)
178   - end
179   -
180   - def test_loading_new_instance_should_change_dirty_attribute_original_value
181   - swistak = Subscriber.find(:first, :conditions => {:nick => 'swistak'})
182   - swistak.name = "Swistak Sreberkowiec"
183   -
184   - Subscriber.update_all({:name => "Raczkowski Marcin"}, {:name => "Marcin Raczkowski"})
185   -
186   - assert_equal({"name"=>["Marcin Raczkowski", "Swistak Sreberkowiec"]}, swistak.changes)
187   - assert_equal("Swistak Sreberkowiec", swistak.name)
188   - end
189   -
190   - def test_loading_new_instance_should_remove_dirt
191   - swistak = Subscriber.find(:first, :conditions => {:nick => 'swistak'})
192   - swistak.name = "Swistak Sreberkowiec"
193   -
194   - assert_equal({"name" => ["Marcin Raczkowski", "Swistak Sreberkowiec"]}, swistak.changes)
195   -
196   - Subscriber.update_all({:name => "Swistak Sreberkowiec"}, {:name => "Marcin Raczkowski"})
197   -
198   - assert_equal("Swistak Sreberkowiec", swistak.name)
199   - assert_equal({"name"=>["Marcin Raczkowski", "Swistak Sreberkowiec"]}, swistak.changes)
200   - assert swistak.name_changed?
201   - end
202   -
203   - def test_has_many_associations
204   - pirate = Pirate.create!(:catchphrase => "Don' botharrr talkin' like one, savvy?")
205   - pirate.birds.create!(:name => 'Posideons Killer')
206   - pirate.birds.create!(:name => 'Killer bandita Dionne')
207   -
208   - posideons, _ = pirate.birds
209   -
210   - pirate.reload
211   -
212   - pirate.birds_attributes = [{ :id => posideons.id, :name => 'Grace OMalley' }]
213   - assert_equal 'Grace OMalley', pirate.birds.to_a.find { |r| r.id == posideons.id }.name
214   - end
215   -
216   - def test_changing_associations
217   - post1 = Post.create("title" => "One post", "body" => "Posting...")
218   - post2 = Post.create("title" => "Another post", "body" => "Posting... Again...")
219   - comment = Comment.new("body" => "comment")
220   -
221   - comment.post = post1
222   - assert comment.save
223   -
224   - assert_same(post1.comments.first, comment)
225   -
226   - comment.post = post2
227   - assert comment.save
228   -
229   - assert_same(post2.comments.first, comment)
230   - assert_equal(0, post1.comments.size)
231   - end
232   -
233   - def test_im_with_polymorphic_has_many_going_through_join_model_with_custom_select_and_joins
234   - tag = posts(:welcome).tags.first
235   - tag_with_joins_and_select = posts(:welcome).tags.add_joins_and_select.first
236   - assert_same(tag, tag_with_joins_and_select)
237   - assert_nothing_raised(NoMethodError, "Joins/select was not loaded") { tag.author_id }
238   - end