Xavier N. wrote:
On Apr 8, 2006, at 10:17, lagroue wrote:
I won’t enter in discussion on MVC, because I do need giving session
to models : I want them to be able to store a
creation/modification/destroy event in the database, holding who did
that to them.
I don’t know whether this may be helpful, but as starting point you
could have a look at acts_as_paranoid and see how it modifies AR to
deal with a special column in a really transparent way.
I’ll do. Thanks.
Here is the message I was writing before I read your advice :
Another solution, the with_session controller method :
PURPOSE
I’d like to write code like :
with_session do
inside this block, ALL models know the session.
…
end
Maybe we’d define the more general with the with_context controller
method :
with_context (object) do
inside this block, ALL models share the same context object.
…
end
Typical (scaffold) controller code with session context :
def update
with_session do
@model = Model.find(param[:id])
if @model.update_attributes(params[:model])
redirect_to :action => ‘show’, :id => @model.id
else
render :action => ‘edit’
end
end
end
And maybe the Model uses this session somewhere, maybe in a after_update
callback.
Or the views do. Who knowns. @session is available to all models, and
that’s the main purpose.
My own purpose is only storing who did update the model, so I’d write :
def update
@model = Model.find(param[:id])
if with_session do @model.update_attributes(params[:model]) end
redirect_to :action => ‘show’, :id => @model.id
else
render :action => ‘edit’
end
end
That’s the code I’d like to be able to write.
Now, how to have it work ?
IMPLEMENTATION
Controllers implementation
What could this with_context method look like ?
It has to be aware of other concurrent controllers running, so it has to
look like :
module ContextController
to be included in Controller classes
the general with_context method
def with_context (context)
(context) do
yield
end
end
the particular with_session
def with_session
with_context(@session) do yield end
end
end
Say we let ActiveRecord::Base be responsible of the synchronization :
module ContextController
def with_context(context)
ActiveRecord::Base.with_context(context) do
yield
end
end
def with_session
with_context(@session) do yield end
end
end
Now we have to define ActiveRecord::Base::with_context :
ActiveRecord implementation
class ActiveRecord::Base
the context available to ALL models
cattr_accessor :context
the with_context method
class << self
def with_context(context)
.synchronize do
@@context= context
yield
end
end
end
end
So we need a mutex.
require ‘thread’
class ActiveRecord::Base
cattr_accessor :context
class << self
def context_mutex
unless defined? @@context_mutex
@@context_mutex = Mutex.new
end
@@context_mutex
end
def with_context(context)
context_mutex.synchronize do
@@context= context
yield
end
end
end
end
WRAP IT ALL UP
If I wrap all this up, I have the lib/context_system.rb :
require ‘thread’
class ActiveRecord::Base
cattr_accessor :context
class << self
def context_mutex
unless defined? @@context_mutex
@@context_mutex = Mutex.new
end
@@context_mutex
end
def with_context(context)
context_mutex.synchronize do
@@context= context
yield
end
end
end
end
module ContextSystem
def with_context(context)
ActiveRecord::Base.with_context(context) do
yield
end
end
def with_session
with_context(@session) do yield end
end
end
Now, in controllers/application.rb :
require_dependency “context_system”
class ApplicationController < ActionController::Base
include ContextSystem
end
I’m now able to write code like I used at the beginning of this post.
And it works as expected.
FINAL WORD
What do you think ?
Of course all this synchronization stuff can’t be seen a perfectly clean
way of achieving our goal.
But ?
Hu ?
:o)