Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Implement identity map!

  • Loading branch information...
commit 218479f71c861db79ccce8e12c4cb59d0a63cc77 1 parent 9e6823b
@sferik authored
View
12 lib/twitter/base.rb
@@ -1,8 +1,12 @@
+require 'twitter/identity_map'
+
module Twitter
class Base
attr_accessor :attrs
alias :to_hash :attrs
+ @@identity_map = IdentityMap.new
@jnunemaker Collaborator

This isn't thread safe, right? I would think we'd need Thread.current[:twitter_identity_map] ||= IdentityMap.new or something. I'm no expert on threads though.

@sferik Owner
sferik added a note

Is your concern that this initialization would happen multiple times? I could make IdentityMap a singleton and call instance instead of new. Even if it is initialized multiple times, it wouldn't really cause anything bad to happen.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+
# Define methods that retrieve the value from an initialized instance variable Hash, using the attribute as a key
#
# @overload self.lazy_attr_reader(attr)
@@ -19,12 +23,18 @@ def self.lazy_attr_reader(*attrs)
end
end
+ def self.new(attrs={})
+ @@identity_map[self.name] ||= {}
+ @@identity_map[self.name][Marshal.dump(attrs)] || super(attrs)
+ end
+
# Initializes a new Base object
#
# @param attrs [Hash]
# @return [Twitter::Base]
def initialize(attrs={})
- @attrs = attrs.dup
+ @attrs = attrs
+ @@identity_map[self.class.name][Marshal.dump(attrs)] = self
end
# Initializes a new Base object
View
8 lib/twitter/identity_map.rb
@@ -0,0 +1,8 @@
+module Twitter
+
+ # Tracks objects to help ensure that each object gets loaded only once.
+ # See: http://www.martinfowler.com/eaaCatalog/identityMap.html
+ class IdentityMap < Hash
+ end
+
+end
View
6 spec/twitter/base_spec.rb
@@ -26,4 +26,10 @@
end
end
+ describe "identical objects" do
+ it "should have the same object_id" do
+ @base.object_id.should == Twitter::Base.new('id' => 1).object_id
+ end
+ end
+
end

2 comments on commit 218479f

@jnunemaker
Collaborator

One problem with identity maps is ensuring they are cleared. If someone used this in a web request, it would basically cache the lookup forever. Same problem with a single background process.

What rails and other things do is turn it off by default, then have middleware that turns it on for a request, ensuring things are cleared at the end. You can see what I did for toystore:

https://github.com/jnunemaker/toystore/blob/master/examples/identity_map.rb
https://github.com/jnunemaker/toystore/blob/master/lib/toy/identity_map.rb
https://github.com/jnunemaker/toystore/blob/master/spec/toy/identity_map_spec.rb
https://github.com/jnunemaker/toystore/blob/master/lib/toy/middleware/identity_map.rb
https://github.com/jnunemaker/toystore/blob/master/spec/toy/middleware/identity_map_spec.rb

@sferik
Owner

I'm not sure you're reading the code correctly. HTTP requests are never cached and objects are only cached based on their values. If a request returns an object with even a single different value, it will be a cache miss, so data should never be stale. For objects that have ids (e.g. Twitter::Status or Twitter::User), we could check to see whether an object with the same id already exists in the cache and then perform an update instead of allocating a new object but it's not obvious to me whether that would be more or less efficient than doing it this way.

Please sign in to comment.
Something went wrong with that request. Please try again.