Chris Kampmeier
2007-Feb-08 04:29 UTC
How to make a per-request variable globally available?
I''m developing a single application that responds to requests from two domains. Various parts of my code need to work differently depending on the request''s domain. The typical solution would be to do something like this: class ApplicationController < ActionController::Base before_filter :initialize_site # makes @site available to all controllers and views def initialize_site @site = Site.find_by_domain(request.domain) end end class Site < ActiveRecord::Base def find_by_domain(domain) # ... end end The problem is that I''d really like to have access to @site in my models as well, because it would make my controllers much much more DRY. In this (contrived) example, imagine two seperate store-fronts, both selling widgets from the same manufacturer. But we want some widgets to appear in one store, and some in the other. class Widget < ActiveRecord::Base belongs_to :manufacturer belongs_to :site end class Site < ActiveRecord::Base has_many :widgets end class Manufacturer < ActiveRecord::Base # This is bad because Manufacturer.find(1).widgets contains # widgets from both sites has_many :widgets # I want to do the following instead, but I can''t because # @site is not available to models because it''s a controller # instance variable: has_many :widgets, :conditions => {:site_id => @site.id} end So I have to do things like this instead: class ManufacturersController < ApplicationController def show @manufacturer = Manufacturer.find(params[:id]) @widgets = @manufacturer.widgets.select do |widget| widget.site == @site end end end Having to do things like that in almost every action in my controllers is driving me nuts. It''s horribly un-DRY. I want to take care of the site filtering at the model level, so my controllers and views don''t even know the difference. So the question is, what''s the best-practice for making a variable available to all models, views, and controllers? A (gasp!) global variable? This variable needs to be set at the beginning of each request. Completely different approaches to solving this problem much appreciated as well. Thanks for reading! -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
> # makes @site available to all controllers and views > def initialize_site > @site = Site.find_by_domain(request.domain) > end > end >You can use Thread.current to pass data to your models. Something like this: def initialize_site @site = Site.find_by_domain(request.domain) Thread.current[:site] = @site end Aaron --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Keynan Pratt
2007-Feb-08 17:50 UTC
Re: How to make a per-request variable globally available?
I have been playing with the id of something similar and have come up with this. make a folder domains then for each domain add public, config and any other files you need to be site specific. then start 2 seperate fcgi proccesses if you set it up properly you can run two domains off the same application; and can put SITE in the enviroments folder -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
Chris Kampmeier
2007-Feb-08 18:39 UTC
Re: How to make a per-request variable globally available?
Aaron wrote:> You can use Thread.current to pass data to your models. Something > like this: > > def initialize_site > @site = Site.find_by_domain(request.domain) > Thread.current[:site] = @site > endThank you, that''s exactly what I needed. After I did that I extended ActiveRecord.Base::find to put my conditions in at the highest level: class Site < ActiveRecord::Base def Site.current Thread.current[:site] end end class Widget < ActiveRecord::Base class << self # keep a pointer to the old find alias :find_without_site :find # extend find to always return widgets for the current site def find(*args) self.with_scope(:find => {:conditions => {:site_id => Site.current.id}}) do self.find_without_site(*args) end end end end And it''s amazing. All unnecessary references to the site are gone from my controllers, and even the other models. Thanks again! -- Posted via http://www.ruby-forum.com/. --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---
> Thank you, that''s exactly what I needed.Your welcome> class Widget < ActiveRecord::Base > class << self > # keep a pointer to the old find > alias :find_without_site :find > > # extend find to always return widgets for the current site > def find(*args) > self.with_scope(:find => {:conditions => {:site_id => > Site.current.id}}) do > self.find_without_site(*args) > end > end > end > end > > And it''s amazing. All unnecessary references to the site are gone from > my controllers, and even the other models. Thanks again!Scoped finds are great, and overriding the default find will automatically scope your associations too. But be aware that the find_by convenience methods (find_by_name, find_by_id, etc...) are NOT scoped. Aaron --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk-unsubscribe-/JYPxA39Uh5TLH3MbocFFw@public.gmane.org For more options, visit this group at http://groups.google.com/group/rubyonrails-talk?hl=en -~----------~----~----~----~------~----~------~--~---