How to access session, cookies, params, request in Model !! 20

Posted by pratik
on Thursday, October 18

For fuck’s sake, STOP asking this question in IRC

You’re completely/totally/fucking wrong if you want to access sessions, params, cookies, etc. in your Models.

You can’t get more wrong than this in rails world probably. But some people just don’t get it. So if you must, this is how you can do it ( I’d suggest you go back to PHP or whatever ) :

Add this code at the bottom of your application.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class ApplicationController < ActionController::Base
  # Your existing stuff
  around_filter :you_dont_have_bloody_clue
  
  protected
  
  def you_dont_have_bloody_clue
    klasses = [ActiveRecord::Base, ActiveRecord::Base.class]
    methods = ["session", "cookies", "params", "request"]
    
    methods.each do |shenanigan|
      oops = instance_variable_get(:"@_#{shenanigan}") 
      
      klasses.each do |klass|
        klass.send(:define_method, shenanigan, proc { oops })
      end
    end
    
    yield
    
    methods.each do |shenanigan|      
      klasses.each do |klass|
        klass.send :remove_method, shenanigan
      end
    end
    
  end
end

Again, do it if you wish, just don’t ask anyone how to do it ever again!

Comments

Leave a response

  1. RSLOctober 18, 2007 @ 12:34 AM

    Yr code shenanigans always make me laugh.

  2. Jack DangerOctober 18, 2007 @ 08:07 AM

    “methods.each do |shenanigan| ” nearly made me cry. That’s frickin’ awesome.

  3. MislavOctober 18, 2007 @ 09:41 AM

    Haha :) Good job Pratik, you’re teaching them crap :P

  4. Tom WardOctober 18, 2007 @ 05:20 PM

    For your next article I propose: ‘How to query the database directly within .erb’

  5. GarryOctober 19, 2007 @ 10:29 PM

    Or you could do something like this:

    http://scie.nti.st/2007/2/1/let-activerecord-observers-see-controller-context

    Since the behavior of an observer may depend in which context it was invoked, I’ve used code like that on a couple of occasions.

  6. TDOctober 23, 2007 @ 02:20 PM

    LOL thank u ive been looking 4 this 4 ages…... do you know y doesnt this works? Can u help me???

    <%= link_to_remote ‘Read article’, :controller => ‘articles’, :class => ‘ajax’, :action => ‘get_article_js’, :onclick => ‘return false’, :update => $(‘article_#{article.id}’).innerHTML %>

    Im making an AJAX desktop!!

  7. hemant kumarOctober 24, 2007 @ 08:33 PM
    Somebody on #ruby-lang actually mentioned a plugin called Goldberg, which made it still simpler and you would be surprised by its ingenuity. They had a class called Goldberg and before_filter in application.rb and in the callback, they used to store session as a class attribute in Goldberg class and now in models they used to do this:
     class Book < ActiveRecord::Base
        def self.my_books
           # use Goldberg.user to have access to current_user object which was copied from session and saved as a class attributed
         end
     end
    
    I literally pulled my hair out and asked myself why?
  8. August LilleaasNovember 16, 2007 @ 10:01 AM

    I enjoy cache sweepers. Look at this:

    class AuthorStampSweeper < ActionController::Caching::Sweeper
      observe Task, Project, User, Log, UrMom
    
      def before_create(record)
        record.creator = current_user
      end
    
      def before_update(record)
        record.updater = current_user
      end
    end
    

    Yay! And you have to call it from the controller, so everything makes a lot of sense. I prefer this method instead of passing the user id to some accessor in the model or something.

  9. RSLNovember 29, 2007 @ 02:40 PM

    @ August Lilleaas. Except that passing the user’s id to the model is the right [and elegant] way to do this.

  10. HeresTomWithTheWeatherDecember 03, 2007 @ 02:27 AM

    i don’t don’t see anything wrong with accessing the sessionid in a model to help prevent against csrf attacks.

    cheers, tom

  11. PratikDecember 03, 2007 @ 09:41 PM

    HeresTomWithTheWeather – U-hum..could you please shed some more light on that ?

  12. HeresTomWithTheWeatherDecember 04, 2007 @ 02:59 AM

    suppose we add a hidden :action_token field in a form_for. the value of the field is derived from the session id (for simplicity, let’s assume it’s just the session id), so let’s add attr_accessor :action_token to the model.

    how will we set the session id to the action token field for new and edit? we could do it in the controller for new():

    @request = Request.new @request.action_token = session.session_id

    and again for edit():

    @request = Request.find(params[:Id]) @request.action_token = session.session_id

    and when we process the submitted action token, we could do this in create():

    @request = Request.new(params[:request]) process_forgery() if @request.action_token != session.session_id

    and again for update():

    @request = Request.find(params[:id]) process_forgery() if @request.action_token != session.session_id

    or we could just explicitly add methods to the model so we don’t have to add all this stuff to the controller.

    in theory, we could use Garry’s observer idea. i had trouble getting after_initialize() to work with STI, though.

    -tom

  13. PratikDecember 04, 2007 @ 11:39 AM

    Probably you should check out Rails built in CSRF protection mechanism.

  14. HeresTomWithTheWeatherDecember 07, 2007 @ 06:13 PM

    now that 2.0 is out, i can!

    http://weblog.rubyonrails.org/2007/12/7/rails-2-0-it-s-done

    thanks, tom

  15. simonApril 10, 2008 @ 02:38 PM

    Ok i can see how breaking MVC is bad. But how would you solve the issue of recording modified_by and created_by where most tables have those columns and you are looking for a auto pain free way of taking the current_user and putting there id or login into those fields???? That is a very real problem and i don’t feel like having Model.modified_by = current_user all over the place be rather prefer it to work like updated_at and created_at

  16. ChrisApril 23, 2008 @ 11:14 PM

    How about the simple task of Auditing changes? On a website where Users make changes to all kinds of objects I want to store the changes in a SystemLogs table.

    A model with no reference to a User object whatsoever (or atleast not directly) gets changed by a user. Now whenever Model.save is called in the controller I want to create a SysLog record/instance with the changes and the user_id of the calling user.

    To do this from the controller violates the DRY principle, as every .save / .update call from different controllers need to be followed by a specific creation of a SysLog object.

    in my specific case auditing causes the DRY principle to conflict with the MVC model

  17. gwMay 31, 2008 @ 08:52 AM

    I have examples where models writing to a session is the only thing that makes any real sense from an API and DRY standpoint as well. IMO, a session is not solely the domain of the controller. It is a system wide bucket for storing state during a visit. It should be available to the entire framework. Can it be abused? Sure, so can every tenet of programming. Considering the wide opinions on just about every facet of OOP details, it cannot be claimed to be absolutely wrong for both controllers and models to make use of a session store.

    Because Rails doesn’t allow it, there’s some ugly APIs that result for those cases where automated handling of tasks by the framework or a service layer just on top of it are handled. the two examples above are legit IMO, and I have at least one other.

  18. JimJune 09, 2008 @ 03:34 AM
    class ApplicationController < ActionController::Base
    before_filter :set_current_user
    private
    def set_current_user
      UserActionObserver.current_user = current_user
    end
    end
    
    class UserActionObserver < ActiveRecord::Observer
      # assuming these all have created_by_id and updated_by_id
      observe Product, Thing, OtherThing
    
      cattr_accessor :current_user
    
      def before_create(model)
        model.created_by = @@current_user
      end
    
      def before_update(model)
        model.updated_by = @@current_user
      end
    end
    
  19. Hive MindJune 19, 2008 @ 09:17 AM

    Pratik, you are being a royal cock about this.

    Surely, adding your own custom logging to a rails app, WITHOUT having to go mess with the controller methods that call save on your model in order to pass the username stored in the session to the model by just making the session data available to the model is WAAAAY more DRY and elegant than going back and fucking around with controller methods.

    You dumbass.

  20. Seth Thomas RasmussenJuly 15, 2008 @ 05:08 PM

    I don’t understand the appeal of the observer examples. What is wrong with going through the current_user’s association proxies to build and create new records in the controller? It makes the code too clear? The logic isn’t split up into enough files and classes with implicit behavior?

Comment