Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top