Support for multiple domains/subdomains in Rails - ruby-on-rails

I have a Rails app that has a similar setup to Tumblr, that is, you can have either:
(1) Subdomain hosting (your-username.myapp.com)
(2) Domain hosting (your-username.com)
Both would forward to a personalized website for that user, created with my application.
How can I accomplish this in Rails? I have been able to get (1) working with subdomain-fu, but I'm not sure how to get (2) working. Any pointers (plugins, gems, tutorials), etc. would be greatly helpful, I can't seem to find any.
Thanks!

The principle for domains is the same as the subdomain - find the domain, map to an account.
The details will depend on how your hosting is going to handle the DNS.
I am currently using Heroku and its wildcard service.
In this case, the domain is mapped with a cname to the subdomain hosted by my Heroku app. From here I can work out the associated account and details.

EDIT: I've found a much easier way: http://www.arctickiwi.com/blog/7-host-and-domain-based-routing-in-ruby-on-rails
Not exactly an answer but this is the best I can give. Maybe this'll help you too.
Ideally, this blog post from transfs.com and subdomain-fu should do the trick. I've been trying to implement it, however, and they don't seem to play nicely together.
Basically, if I don't include the intiializer, the subdomain route works fine. If I include the initializer, the subdomain route breaks (everything gets caught by map.root). I have a feeling it's with the way it builds the condition string in the initializer. If you can figure out how it breaks, then you'll have a working app.
My initializer:
module ActionController
module Routing
class RouteSet
def extract_request_environment(request)
env = { :method => request.method }
env[:domain] = request.domain if request.domain
env[:host] = request.host if request.host
env
end
end
class Route
alias_method :old_recognition_conditions, :recognition_conditions
def recognition_conditions
result = old_recognition_conditions
[:host, :domain].each do |key|
if conditions[key]
operator = "==="
if conditions[key].is_a?(Regexp)
operator = "=~"
end
result << "conditions[:#{key.to_s}] #{operator} env[:#{key.to_s}]"
end
end
result
end
end# end class Route
end
end
My routes (just for development). You'll see my local development domain, stiltify.dev. Sorry, I tried to make it look good in here but I couldn't get the code block to look nice. I put it on pastie instead: http://pastie.org/940619.
The comments section in Ryan Bates' screencast was very helpful, and got me to figure out the subdomain => false and the other errors they were getting into. Still didn't fix the problem though!

Related

Accessing request context object in rails router

I need to create a few dynamic routes in my rails router in the following way:
Rails.application.routes.draw do
account = Account.find_by(
subdomain: request.subdomain,
domain: request.domain
)
EditableField.where(account_id: account.id).links.each do |link|
get link.link_href, to: link.method
end
end
As shown above, I need to determine the account based on the request domain and subdomain, however I cant find how to access the request object in the rails router. What is the correct way to do this ?
I would suggest using the rack-rewrite gem
https://github.com/jtrupiano/rack-rewrite
It is in lower level and give you much more options to do this kind of dynamic routing
And if you still want to do that in the routes.rb file, you can follow this answer https://stackoverflow.com/a/24411835/2529330
Enjoy :)

User based custom subdomains

I've tried to research and figure out how people are doing this without much success.
For my rails project I have projects that each have their own slug. The user can themselves dictate the slug and that slug will be the subdomain.
Now I'm trying to figure out how to do this routing wise and make it work in production as-well.
I have this now:
get '/', to: 'posts#index', constraints: { subdomain: /.+/, via: [:get] }, as: :feed
At a controller level I do:
before_action :get_project, only: :index
...
def get_project
unless #project ||= Project.find_by_slug(request.subdomain)
redirect_to root_url
end
end
When I test this on localhost using lvh.me it works. However when I try to do this on a live production server and check server logs, Rails return this:
ActionController::RoutingError (No route matches [OPTIONS] "/"):
Does anybody have any experience with this?
As for why your current code doesn't work in production you'd need to give us your web server's configuration if we are to help you debug it since that is probably the main difference between the two environments. Especially if your app is served behind a reverse proxy. But you may be able to solve this using this gem. Their implementation of this covers several cases and simplifies what your trying to achieve and may end up working in production. It allows mapping of subdomain routes in several ways that really saves a lot of time. What you might be really interested in is this section that talks about mapping routes with active record.

Ruby on Rails match routing error

I'm trying to make a Rails application that serves simple static HTML pages. I followed Mikel's tutorial here (it involves making a Pages controller and setting up some routing) but I keep getting an error message.
I made a app/views/site/pages/_about.html.erb file to contain my About page. After starting the rails server, I try to go to http://localhost:3000/about/ but it gives me a Routing Error because I have an "uninitialized constant Site."
My project is uploaded to GitHub if you want to take a look at the code.
Edit: here's my config/routes.rb file:
NINAgallery::Application.routes.draw do
match ':page_name' => 'site/pages#show'
end
And here's the important part of my app/controllers/pages_controller.rb file:
class PagesController < ApplicationController
layout 'site'
def show
#page_name = params[:page_name].to_s.gsub(/\W/,'')
unless partial_exists?(#page_name)
render 'missing', :status => 404
end
end
# extra code for handling 404 errors goes here
end
site/pages#show means the show action in Site::PagesController
You either need to put your controller in the namespace your routes imply or change the route
The last line in the PagesController is this:
ValidPartials = Site::PagesController.find_partials
That means that the PagesController is contained in a Site module. But there is no Site module in your app.
I think simple removing Site:: should fix the problem:
ValidPartials = PagesController.find_partials
Plus the route:
match ':page_name' => 'pages#show'
Your application is called NINAgallery.
Replace Site in pages_controller.rb line 27 by NINAgallery.
PS:
I just took a peek at the so-called tutorial. You are taking really really really bad habits.
Some resources to take very good basics:
http://guides.rubyonrails.org/
http://api.rubyonrails.org/
If you like tutorials: http://ruby.railstutorial.org/
And there are plenty of books about rails. All good.
Besides the namespace problem, you also needed to add the 'app' Gem to the Gemfile, as explained in the tutorial.
I don't know why you removed the caching of the static pages in your working code. I made a pull request with the app working and maintaining the cache problem. If another person is interested, the code is here
Also ryan bates has a tutorial called "Semi static pages" that does something similar. I would encourage you to follow his solutions because there are very rarely mistaken.

How to get the subdomain value from a url?

how can I get the subdomain value in rails, is there a built-in way to do this?
e.g.
test123.example.com
I want the test123 part of the url.
Rails 3.0 has this capability built-in, you can access the subdomain from request.subdomain.
You can also route based on the subdomain:
class SupportSubdomain
def self.matches?(request)
request.subdomain == "support"
end
end
Basecamp::Application.routes do
constraints(SupportSubdomain) do
match "/foo/bar", :to => "foo#bar"
end
end
If you're using 2.3, you'll need to use a plugin such as subdomain-fu.
Use the following method inside your controller
request.subdomains
This Returns an array of subdomains
account_location is also a good plugin. After using it, you can find the account based on different subdomains. And you can find out subdomain from url just by writing request.subdomains(0).first in your code.
In case you are working with a string, and assuming it can be a true URI, you can do this to extract the subdomain.
require 'uri'
uri = URI.parse('http://test123.example.com')
uri.host.split('.').first
=> "test123"
https://stackoverflow.com/a/13243810/3407381
Simple in your controller just do the following
unless request.subdomains.any?
#No domains available redirect
redirect_to subdomain: 'www'
end
You can use the SubdomainFu plugin. This plugin gives you a method current_subdomain which returns the current_subdomain of your app.
You can also have a look at this Railscast
UPDATE
You can also use request.subdomains this will give you an array of subdomains.
For anyone looking to get the subdomains on localhost using WEBrick:
Put config.action_dispatch.tld_length = 0 into config/environments/development.rb and everything should work.
Link to SO post here:
Can I make Rails / WEBrick recognize entries in /etc/hosts as subdomains (instead of domains)?
Link to Github post:
https://github.com/rails/rails/issues/12438
current domain with subdomains:
"#{request.subdomain}.#{request.domain}"
# or
"#{request.subdomains.join(".")}.#{request.domain}"
A bit late to the party but here's what I used in older versions of rails.
subdomain = request.subdomains.join('.')
It should be backwards compatible in newer versions

How should I test request-related logic in Rails development?

I have several before_filters defined in my application controller to handle requests I don't like. One representative example is:
before_filter :reject_www
private
def reject_www
if request.subdomains.include? 'www'
redirect_to 'http://example.com' + request.path, :status => 301
false
end
end
(Returning false skips any following before_filters and simply returns the redirection immediately)
So, two questions:
One, how should I test this functionality? The only testing framework I've used so far is Cucumber + Webrat, which isn't really set up to handle this kind of thing. Is there another framework I should also use to fake requests like this?
Two, is there any way I can try out this functionality myself in my development environment? Since I'm simply browsing the site at localhost:3000, I can't ensure that the above code works in my browser - I'd have to push it to production, hope it works and hope it doesn't mess up anything for anyone in the meantime, which makes me nervous. Is there an alternative?
In a functional test, you can explicitly set the request host. I'm not sure what testing framework you prefer, so here is an example in good ole' Test::Unit.
def test_should_redirect_to_non_www
#request.host = 'www.mydomain.com'
get :index
assert_redirected_to 'http://mydomain.com/'
end
To address #2
You can add entries in your hosts file so that www.mydomain.com points to your local host and then test the logic in your development environment .
You can try hosting it using passenger so that it works on apache .
Hope that helps .

Resources