I'm struggling with exiting out of a subdomain back up to the root domain in Rails 3.2.
Say I have a blog with a dashboard. Each user has a subdomain at username.blog.com. Each user also has a dashboard at blog.com/dashboard.
If a user manually types in username.blog.com/dashboard, I want them to be redirected to blog.com/dashboard.
I've tried using subdomain => false in my routes, but it seems to be of no use. I also tried a matcher underneath the domain scope, but that also didn't work. Any help would be greatly appreciated!
Relevant routes.rb follows.
resource :dashboard, :controller => 'dashboard', :subdomain => false
scope '/', constraints: lambda { |r| r.subdomain.present? && r.subdomain != 'www' } do
get '/' => 'feed#show'
end
root :to => 'dashboard#show', :subdomain => false
I would keep that logic outside the routing.
I would use dashboard_url instead of dashboard_path in my views / controllers.
I would put something like this in my ApplicationController
def dashboard_url(options={})
options[:subdomain] = false
super(options)
end
Related
I'd like so overwrite my root :to => redirect("/projects") route if the subdomain is equal to "www" or is just "". This means if the subdomain is "www" or "" the app should use my website#index controller/action.
At the moment I got this setup:
class Subdomain
def self.match(r)
r.subdomain == "www" || r.subdomain == ""
end
end
.....
# website
scope :constraints => lambda { |request| Subdomain.match(request) } do
get '/' => 'website#index'
get "/help" => "website#help", as: "help"
get "/about" => "website#about", as: "about"
get "/signup" => "website#signup", as: "signup"
# post "/signup" => "website#signup_account"
end
root :to => redirect("/projects")
If I access www.satisfy.dev/help or satisfy.dev/help everything works fine. If I access www.satisfy.dev/ or satisfy.dev/ the root_path (/projects) is in use. I thought the get '/' => 'website#index' should be more important than the root_path since it's above the root_path.
Hope somebody has a hint for me!
I tried treating the routes as "get" requests and had more success.
See here:
Multiple 'root to' routes in rails 4.0
I have a subdomain widgets.mywebsite.com. The only thing the subdomain does is return json on a given route. How do I prevent people/crawlers accessing my site via the subdomain whilst still keeping the route available? I would be happy to simply redirect any requests that include this subdomain to mywebsite.com but can't work out how to do it.
constraints :subdomain => 'widgets' do
namespace :widgets, :path => nil, :format => 'json' do
match 'v1' => 'v1/widgets#index'
end
end
So it sounds like you want to reverse the logic in your routes - you want to constrain the bulk of your routes so that they only resolve when the subdomain isn't equal to widgets. That's opposed to the above example, where you're making a single route only available on the 'widgets' subdomain. This is pretty straightforward.
In your config/routes.rb you can define a class before the Application.routes.draw block
class NotWidgetsRequest
def matches?(request)
request.subdomain != 'widgets'
end
end
and then you can wrap all of your routes other than the v1/widgets#index route in a
constraints NotWidgetRequest.new do
...
end
block. This will prevent these routes from resolving on widgets.mywebsite.com
I'm running Rails 3.0.17.
I currently have the following defined in my routes:
constraints :subdomain => "" do
get 'faq' => 'static_pages#faq'
end
The problem is that it allows any faq url with a subdomain (e.g. anysubdomain.mypage.com/faq)to route to the faq page when I only want the url without a subdomain (mypage.com/faq) to route to the faq page.
Any tips/insights?
Thank you in advance.
In your routes.rb
constraints SubdomainConstraint.new do
get 'faq' => 'static_pages#faq'
end
In your lib/
class SubdomainConstraint
def self.matches?(request)
request.subdomain.blank?
end
end
I have successfully added the ability to use dynamic subdomains within my application. The issue is that when I run my Cucumber tests, I receive the following error when my application performs a redirect_to which contains a subdomain:
features/step_definitions/web_steps.rb:27
the scheme http does not accept registry part: test_url.example.com (or bad hostname?)
I have a signup controller action that creates the user and the account chosen and redirects the the user to the logout method with the subdomain specified based on what the user selected as the subdomain in the sign up form. Here is the code for the redirect action that happens once the user and account models are created and saved:
redirect_to :controller => "sessions", :action => "destroy", :subdomain => #account.site_address
Here are my rails 3 routes:
constraints(Subdomain) do
resources :sessions
match 'login', :to => 'sessions#new', :as => :login
match 'logout', :to => 'sessions#destroy', :as => :logout
match '/' => 'accounts#show'
end
Here is the code I have so far for the Subdomain class which is specified in the constraint above:
class Subdomain
def self.matches?(request)
request.subdomain.present? && request.subdomain != "www"
end
end
I added UrlHelper to the ApplicationController:
class ApplicationController < ActionController::Base
include UrlHelper
protect_from_forgery
end
This is the code for the above UrlHelper class:
module UrlHelper
def with_subdomain(subdomain)
subdomain = (subdomain || "")
subdomain += "." unless subdomain.empty?
[subdomain, request.domain, request.port_string].join
end
def url_for(options = nil)
if options.kind_of?(Hash) && options.has_key?(:subdomain)
options[:host] = with_subdomain(options.delete(:subdomain))
end
super
end
end
All of this code above allows me to run subdomains fine within the local browser. The issue above happens when I run my Cucumber test. The test clicks the signup button which in turn calls the redirect_to and throws the exception listed above.
Here is what my gem file looks like:
require 'subdomain'
SomeApp::Application.routes.draw do
resources :accounts, :only => [:new, :create]
match 'signup', :to => 'accounts#new'
constraints(Subdomain) do
resources :sessions
match 'login', :to => 'sessions#new', :as => :login
match 'logout', :to => 'sessions#destroy', :as => :logout
match '/' => 'accounts#show'
end
end
Could you please let be know an additional method to have my tests work now? I would be interested in either a fix or a way I can test my methods without using subdomains (for example a mocked out method that retrieves the account name).
I have this same pattern in my code. I use Capybara (but not Cucumber), and I was able to get around it like this:
# user creates an account that will have a new subdomain
click_button "Get Started"
host! "testyco.myapp.com"
# user is now visiting app on new subdomain
visit "/register/get_started/" + Resetkey.first.resetkey
assert_contain("Get Started Guide")
The host! command effectively changes the host as it appears to the app from the test request.
EDIT: Just realized this was working with webrat, but not capybara (i'm using both, phasing out webrat now.) The way I'm doing it in capybara is to either click a link to a new domain (capybara follows it) or to:
visit "http://testyco.myapp.com/register"
EDIT: Another update. Found a method that works without having to use the full URL in every event.
host! "test.hiringthing.com"
Capybara.app_host = "http://test.hiringthing.com"
In the test setup.
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.