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.
Related
I'm working on a Rails 4 app. One part of the app is a customer portal that has to be accessed from a separate domain.
I have everything working fine by navigating to domain.com/cp. The routes use namespaced controllers:
namespace :cp do
get :dashboard, to: 'dashboard#index', path: ''
...
end
How should I set up DNS records and change the routes definition so that another domain cpdomain.com points to domain.com/cp properly (no redirecting).
Thanks.
This answer can be useful for the rails routes problem:
Rails routing to handle multiple domains on single application
Shortened:
1) define a custom constraint class in lib/domain_constraint.rb:
class DomainConstraint
def initialize(domain)
#domains = [domain].flatten
end
def matches?(request)
#domains.include? request.domain
end
end
2) use the class in your routes with the new block syntax
constraints DomainConstraint.new('mydomain.com') do
root :to => 'mydomain#index'
end
root :to => 'main#index'
or the old-fashioned option syntax
root :to => 'mydomain#index', :constraints => DomainConstraint.new('mydomain.com')
I'm building a web app with Ruby on Rails and I want a route to be user-editable. So I though of adding a simple initializer to add the route the user specified, but I don't really know how can I do this. Is there some easy way of doing it? I tried this on application.rb:
config.after_initialize do
Rails.application.reload_routes!
Rails.logger.info "#{Rails.application.routes.routes.map(&:path)}"
MyApp::Application.routes.draw do
get '/droplet' => 'admin#index'
end
end
But that doesn't seems to work. This is what I have in my routes.rb:
MyApp::Application.routes.draw do
devise_for :users
root :to => 'target_finder#show_page'
match '*path' => 'target_finder#show_page'
end
Thanks in advance!
How about making a controller action that takes in the params, tests to see if they match the configured route, and take action from there.
MyApp::Application.routes.draw do
devise_for :users
root :to => 'target_finder#show_page'
match '*path' => 'test_if_custom#CustomController'
end
class CustomController < ActionController::Base
def test_if_custom
if url_for(params) == Rails.application.routes.routes.map(&:path)
take_custom_action
else
take_default_action # Redirects or errors, maybe.
end
end
end
I suggest having a look at the friendly_id gem, which can define custom routes from any attribute of the user, so you could have a :url user attribute and then use that for the route by adding a slug index and then putting extend FriendlyId and friendly_id :url, use: :slugged in the user model.
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
I'm trying to write a catch-all route in Rails 3, but I want to reserve some terms in it. I'm specifically following the example put forth in this post, in the answer by David Burrows: Dynamic routes with Rails 3
The syntax I am using is the following:
match '*path' => 'router#routing', :constraints => lambda{|req| (req.env["REQUEST_PATH"] =~ /(users|my-stuff)/).nil? }
Now, that syntax works just fine - if a user visits a page with "user" or "my-stuff" in the path, it falls through the catch-all and goes to a specific place. If the user goes to any other URL, it goes to my routing logic.
My question is more about readability - is there a way I can match the route against something other than a regex? Is there a way to provide an array of terms to match against? Also, is there a way to match specific segments of the route, as opposed to the entire thing?
Obviously Rails has built-in routing, but this project has a requirement that for certain routes, the controller not be present in the URL. Hence, the catch-all.
Thanks for any help
Here's the updated routes file per the answer below:
class RouteConstraint
RESERVED_ROUTES = ['users', 'my-stuff']
def matches?(request)
!RESERVED_ROUTES.map {|r| request.path.include?(r)}.empty?
end
end
App::Application.routes.draw do
resources :categories
resources :sites
match '*path' => 'router#routing', :constraints => RouteConstraint.new
devise_for :users, :path_names =>{ :sign_in => 'login', :sign_out => 'logout', :registration => 'register' }
root :to => "router#routing"
end
You can use a class to specify the constraints if you want something cleaner once you have multiple routes to try:
class MyConstraint
BYPASSED_ROUTES = ['users', 'my-stuff']
def matches?(request)
BYPASSED_ROUTES.map {|r| request.path.include?(r)} .empty?
end
end
TwitterClone::Application.routes.draw do
match "*path" => "router#routing", :constraints => MyConstraint.new
end
This example is adapted from the rails routing guide.
It's taking a lambda; you can use whatever criteria you want.
I want to copy the twitter profile page and have a url with a username "http://www.my-app.com/username" and while I can manually type this into the address bar and navigate to the profile page I can't link to the custom URL.
I think the problem is in the routes - here's the code in my routes.rb
map.connect '/:username', :controller => 'users', :action => 'show'
Also, I have Question and Answer models and I want to link to them with the customized URL like so:
http://www.my-app.com/username/question/answer/2210
There's nothing wrong with your route. Just remember to define it at the end, after defining all other routes. I would also recommend using RESTful routes and only if you want to have better looking URLs use named routes. Don't use map.connect. Here's some good reading about Rails routes.
Here's how this could look:
map.resources :questions, :path_prefix => '/:username' do |question|
question.resources :answers
end
map.resources :users
map.user '/:username', :controller => 'users', :action => 'show'
Just a draft you can extend.
To create urls you need to define to_param method for your user model (read here).
class User < ActiveRecord::Base
def to_param
username
end
end
I know this questions is old but it will help someone.
You could try the below. I've used it in a rails 4 project and all seems to be working great. The reason for the as: :admin is I also had a resources posts outside of this scope. It will add a admin to the helper calls e.g. admin_posts_path
scope ":username", module: 'admin', as: :admin do
get '', to: 'profiles#show'
resources :posts
end
I have used like this
In View part
portfolio.user.name,:id =>portfolio) %>
and in rout.rb
map.show_portfolio "portfolios/:username", :action => 'show_portfolio', :controller => 'portfolios'