views:

35

answers:

2

I am in the middle of migrating my application from using subdirectories for userspace to subdomains (ie. domain.com/~user to user.domain.com). I've got a method in my user class currently to get the "home" URL for each user:

class User
  def home_url
    "~#{self.username}"

    # How I'd like to do it for subdomains:
    #"http://#{self.username}.#{SubdomainFu.host_without_subdomain(request.host)}"
  end
end

I'd like to update this for subdomains, but without hardcoding the domain into the method. As you can see, I am using the subdomain-fu plugin, which provides some methods that I could use to do this, except that they need access to request, which is not available to the model.

I know it's considered bad form to make request available in a model, so I'd like to avoid doing that, but I'm not sure if there's a good way to do this. I could pass the domain along every time the model is initialized, I guess, but I don't think this is a good solution, because I'd have to remember to do so every time a class is initialized, which happens often.

+1  A: 

The model shouldn't know about the request, you're right. I would do something like this:

# app/models/user.rb
class User
  def home_url(domain)
    "http://#{username}.#{domain}"
  end
end

# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  # ...

  def domain
    SubdomainFu.host_without_subdomain(request.host)
  end

  # Make domain available to all views too
  helper_method :domain
end

# where you need it (controller or view)
user.home_url(domain)

If there is such a thing as a canonical user home URL, I would make a configurable default domain (e.g. YourApp.domain) that you can use if you call User#home_url without arguments. This allows you to construct a home URL in places where, conceptually, the "current domain" does not exist.

molf
A: 

While molf's answer is good, it did not solve my specific problem as there were some instances where other models needed to call User#home_url, and so there would be a lot of methods I'd have to update in order to pass along the domain.

Instead, I took inspiration from his last paragraph and added a base_domain variable to my app's config class, which is the set in a before_filter in ApplicationController:

module App
  class << self
    attr_accessor :base_domain
  end
end

class ApplicationController < ActionController::Base
  before_filter :set_base_domain

  def set_base_domain
    App.base_domain = SubdomainFu.host_without_subdomain(request.host)
  end
end

And thus, when I need to get the domain in a model, I can just use App.base_domain.

Daniel Vandersluis