User based custom subdomains - ruby-on-rails

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.

Related

Routes in Engine mounted on subdomain do not inherit the constraints

Setup
Our current Rails app is made out of sub-apps that are mounted as engines. Typically these engines are mounted on a subdomain in the main routes.rb file as follows
mount MySubApp::Engine => '/', as: :sub_app, constraints: {subdomain: 'sub_app'}
The Problem
Routes within MySubApp's routes.rb file do not get the subdomain when using the named _url helpers. For example the following in apps/my_sub_app/config/routes.rb
MySubApp::Engine.routes.draw do
resources :foos
end
gives us sub_app.foo_url(5) but it results in
http://www.example.com/foos/5
when we want
http://sub_app.example.com/foos/5
tl;dr
How can I get the engine's mounting constraints passed to its named routes?
EDIT: A Workaround
While I'd still prefer a better solution, the following will work. You can wrap all the routes in each of the sub apps routes.rb files that could be mounted on a subdomain like so
MySubApp::Engine.routes.draw do
constraints Rails.application.routes.named_routes[:sub_app].constraints do
resources :foos
end
end
EDIT 2: A much less desirable workaround
A commenter (since deleted?) pointed out you can pass a subdomain option to the helpers but we'd like to avoid having to use sub_app.foo_url(5, {subdomain: 'sub_app'}) for every cross subdomain link. Even if we moved the subdomain name into an ENV var and made a wrapper, this is not DRY.
check out the guide it says you can do it by
namespace :Engine do
resources :controller, :methods
end
the Engine is just name spacing your code
#Aaron not sure if you ever got this fixed, but look into the
config.action_dispatch.tld_length
setting (on the engine's config). I'm not sure how it'll react with engines, but in our case it lets us handle the case of sub-subdomains for our staging server (so when we use the _url helpers with the staging server it correctly does subdomain.staging.domain.com, rather than subdomain.domain.com).

How do I ignore a route in rails?

I have a route i.e. mysite.com:3000/new_route that I'd like to ignore, is this possible to do through rails and not server side?
I've read that this can be done through apache, however, my app is running on Heroku and that type of control isn't accessible to me. Therefore, I'm looking for another way to do this through Rails.
Thanks
update
I am using faye to have live notifications in my app, on localhost faye runs on port 9292 - localhost:9292/faye, all good in development mode, but in production it should point to mydomain.com/faye, there are no port numbers in production environment, and loading faye.js returns error not found
If you're talking about a resources route you don't want to be created:
resources :something, except: :new
However, I'm not exactly sure if this is what you meant by ignore.
You can define a route at the top of your routes.rb file that will redirect to some other page.
get '/new_route', redirect: '/'
By the time you ask Rails to process the route, it is already too late. If you ask rails to process a route, it will, either by returning a 404 of 500 error, or a page.
If you want the route to be processed by another application, it will need to be intercepted by your webserver (nginx or apache, or whichever one you're using). In their configuration, you just redirect that route to the other application, and every other route to the Rails app.
EDIT
Another option you have, is to forward your requests to a different server.
You add a route like
get 'faye/*query' => 'faye#get'
post 'faye/*params' => 'faye#post'
And then a controller
require 'faraday'
class FayeController < ApplicationController
APP = 'http://mydomain.com:9292'
def get
request_page :get
end
def post
request_page :post
end
private
def request_page(method)
conn = Faraday.new(:url => APP)
query = params.delete(:query)
response = conn.send method, query, params
render text: response.body.gsub(APP, 'mydomain.com/faye')
end
end
which will use Faraday to load the information from your other application.

Recursively redirecting in Rails 3 Routes

We are updating our large web app and getting rid of our URL synonym system. For example, though we only use one model for 'advertiser', it is aliased to 'merchants' and 'sellers', using synonymous paths. Thus, /merchant/1 and /seller/1 use the same exact controller and action as advertiser/1.
We no longer need this functionality, but we do need for old URLs to correctly redirect users to the proper place (i.e. proper controller actions).
So far, I've tried:
1) reading http://guides.rubyonrails.org/routing.html - This lead me to try the following suggestions
2) match '/merchants' => redirect('/advertisers') - this didn't seem to work at all, though the syntax seems correct.
3) iterating over resources (this produces a TON of routes and may cause insecurities):
ADVERTISER_SYNONOYMS = ['affiliates', 'sellers']
ADVERTISER_SYNONYMS.each do |a|
resources :advertisers, :path => a, :as => :advertiser
resources :other_controller do
member do
get :test
match :generate_test
end
end
end
end
We use nested resources in our routes.rb, so I'm struggling with getting these synonymns to be recognized all throughout more complex URLs. If anyone dealt with this problem or has a more elegant suggestion, I'd be most grateful.
There are many ways to handle this, including additional routes in routes.rb like you've tried, or rewrites at the webserver level such as Apache or Nginx.
One solution I like is the rack-rewrite gem; it keeps routes clean and the rules are plain ruby. It provides a set of methods for configuring rewrites and redirects. For your situation, I would want to use permanent redirects. Though I haven't tested it out myself, something like the following may work for you:
# Gemfile
gem 'rack-rewrite'
# application.rb
config.middleware.use(Rack::Rewrite) do
r301 %r{/merchant|seller/(\w+)}, '/advertiser/$1'
end

What's the "Rails Way" to create route helpers to other hosts?

My app consists of two rails servers with mostly different concerns sitting behind a reverse proxy. Let's call them Server1 and Server2. Occasionally, Server1 needs to render a link to a url on Server2. Is there a good way to use Rails route helpers for this? Specifically in Rails 2? I came up with this:
ActionController::Routing::Routes.draw do |map|
# other routes omitted
map.with_options(:host => 'server2.example.com') do |add|
# create a named route for 'http://server2.example.com/thingies'
add.server2_thingies '/thingies', :controller => 'fake'
# create a named route for 'http://server2.example.com/thingies/combobulate'
add.enhance_server2_thingies '/thingies/combobulate', :controller => 'fake'
# create a named route for 'http://server2.example.com/mabobs/combobulate'
add.enhance_server2_mabobs '/mabobs/combobulate', :controller => 'fake'
# etc..
end
end
So then I can use server2_thingies_url and such in my views. This works, but it makes me feel like a bad person because there is no FakeController and I certainly have no intention of routing requests to it. I considered making helper methods and placing them in app/controllers/application_controller.rb, but a colleague made the argument that it is best to keep all route helpers in routes.rb, so things like rake routes will be accurate, for instance. Is there a better way?
I think I'd make a counter-argument to your colleague: if you're having to dirty up routes.rb with a "FakeController", then your rake routes is still not going to be accurate. In fact, I'd say that this is exactly the kind of thing that a helper was meant to help: it's taking logic that belongs purely in the view (link generation), and removing it from your view templates. Helpers are also easier to maintain and tweak.
Another advantage to the helper style is that if and when it's time to upgrade to Rails 3.x, the less hackish your routes.rb file is, the happier you will be - and that I can attest to from experience. :)
I'd just pass in :host to your foo_path or foo_url calls.
Seems cleaner than messing around with a FakeController

Support for multiple domains/subdomains in 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!

Resources