As part of an optimization, we don't store our entire User
in the session, instead we wrap the User
inside a facade which contains only the basic attributes, such as id
, username
and admin?
. Anything else is lazy-loaded (using method_missing
). When this object is written to the session, it unloads the user prior to marshalling.
Basically:
class UserProxy
attr_reader :id
attr_reader :username
def initialize(user)
@user = user
reload_attributes!
end
def admin?
@admin
end
def self.model_name
User.model_name
end
def method_missing(meth, *args, &block)
@user ||= User.get!(id)
@user.send(meth, *args, &block)
ensure
reload_attributes! # seems as good a place as any to keep the data fresh
end
def unset_user!
@user = nil
self
end
def ==(*args, &block)
method_missing('==', *args, &block)
end
def !=(*args, &block)
method_missing('!=', *args, &block)
end
def self._load(data) # custom unmarshalling logic
UserProxy.new(ActiveSupport::JSON.decode(data)).unset_user!
end
def _dump(depth) # custom marshalling logic
reload_attributes!
ActiveSupport::JSON.encode({"id" => id, "username" => username, "admin" => admin})
end
private
def reload_attrib开发者_如何转开发utes!
return if @user.nil?
@id = @user["id"]
@username = @user["username"]
@admin = @user["admin"]
end
end
This works great everywhere (it behaves just like the DataMapper user it wraps and we don't have to hit the database to get the user on most page requests, since the important bits (key and username) are already loaded.
What it doesn't play nicely with, is the path helpers Rails creates.
<%= url_for(@user) # should be /users/1234 %>
Initially it would error badly because it couldn't figure out what the model_name
was, so I fixed that by adding the class method model_name
to my UserProxy
class and just returning User.model_name
and it now finds the correct routes, but in place of the user ID, it puts:
<a href="/users/%23%3CUserProxy:0xa4449cc%3E">Chris Corbyn</a>
I'm not sure where to look to fix this one. I've put debug calls in method_missing to see what is being invoked, but it's not even receiving a call to to_param
, like I'd expect it to.
I can't believe I didn't try this. I just added to_param
to the proxy and delegated the call to the user object and it works. I didn't realize all objects in Rails had a to_param
method, model or not. I should probably do the same for to_key
then!
精彩评论