Say I have the following routes that are constrained to specific subdomains:
App::Application.routes.draw do
constraints :subdomain => "admin" do
scope :module => "backend", :as => "backend" do
resources :signups
root :to => "signups#index"
end
end
constraints :subdomain => "www" do
resources :main
root :to => "main#landing"
end
end
My problem is that root_url and backend_root_url both returns a url on the current subdomain: "http://current-subdomain.lvh.me/" instead the subdomain specific for the resource.
I would like root_url to return "http://www.lvh.me/" and backend_root_url to return "http://admin.lvh.me/" (the behavior should be the same for all resources under the subdomain).
I have tried to accomplish this in rails 3.2 by setting the url options in various places, one being url_options in application controller:
class ApplicationController < ActionController::Base
def url_options
{host: "lvh.me", only_path: false}.merge(super)
end
end
Maybe I need to override the url helpers manually? How would I approach that (accessing the routes etc)?
Edit: I'm able to get the correct result using root_url(:subdomain => "admin") which returns "http://admin.lvh.me/" regardless of the current subdomain. However, I would prefer not having to specify this all over the code.
Using "defaults" as shown below will make rails url helpers output the correct subdomain.
App::Application.routes.draw do
constraints :subdomain => "admin" do
scope :module => "backend", :as => "backend" do
defaults :subdomain => "admin" do
resources :signups
root :to => "signups#index", :subdomain => "admin"
end
end
end
constraints :subdomain => "www" do
defaults :subdomain => "www" do
resources :main
root :to => "main#landing"
end
end
end
Related
I can't set root path for subdomain.
This is my routes.rb part:
constraints subdomain: 'blog' do
scope :module => "blog", :as => "blog" do
resources :posts
end
end
root 'statics#index'
When I am visiting blog.example.com I've got static#index action response and get posts#index, when visiting blog.example.com/posts.
I want to set root path for blog.example.com pointing to posts#index.
No effect for this:
match '/' => 'posts#index', :constraints => { :subdomain => 'blog' }, via: [:get]
I believe you should still be able to call root within the constraints block:
constraints subdomain: 'blog' do
root :to => 'posts#index' # Perhaps this needs to be `blog/posts#index`?
scope :module => "blog", :as => "blog" do
resources :posts
end
end
root 'statics#index'
This works for me with separate namespaces for each subdomain.
Update: rewritten question a bit. Trying to route my subdomains like below
login.app.ltd
user1.app.ltd
user2.app.ltd
signup.app.ltd
Using
Rails 3.2
Devise
To no avail tried several tutorials blog posts, anyone knows a working example for this?
Really stuck on this :(
this is my routes now:
match '', to: 'frontend#index', constraints: lambda { |r| r.subdomain.present? && ( r.subdomain != 'www') }
#match '' => 'home#index', :constraints => { :subdomain => 'login' }
constraints :subdomain => /^(?!signup\b)(\w+)/ do
root :to => "frontend#index"
end
root :to => "frontend#index"
My RailsApps project offers a complete example app showing how to use subdomains:
Rails Tutorial for Subdomains with Devise
Did you take a look at that?
config/routes.rb
devise_for :users
resources :users, :only => :show
constraints(Subdomain) do
match '/' => 'profiles#show'
end
root :to => "home#index"
lib/subdomain.rb
class Subdomain
def self.matches?(request)
case request.subdomain
when 'www', '', nil
false
else
true
end
end
end
Ok with some help managed to get it working
One should do:
constraints subdomain: 'login' do
devise_scope :user do
root to: 'sessions#new'
end
end
It used to be that you could load Typus routes exactly where you needed them by placing
Typus::Routes.draw(map)
at the appropriate point in your routes.rb file. It seems that this is no longer supported and that they're always loaded after all of the application routes. This causes problems with catchall routes which must be defined last. Does anyone know how to control the load order for typus now? Is there a way to get them defined before any of the app routes rather than after? Thanks!
I got around it by leaving my catch-all routes at the end of my apps routes.rb BUT excluding it from matching for Typus urls:
# A catch all route
match '*path' => 'content#show', :constraints => lambda{|request|
!request.path.starts_with?("/admin") # excluded if typus will be taking it...
}
This may or may now work for you...
I'm looking for the same answer.
At the moment I have resorted to copying the contents from typus's config/routes.rb and placing it into my routes.rb file, before the catchall route.
It's a horrible, hackish solution, but it's solving my immediate problem.
Example:
# TODO: KLUDGE: MANUALLY BRING THE TYPUS ROUTES IN
# Typus used to provide :
# Typus::Routes.draw(map)
# But that is no longer the case.
scope "admin", :module => :admin, :as => "admin" do
match "/" => "dashboard#show", :as => "dashboard"
match "user_guide" => "base#user_guide"
if Typus.authentication == :session
resource :session, :only => [:new, :create, :destroy], :controller => :session
resources :account, :only => [:new, :create, :show, :forgot_password] do
collection do
get :forgot_password
post :send_password
end
end
end
Typus.models.map { |i| i.to_resource }.each do |resource|
match "#{resource}(/:action(/:id(.:format)))", :controller => resource
end
Typus.resources.map { |i| i.underscore }.each do |resource|
match "#{resource}(/:action(/:id(.:format)))", :controller => resource
end
end
# END KLUDGE
# Catch all to the state page handler
match '/:page' => 'pages#show', :as => 'page'
I wondered if there were any plugins or methods which allow me to convert resource routes which allow me to place the controller name as a subdomain.
Examples:
map.resources :users
map.resource :account
map.resources :blog
...
example.com/users/mark
example.com/account
example.com/blog/subject
example.com/blog/subject/edit
...
#becomes
users.example.com/mark
account.example.com
blog.example.com/subject
blog.example.com/subject/edit
...
I realise I can do this with named routes but wondered if there were some way to keep my currently succinct routes.rb file.
I think that subdomain-fu plugin is exacly what you need.
With it you will be able to generate routes like
map.resources :universities,
:controller => 'education_universities',
:only => [:index, :show],
:collection => {
:all => :get,
:search => :post
},
:conditions => {:subdomain => 'education'}
This will generate the following:
education.<your_site>.<your_domain>/universities GET
education.<your_site>.<your_domain>/universities/:id GET
education.<your_site>.<your_domain>/universities/all GET
education.<your_site>.<your_domain>/universities/search POST
The best way to do it is to write a simple rack middleware library that rewrites the request headers so that your rails app gets the url you expect but from the user's point of view the url doesn't change. This way you don't have to make any changes to your rails app (or the routes file)
For example the rack lib would rewrite: users.example.com => example.com/users
This gem should do exactly that for you: http://github.com/jtrupiano/rack-rewrite
UPDATED WITH CODE EXAMPLE
Note: this is quickly written, totally untested, but should set you on the right path. Also, I haven't checked out the rack-rewrite gem, which might make this even simpler
# your rack middleware lib. stick this in you lib dir
class RewriteSubdomainToPath
def initialize(app)
#app = app
end
def call(env)
original_host = env['SERVER_NAME']
subdomain = get_subdomain(original_host)
if subdomain
new_host = get_domain(original_host)
env['PATH_INFO'] = [subdomain, env['PATH_INFO']].join('/')
env['HTTP_X_FORWARDED_HOST'] = [original_host, new_host].join(', ')
logger.info("Reroute: mapped #{original_host} => #{new_host}") if defined?(Rails.logger)
end
#app.call(env)
end
def get_subdomain
# code to find a subdomain. simple regex is probably find, but you might need to handle
# different TLD lengths for example .co.uk
# google this, there are lots of examples
end
def get_domain
# get the domain without the subdomain. same comments as above
end
end
# then in an initializer
Rails.application.config.middleware.use RewriteSubdomainToPath
This is possible without using plugins.
Given the directory structure app/controllers/portal/customers_controller.rb
And I want to be able to call URL helpers prefixed with portal, i.e new_portal_customer_url.
And the URL will only be accessible via http://portal.domain.com/customers.
Then... use this:
constraints :subdomain => 'portal' do
scope :module => 'portal', :as => 'portal', :subdomain => 'portal' do
resources :customers
end
end
As ileitch mentioned you can do this without extra gems it's really simple actually.
I have a standard fresh rails app with a fresh user scaffold and a dashboard controller for my admin so I just go:
constraints :subdomain => 'admin' do
scope :subdomain => 'admin' do
resources :users
root :to => "dashboard#index"
end
end
So this goes from this:
site.com/users
to this :
admin.site.com/users
you can include another root :to => "{controller}#{action}" outside of that constraint and scope for site.com which could be say a pages controller. That would get you this:
constraints :subdomain => 'admin' do
scope :subdomain => 'admin' do
resources :users
root :to => "dashboard#index"
end
end
root :to => "pages#index"
This will then resolve:
site.com
admin.site.com
admin.site.com/users
Ryan Bates covers this in his Railscast, Subdomains.
Given the following routes.rb file:
# Add Admin section routes
map.namespace :admin do |admin|
admin.resources :admin_users
admin.resources :admin_user_sessions, :as => :sessions
admin.resources :dashboard
# Authentication Elements
admin.login '/login', :controller => 'admin_user_sessions', :action => 'new'
admin.logout '/logout', :controller => 'admin_user_sessions', :action => 'destroy'
# Default is login page for admin_users
admin.root :controller => 'admin_user_sessions', :action => 'new'
end
Is it possible to alias the 'admin' section to something else without having to change every redirection and link_to in the application? The main reason is that it's something I'd like to be configurable on the fly and hopefully make it also a bit less easy to guess.
map.namespace method just sets some common options for routes inside its block. It uses with_options method:
# File actionpack/lib/action_controller/routing/route_set.rb, line 47
def namespace(name, options = {}, &block)
if options[:namespace]
with_options({:path_prefix => "#{options.delete(:path_prefix)}/#{name}", :name_prefix => "#{options.delete(:name_prefix)}#{name}_", :namespace => "#{options.delete(:namespace)}#{name}/" }.merge(options), &block)
else
with_options({:path_prefix => name, :name_prefix => "#{name}_", :namespace => "#{name}/" }.merge(options), &block)
end
end
So it is possible to use with_options method directly instead of namespace:
map.with_options(:path_prefix => "yournewprefix", :name_prefix => "admin_", :namespace => "admin/" ) do |admin|
admin.resources :admin_users
# ....
end
And you can continue to use routes the same way as before, but prefix will be "yournewprefix" instead of "admin"
admin_admin_users_path #=> /yournewprefix/admin_users
In order to create an alias to the namespace (calling one api_version for example, from another router address) you can do the following:
#routes.rb
%w(v1 v2).each do |api_version|
namespace api_version, api_version: api_version, module: :v1 do
resources :some_resource
#...
end
end
this will cause the routes /v1/some_resource and /v2/some_resource to get to the same controller. then you can use params[:api_version] to get the evrsion you need and respond accordingly.
Like in any other resource, :path seem to be working fine for me.
namespace :admin, :path => "myspace" do
resources : notice
resources :article do
resources :links , :path => "url"
end
end
end