Models accessing the session

Gazoduc wrote:

lagroue wrote:

Gazoduc, your secure method gives the session to your Documents, but not
to their members. Like, say, this_document.writer which is a model,
and which should be “secured”, too. The “secure” fonction fails doing
that.

This was solved by implementing a secure method in the base class of my
model and doing all the “children” stuff by hand.

I then have some code in validate_on_update and validate_on_create
making sure my object has passed through “secure” before it goes into
the DB, so if I forget to overwrite some Rails clever stuff for
retrieving data, it breaks on save and my model stays “secured”.

:o) I understand

Besides checking for user read/write/publish access, I also use this
“secure” pipeline to manage multiple languages, passing the desired one
with user id and groups.

Good idea

If Webrick and Co are only serving one request per thread, then the
easiest piece of code for all this is:
$session = session

Isn’t it ?

Well, yes.
This is just like PHP coding, though.

Ben M. wrote:

I could be totally wrong though…

Hi Ben. I think that a controller instance is created per request - it
would be very blatantly unthreadsafe not to do that.

There is a class variable in ActionController::Base, allow_concurrency,
which is normally set to false. The comment on it says:

 # Controls whether the application is thread-safe, so

multi-threaded servers like WEBrick know whether to apply a mutex
# around the performance of each action. Action Pack and Active
Record are by default thread-safe, but many applications
# may not be. Turned off by default.
@@allow_concurrency = false
cattr_accessor :allow_concurrency

…but AWDR says Rails is not thread-safe, and Zed, in a recent
discussion of threading, said “Mongrel has to use a “grand mutex” around
the Dispatcher.dispatch in order to prevent Rails from going crazy.
Rails just isn’t thread safe”.

So I’m puzzled. It might be that just the use of the database connection
needs protection… and there has been discussion from time to time
about having a connection pool.

I sympathise with your desire for a simple container.

regards

Justin

Justin F. wrote:

So I’m puzzled. It might be that just the use of the database connection
needs protection… and there has been discussion from time to time
about having a connection pool.

I sympathise with your desire for a simple container.

regards

Justin

So we have to keep our design clean of globals of any kind, be they
class variables or ruby $globals. This should not hurt. Betting on the
reverse is a very dangerous attitude which could lead to very hard to
resolve bugs… months later when we forgot about this little “global”
hack.

Let’s keep globals out of our rails.

Justin F. wrote:

through at the time.
While the mutex slows things down, the use of a single process makes
other things easier. For example, WEBrick servlets are the only runtime
that make it safe to use the memory-based stores for sessions and caches.

FastCGI and SCGI use a pool of processes, each of which handles one
request at a time.

However, it’s never been clear to me what is not thread-safe in Rails.

Even though I know absolutely nothing about ActionPack internals, I
would still hazard a
guess: instance vars. The ability to have all those @vars holding stuff
for us from the
controller to the view makes things very easy for us. But that
flexibility comes at a
price… one of the first things you learn in Java servlet programming
is NO instance vars!

I could be totally wrong though…

b

These are just global variable with syntactic sugar.

On 4/10/06, lagroue [email protected] wrote:

Hash)

- a “local”, thread-safe way

end

Those methods are to be used as filters :

require_dependency “context_system”

include ContextSystem

require ‘thread’
def with_context(context)
def context
ActiveRecord::Base.with_context(context) do
end
def context
def store_context(context)
ActiveRecord::Base.context = nil
context[:session]
with_context({:session => @session}) do yield end
Rails mailing list
[email protected]
http://lists.rubyonrails.org/mailman/listinfo/rails


Tobi
http://shopify.com - modern e-commerce software
http://typo.leetsoft.com - Open source weblog engine
http://blog.leetsoft.com - Technical weblog

Based on the generous information many of you gave, here’s my own
attempt, lib/context_system.rb
(I apologize for the poor English of my documentation).

Here we define a way for models to access a context set by the

controllers.

We add those methods to ActiveRecord::Base instances :

- context the context object

- context_session same as context[:session] (context has to be a

Hash)

- context_user same as context[:session][:user]

(defined in a LoginSystem context)

Those methods raise unless controllers had the context set before.

The context can not be nil.

Controllers who include the ContextSystem module have two ways to

define

the context :

- a “local”, thread-safe way

- a “global”, not thread-safe way

Both are always available, but the “global” methods will raise if

ActionController::Base.allow_concurrency is true.

Thus the “local” way is best practice.

-----

Let’s describe the two ways :

1) “local” methods

In your controller code, give with_context or with_session methods a

block.

Inside this block, the given context will be available to all

ActiveRecord::Base instances.

- with_context(object) { … } stores object as the context

- with_session { … } stores {:session => @session} as the

context

Example :

def update

with_session do

# update and save a model.

model.modifier_id = model.context_user.id

model.save

end

model.context # raises since we’re out of the with_session block

end

If required by ActionController::Base.allow_concurrency, a mutex makes

sure

that no more than one with_context or with_session block runs at

a given moment.

If not required, no mutex is used.

2) “global” methods

Those methods are to be used as filters :

- store_context may be called on before_filter

- store_session may be called on before_filter

- reset_context should be called on after_filter

They do not require the controller to use the local methods

with_context

or with_session for all models to know about the context.

Exemple :

require_dependency “context_system”

class FooController < ActionController::Base

def update

# update and save a model.

# no surrounding with_session block, but this line works :

model.modifier_id = model.context_user.id

model.save

end

include ContextSystem

before_filter :store_session

after_filter :reset_context

end


if ActionController::Base.allow_concurrency

thread safe mode

require ‘thread’

class ActiveRecord::Base

class << self

  def context_mutex
    @@context_mutex = Mutex.new unless defined? @@context_mutex
    @@context_mutex
  end

  def with_context(context)
    # protect the context with a mutex
    context_mutex.synchronize do
      @@context= context
      result = yield
      @@context= nil
      result
    end
  end
end

def context
  raise(RuntimeError, "Missing with_context or with_session block in 

Controller method.", caller) unless defined?(@@context) &&
!@@context.nil?
@@context
end
end

module ContextSystem

def with_context(context)
  ActiveRecord::Base.with_context(context) do
    yield
  end
end

def store_context(context)
  raise "Not allowed when ActionController::Base.allow_concurrency 

is true"
end

def reset_context
end

end

else

not thread safe mode

class ActiveRecord::Base

cattr_writer :context

def context
  raise(RuntimeError, "Missing store_context or store_session 

before_filter in Controller class, or with_context or with_session block
in Controller method.", caller) unless defined?(@@context) &&
!@@context.nil?
@@context
end
end

module ContextSystem

def store_context(context)
  ActiveRecord::Base.context = context
end

def reset_context
  ActiveRecord::Base.context = nil
end

def with_context(context)
  ActiveRecord::Base.context = context
  result = yield
  ActiveRecord::Base.context = nil
  result
end

end
end

session an user mappings on context : thread-independant

class ActiveRecord::Base

def context_session
context[:session]
end

def context_user
context[:session][:user]
end
end

module ContextSystem

def with_session
with_context({:session => @session}) do yield end
end

def store_session
store_context({:session => @session})
end
end

Tobias Lütke wrote:

These are just global variable with syntactic sugar.

Yeah.
Except code build around my “syntactic sugar” will raise if it has been
done in the lazy way, AND that Rails becomes concurrent.

Of course I’d love to have more experience, and dig into ActiveRecord
API to implement such feature without globals.

So, sweety, it’s up to you now.