Deepak A
Deepak A

Reputation: 322

How to detect the subdomain from request in a gem file

I am trying to extend a gem file and create a library out of it. In the gem file I want to access the request parameter and find the specific sub-domain from it.

I have used request.subdomain over my application to access the sub-domain, but when i tried the same its results in an error.

This is how i access the local copy of gem in my project Gemfile

gem 'i18n-active_record', :path => '/home/myname/Downloads/i18n-active_record-master' ,:require => 'i18n/active_record'

This is the method i am trying to access the sub-domain

require 'rails'
require 'active_record'

module I18n
  module Backend

    class ActiveRecord
      class Translation < ::ActiveRecord::Base
        TRUTHY_CHAR = "\001"
        FALSY_CHAR = "\002"

        set_table_name 'translations'
        attr_protected :is_proc, :interpolations

        serialize :value
        serialize :interpolations, Array

        class << self
          def locale(locale)
            school_id = find_school
            scoped(:conditions => { :school_id => school_id.nil? ? nil : school_id, :locale => locale.to_s })
          end

          #for finding subdomain from request
          def find_subdomain
              subdomain = request.subdomain
              subdomain_id = Rails.cache.fetch([subdomain.hash,TRUTHY_CHAR.hash]){ School.find_by_subdomain(subdomain).id }
              return subdomain_id
          end


          def lookup(keys, *separator)
            column_name = connection.quote_column_name('key')
            keys = Array(keys).map! { |key| key.to_s }

            unless separator.empty?
              warn "[DEPRECATION] Giving a separator to Translation.lookup is deprecated. " <<
                "You can change the internal separator by overwriting FLATTEN_SEPARATOR."
            end

            namespace = "#{keys.last}#{I18n::Backend::Flatten::FLATTEN_SEPARATOR}%"
            scoped(:conditions => ["#{column_name} IN (?) OR #{column_name} LIKE ?", keys, namespace])
          end

          def available_locales
            Translation.find(:all, :select => 'DISTINCT locale').map { |t| t.locale.to_sym }
          end
        end

        def interpolates?(key)
          self.interpolations.include?(key) if self.interpolations
        end

        def value
          value = read_attribute(:value)
          if is_proc
            Kernel.eval(value)
          elsif value == FALSY_CHAR
            false
          elsif value == TRUTHY_CHAR
            true
          else
            value
          end
        end

        def value=(value)
          if value === false
            value = FALSY_CHAR
          elsif value === true
            value = TRUTHY_CHAR
          end

          write_attribute(:value, value)
        end
      end
    end
  end
end

I want the gem to auto-detect the sub-domain and act accordingly.

Upvotes: 0

Views: 614

Answers (2)

Kelvin
Kelvin

Reputation: 20867

It sounds like you'll need to store the current request in a thread variable. It's not the best practice, since it's like using global variables, but in this case there may not be another way.

Example: set up a before filter in your controller (in ApplicationController if you want it for all controllers & actions) that does this:

Thread.current[:current_request] = request

Then in the gem, just access the variable, making sure to handle the case when the value is nil. The problem is that the gem now relies on the filter. There are probably ways to make it cleaner, but that's out of scope for this question.

EDIT

A possible way to make this cleaner is to provide a class method in your gem to set the current request. The method will handle the current thread. Example:

class I18n::Backend::ActiveRecord::Translation
  THREAD_KEY = :i18n_backend_ar_trans_request

  class << self
    def current_request=(request)
      Thread.current[THREAD_KEY] = request
    end

    def with_current_request(request)
      Thread.current[THREAD_KEY] = request
      yield
    ensure
      Thread.current[THREAD_KEY] = nil
    end

    # This is for your gem to access the value.
    # It's your choice whether to make it private, but I recommend doing so.
    private
    def current_request
      Thread.current[THREAD_KEY]
    end
  end
end

In your around_filter:

def your_filter
  I18n::Backend::ActiveRecord::Translation.with_current_request(request) do
    yield
  end
end

Upvotes: 1

Litmus
Litmus

Reputation: 10996

Most likely the cause is that your request object is not accessible inside the gem. You can pass it as a parameter to find_subdomain method like this

def find_subdomain(request)
   subdomain = request.subdomain
   subdomain_id = Rails.cache.fetch([subdomain.hash,TRUTHY_CHAR.hash]){ School.find_by_subdomain(subdomain).id }
   return subdomain_id
end

You may want to dissect and explore this gem subdomain-fu for more

Upvotes: 0

Related Questions