Reputation: 47
I'm working on ecommerce solution, which provides few different shops within a single rails application.
There I have a class to hold shop-specific settings.
# models/shop.rb
class Shop < Settingslogic
source "#{Rails.root}/config/shop.yml"
def self.init!(shop)
namespace shop.to_s
load!
end
end
Shop::init! loads a specified section from config file
Here is yml file just in case:
# config/shop.yml
shop_1:
shop_name: Shop 1
shop_2:
shop_name: Shop 2
I match requested shop by domain name in application controller.
# controllers/application_controller.rb
class ApplicationController < ActionController::Base
before_filter :set_shop
protected
def set_shop
Shop.init! if request.domain.match(/^.*shop1\.com$/)
:shop_1
elsif request.domain.match(/^.*shop2\.com$/)
:shop_2
end
end
end
The problem is that Shop somehow keeps settings for the first requested shop between requests. For example:
shop1.com
Shop.shop_name # => Shop 1
shop2.com
Shop.shop_name # => Shop 1
And it keeps initial settings until I make some change to either application_controller.rb or shop.rb. (I think it's a first key to the answer)
What I know is that Settingslogic defines attr_accessor for each option in shop.yml (only when first called though). But why they still work after reloading (I work in development environment with cache_classes = false)
When I do that way:
# models/shop.rb
class Shop < Settingslogic
source "#{Rails.root}/config/shop.yml"
namespace "shop_#{Random.rand(1..2)}"
load!
end
it works as expected - it loads appropriate settings everytime. But I can't set shop name by domain on this stage. Also I'm not able to test different shops when I can't specify shop_name from the outside. So I need to have a method.
I tried class_eval
def self.init!(shop)
class_eval do
namespace shop.to_s
load!
end
end
but it didn't help. I feel a lack of knowledge on scoping here. Do you have ideas what's wrong? Thanks in advance.
Upvotes: 1
Views: 172
Reputation: 47
I finally came up with a solution on how to stick to initial plan mentioned in question, but to not have "class caching" problem.
It is based on the answer to my another question on this topic.
Anyway here is new Shop.rb
class Shop < Settingslogic
source "#{Rails.root}/config/shop.yml"
load!
def self.current=(shop)
Thread.current[:current_shop] = shop.to_sym
end
def self.current
self.send(Thread.current[:current_shop])
end
end
I decided to load all sections of shop.yml and access them through Shop::current which forwards the call to appropriate section.
So I can use Shop.current.name
and Shop.current.tax
anywhere and it corresponds to the shop which was set in applicationController or spec_helper.
Upvotes: 0
Reputation: 84114
There is no reason that class attributes shouldn't be preserved until they are next updated.
In development mode in a rails app, your code is reloaded when it has changed, which will have the side effect of resetting those class variables, but you don't want to rely on that.
What your before filter should be doing is setting a @current_shop instance variable that contains the shop to use for the current request.
Upvotes: 0