Forum: Ruby on Rails LoginSystem : make @session available to models

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
6232c6cddbfa61379ba7d54bb9eccab2?d=identicon&s=25 lagroue (Guest)
on 2006-04-06 12:51
Hello.

LoginSystem is cool, it populates the @session instance variable of
controllers with many useful info.

The fact is : it would be cool to let models know about the session,
too.
For instance, after_update and after_create callbacks could store *who*
did *what* on *what.

Aim : having ActiveRecord::Base::session defined, returning the @session
of the controller which manipulates the model.

Here's a way to do that. I was wondering whether it was good ruby and
good rails because I'm a real newbie. I don't know either if the class
member I added to ActiveRecord::Base doesn't break when several users
use the application, leading to multiple sessions.

First, let controllers give @session to models :

(In controllers/application.rb)
class ApplicationController < ActionController::Base
  include LoginSystem
  before_filter :export_session_to_models
  def export_session_to_models
    ActiveRecord::Base.set_session { @session }
  end
end

Now, let models know the set_session method, and let's define
ActiveRecord::Base::session:

(In controllers/application.rb)
# This unless is required for proper behavior in FastCGI context
unless defined? ActiveRecord::Base::session
  class ActiveRecord::Base
    @@session = nil
    class << self
      def set_session(&action)
        @@session = action
      end
      def session
        @@session.call()
      end
    end
    def session
      self.class.session
    end
  end
end

Is this code OK ?

---

Now that this framework is set, I can store who did what on what on any
database update :

(In controllers/application.rb)
class ActiveRecord::Base
  after_update :update_history
  after_create :create_history
  before_destroy :destroy_history

  def update_history
    history ('update')
  end
  def create_history
    history ('create')
  end
  def destroy_history
    history ('destroy')
  end
  def history(action)
    unless kind_of?(History)
      history = History.new(:action => action)
      history.ref = self
      history.save
    end
  end
 end

With, roughly, a History model like :
:user_id # who performed the action
:action # the action
:date # when
:ref_table # the table of the altered model
:ref_id # the id of the altered model

(In models/history.rb)
class History < ActiveRecord::Base
  def ref=(object)
    write_attribute('ref_table', object.class.table_name)
    write_attribute('ref_id', object.id)
  end

  before_create :fix_history
  before_update :fix_history

  def fix_history
    write_attribute('user_id', session[:user].id) if user_id.nil?
    write_attribute('date', Time.now) if date.nil?
  end
end
6232c6cddbfa61379ba7d54bb9eccab2?d=identicon&s=25 lagroue (Guest)
on 2006-04-06 13:25
lagroue wrote:
> Hello.
>
> LoginSystem is cool, it populates the @session instance variable of
> controllers with many useful info.
>
> The fact is : it would be cool to let models know about the session,
> too.
> For instance, after_update and after_create callbacks could store *who*
> did *what* on *what.
>
> Aim : having ActiveRecord::Base::session defined, returning the @session
> of the controller which manipulates the model.

OK. Thanks to http://www.koziarski.net/archives/2005/07/16/environment
I understand things better.

LoginSystem has nothing to do with that.

class ActiveRecord::Base
  cattr_accessor :session
end

class ApplicationController < ActionController::Base
  before_filter :export_session_to_models
  def export_session_to_models
    ActiveRecord::Base.session= @session
  end
end

Is much sufficient.


My history framework should read :

(in controllers/application.rb)

class ActiveRecord::Base
  cattr_accessor :session
end

# this is the actual unless required by FastCGI
unless defined? ActiveRecord::Base::history
  class ActiveRecord::Base
    after_update :update_history
    after_create :create_history
    before_destroy :destroy_history
    def update_history
      history ('update')
    end
    def create_history
      history ('create')
    end
    def destroy_history
      history ('destroy')
    end
    def history(action)
      unless kind_of?(History)
        history = History.new(:action => action)
        history.ref = self
        # without the fastcgi unless, the number of historics saved
grows !!
        history.save
      end
    end
  end
end
675475d0b65710be6d992eb5eb2c61c2?d=identicon&s=25 Gregory Seidman (Guest)
on 2006-04-06 14:31
(Received via mailing list)
On Thu, Apr 06, 2006 at 12:51:01PM +0200, lagroue wrote:
[...]
} The fact is : it would be cool to let models know about the session,
} too.
[...]

This should be a FAQ. It gets asked all the time. As such, I have added
it
to the FAQ at http://wiki.rubyonrails.com/rails/pages/FAQ#mvc

--Greg
A5d8283732da86365736508fef6c1c67?d=identicon&s=25 Nuno (Guest)
on 2006-04-06 15:52
Good idea Lagrou, I've just tried your solution :

class ActiveRecord::Base
  cattr_accessor  :account
  after_update    :update_log

  def update_log
    puts("store to log here")
  end
end

but there's something strange, update_log method is called SIX times
even if there's only ONE @article.save call in article_controller

And the more I cycle throught list->post (then save)->list the more
update_log is called (14 times on second cycle)

I don't understand why

Did you had this problem too ?
6232c6cddbfa61379ba7d54bb9eccab2?d=identicon&s=25 lagroue (Guest)
on 2006-04-07 09:52
Nuno wrote:
> Good idea Lagrou, I've just tried your solution :
>
> class ActiveRecord::Base
>   cattr_accessor  :account
>   after_update    :update_log
>
>   def update_log
>     puts("store to log here")
>   end
> end
>
> but there's something strange, update_log method is called SIX times
> even if there's only ONE @article.save call in article_controller
>
> And the more I cycle throught list->post (then save)->list the more
> update_log is called (14 times on second cycle)
>
> I don't understand why
>
> Did you had this problem too ?

Yeah, I do have it.

I thought, by mistake, that this unless defined?
ActiveRecord::Base::history (unless defined?
ActiveRecord::Base::update_log) would fix that.

More investigation lead me to that fact :

When ActiveRecord::Base is altered in controllers/application.rb :
- callbacks are triggered too many times on FastCGI mode (too many
times, precisely, an increasing number of times at each request. The
tracebacks are the same for each spurious callback trigger)
- callbacks are NOT triggered on CGI mode.

So the main problem may be controllers/application.rb.

I'm investigating oher ways to inject code.


Thanks for your answer !
6232c6cddbfa61379ba7d54bb9eccab2?d=identicon&s=25 lagroue (Guest)
on 2006-04-07 10:15
OK.

So far, I came up with this solution for my session/history feature :

I defined lib/history_system.rb :

class ActiveRecord::Base
	cattr_accessor :session
end


module HistoryModel

	def update_history
		history ('update')
	end

	def create_history
		history ('create')
	end

	def destroy_history
		history ('destroy')
	end

	def history(action)
		unless kind_of?(History)
			history = History.new(:subtype => action)
			history.ref = self
			history.save
		end
	end
end

module HistorySystem

	protected

	def prepare_history
		ActiveRecord::Base.session= @session
	end
end


In controllers/applications.rb :

require_dependency "login_system"
require_dependency "history_system"

class ApplicationController < ActionController::Base
	include LoginSystem
	model :user

	include HistorySystem
	before_filter :prepare_history
end


In *every* models/model.rb I want its model to have history features :
(I gave up the idea of giving it to every ActiveRecord::Base subclass)

require_dependency "history_system"

class <Model> < ActiveRecord::Base

	include HistoryModel
	after_update :update_history
	after_create :create_history
	before_destroy :destroy_history

end

The History model has already been described in a post before.



So. That works, in CGI and FastCGI modes, there is no longer too many
calls, BUT I could not inject that code in any ActiveRecord::Base
subclass.

Consider that as further exercice !

Thanks again.
D893e113b51a8f200d2abb3ed9e54143?d=identicon&s=25 Gazoduc (Guest)
on 2006-04-07 10:56
(Received via mailing list)
lagroue <lagroue@...> writes:
> module HistorySystem
>
> 	protected
>
> 	def prepare_history
> 		ActiveRecord::Base.session=  <at> session
> 	end
> end

Isn't this going to break if you have two concurrent requests on the
same
process (that is sharing the same class variables) ?

I posted a solution to this issue here :
http://www.ruby-forum.com/topic/60732#58279

It's the stuff with secure { }
6232c6cddbfa61379ba7d54bb9eccab2?d=identicon&s=25 lagroue (Guest)
on 2006-04-08 09:50
Gazoduc wrote:
> lagroue <lagroue@...> writes:
>> module HistorySystem
>>
>> 	protected
>>
>> 	def prepare_history
>> 		ActiveRecord::Base.session=  <at> session
>> 	end
>> end
>
> Isn't this going to break if you have two concurrent requests on the
> same
> process (that is sharing the same class variables) ?
>
> I posted a solution to this issue here :
> http://www.ruby-forum.com/topic/60732#58279
>
> It's the stuff with secure { }

That's exactly the kind of answer I was waiting.

I admit I have difficulties finding clear documents explaining how
(when) Rails creates instances. On CGI mode, a new ruby process is
launched, so I *guess* there is no persistency whatsoever except the
@session which *may* be marshalled *somewhere*.

But in FastCGI mode, I'm still blinded by a total fog.

I *infer* theories on Rails from the odd behaviors it shows when I try
some edgies solutions, but I still miss a reference document. *sigh*

Thanks for your solution, I'm gonna read it right now.
This topic is locked and can not be replied to.