Subdomain routing with devise not working on heroku - ruby-on-rails-3

I have defined a few types of users using devise (members, company_users, etc) and I'd like to use different subdomains for the login pages of each type of user.
I've referred to this railscast in order to implement the matching of the subdomain and redirect to the appropriate action. My routes.rb file looks like this:
devise_for :company_users, :controllers => { :registrations => 'company_users/registrations', :sessions => 'company_users/sessions' }
devise_scope :company_user do
constraints Subdomain do
match '/' => 'company_users/sessions#new'
end
end
And my lib/subdomain.rb file:
class Subdomain
def self.matches?(request)
request.subdomain.present? and request.subdomain =~ /\Acompanies\z/
end
end
Locally, it works perfectly. I've tested using companies.lvh.me:3000 (as the same railscast suggests) and it really redirects to the correct login page.
In order to try and make it work on Heroku I have added the domain, using heroku domains:add companies.mydomain.com, and I have added a new CNAME record on my DNS server, pointing to my Heroku application.
However, when I try to access companies.mydomain.com it redirects me to the root path, and not to the correct login page. I'm kind of clueless of what's happening. Any help will be appreciated.

This happen when the tld of your domain is different from tld of the heroku domain.
Mine is .com.br and I have to add config.action_dispatch.tld_length = 2 to production.rb, so Rails can parse the URL correctly and redirect to the right subdomain.

Related

API subdomain for Heroku app, is it possible?

I am trying to build an API and I am concerned that all my resources will either not be accessible with the api.myapp.com domain or that they will "live" with the wrong uris.
I have added the CNAME for my domain name to point to my Heroku app.
(ex: browsing to www.myapp.com takes you to https://myherokuapp.heroku.com)
I would like to set up an API subdomain, so that a GET to
https://api.myapp.com takes you to https://myherokuapp.heroku.com/api/v1
The best scenario would be that a POST to https://api.myapp.com/accounts/12345 would create a new account. Is that even possible?
(I know that subdomains (eg: mysubdomain.myappname.heroku.com) are not possible with Heroku)
I believe the answer could be in three different places:
Something to do with DNS provider forwarding configs (maybe
something to do with "A" records).
Something to config in Heroku, possibly a paid add-on to handle domains/subdomains.
Handle all subdomains within my app.
If you want to differentiate between api.mydomain.com and www.mydomain.com and have different controllers for your API requests then you could certainly use Rails routes constrained to your api subdomain to handle this
constraints :subdomain => "api" do
scope :module => "api", :as => "api" do
resources :posts
end
end
which would then use the posts_controller.rb in the app/controllers/api folder of your application.
You'll then have both www.mydomain.com and api.mydomain.com added a custom domains for your application and then the routes will take care of the rest.
You might also want to look into the Grape Gem for helping build your api

rails redirect_to different subdomain, controller, and params

If a user tries to go to a specific page on a mobile device (say: myapp.com/foo) I want to redirect them to a different page on the mobile site with parameters (in this case: m.myapp.com/bar?param1=foo).
I've figured out how to redirect to a different page with parameters (like myapp.com/bar?param1=foo), but I cannot figure out how to redirect to another subdomain without providing the whole string as in: redirect_to "m.myapp.com/bar?param1=foo"
Depending on the version of Rails you are using, you can accomplish this via:
redirect_to some_url(subdomain: 'some_subdomain', some_param: 'foo')
The URL helpers accept a subdomain argument--as of Rails 3, I think.

Subdomain constraints and url helpers on a domain with a subdomain (sub-subdomain?)

So I have read all about how to use Rails 3 subdomain constraints and url helpers, which works great for most applications. For instance, if I want an admin subdomain (which I do) I can use:
constraint :subdomain => :admin
scope :module => :admin
defaults :subdomain => 'admin'
But my application runs on many (customers') domains, and usually on a subdomain of it. So, something like directory.customer.com. Now if I apply that logic to admin.directory.customer.com, Rails things directory is part of the subdomain, so I have to do this:
constraint :subdomain => /^admin.*/
scope :module => :admin
defaults :subdomain => 'admin'
This is great, just match any subdomain that stats with admin, BUT when it comes to using the url helpers, it's NOT great, because the default subdomain I've set (admin) doesn't include the customer's portion of the subdomain (directory).
Setting the :host option to directory.customer.com doesn't seem to fix this, the url helper still returns admin.customer.com. Is there any way to set it up so url helpers "know" that the directory.customer.com is all part of the :host and the subdomain shouldn't overwrite that part of the host name? There's got to be a method I can modify or something to make it keep that host name intact, right?

Mount an engine on subdomain Rails 3.1

I am doing some experiments with Mountable Engines. First i need your opinion for a scenario, Is it a good idea that we make "chunk of large modules" in an application as "mountable engines".
I tried this it works great, In mountable engine we can access models of app globally and in app we can access engine models with module prefix. So it works great for me.
Now came to original question:
I want to mount an engine to a subdomain, so that every request with specific subdomain should be served by that specific engine. I used this code.
root :to=>'dashboard#index'
scope :subdomain => 'admin' do
mount MyAdmin::Engine => '/'
end
In this case mydomain.com and admin.mydomain.com goes to dashboard controller. If i change the preferences like that
scope :subdomain => 'admin' do
mount MyAdmin::Engine => '/'
end
root :to=>'dashboard#index'
In this case mydomain.com and admin.mydomain.com goes to engine specific root controller.
How can we accomplish this scenario and mount an engine on specific sub-domain?
I accomplish the task by using these route entries:
scope :subdomain => 'www' do
root :to=>'dashboard#index'
end
scope :subdomain => 'admin' do
mount MyAdmin::Engine => '/'
end
Working with Rails 3.2.12 and ruby 1.9.3-194 I came to a different solution that also works locally for avoiding the www. subdomain issue while allowing there to be an Engine at a certain subdomain.
get "home/index"
constraints :subdomain => 'store' do
mount Spree::Core::Engine, :at => '/'
end
root :to => 'home#index'
I could totally be wrong but it's working so far.

rails 3 sessions across subdomains not working in Internet Explorer

I am working on a rails 3 application which use subdomains. I used railscasts #221 "Subdomains in rails 3" (http://railscasts.com/episodes/221-subdomains-in-rails-3) as a guide and everything goes well, except in Explorer.
To keep my session across all the subdomains I put the next line in session_store.rb as the tutorial says:
MyApp.application.config.session_store :cookie_store, :key => '_myapp_session', :domain => "example.com"
I have tested my app on Firefox and Chrome and it works well, but for some reason is not working at all in Internet Explorer. The behavior is strange because sometimes it seems the session is share across all my subdomains, but some others there are some subdomains where I am logged in and other sudomains where I am not logged in.
I can't find any reason for this and I would appreciate any idea...
I am using Devise for authentication with rails 3.0.5
I believe you'll need to change your domain value to .example.com (the leading dot indicates that the cookie can be used across subdomains):
MyApp.application.config.session_store :cookie_store, :key => '_myapp_session', :domain => ".example.com"
For some reason this did not work (rails 3.2.11) for any session data that was set on a subdomain. It took a piece of custom Middleware to fix it. A summary of that solution is below.
tl;dr: You need to write a custom Rack Middleware. You need add it into your conifg/environments/[production|development].rb. This is on Rails 3.2.11
Cookie sessions are usually stored only for your top level domain.
If you look in Chrome -> Settings -> Show advanced settings… -> Privacy/Content settings… -> All cookies and site data… -> Search {yourdomain.com} You can see that there will be separate entries for sub1.yourdomain.com and othersub.yourdomain.com and yourdomain.com
The challenge is to use the same session store file across all subdomains.
Step 1: Add Custom Middleware Class
This is where Rack Middleware comes in. Some relevant rack & rails resources:
Railscasts about Rack
Railsguide for Rack
Rack documentation for sesssions abstractly and for cookie sessions
Here is a custom class that you should add in the lib
This was written by #Nader and you all should thank him
# Custom Domain Cookie
#
# Set the cookie domain to the custom domain if it's present
class CustomDomainCookie
def initialize(app, default_domain)
#app = app
#default_domain = default_domain
end
def call(env)
host = env["HTTP_HOST"].split(':').first
env["rack.session.options"][:domain] = custom_domain?(host) ? ".#{host}" : "#{#default_domain}"
#app.call(env)
end
def custom_domain?(host)
host !~ /#{#default_domain.sub(/^\./, '')}/i
end
end
Basically what this does is that it will map all of your cookie session data back onto the exact same cookie file that is equal to your root domain.
Step 2: Add To Rails Config
Now that you have a custom class in lib, make sure are autoloading it. If that meant nothing to you, look here: Rails 3 autoload
The first thing is to make sure that you are system-wide using a cookie store. In config/application.rb we tell Rails to use a cookie store.
# We use a cookie_store for session data
config.session_store :cookie_store,
:key => '_yourappsession',
:domain => :all
The reason this is here is mentioned here is because of the :domain => :all line. There are other people that have suggested to specify :domain => ".yourdomain.com" instead of :domain => :all. For some reason this did not work for me and I needed the custom Middleware class as described above.
Then in your config/environments/production.rb add:
config.middleware.use "CustomDomainCookie", ".yourdomain.com"
Note that the preceding dot is necessary. See "sub-domain cookies, sent in a parent domain request?" for why.
Then in your config/environments/development.rb add:
config.middleware.use "CustomDomainCookie", ".lvh.me"
The lvh.me trick maps onto localhost. It's awesome. See this Railscast about subdomains and this note for more info.
Hopefully that should do it. I honestly am not entirely sure why the process is this convoluted, as I feel cross subdomain sites are common. If anyone has any further insights into the reasons behind each of these steps, please enlighten us in the comments.