request.subdomain not returning anything - ruby-on-rails

I am a new Rails Developer
My application_controller is:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :sshow
def sshow
puts "==========================="
puts YAML::dump(request.subdomains)
end
end
now when I put kausik.localhost:3000 in my browser address bar it
returns blank Array [] instead ['kausik'] .
Also I rewrite etc/host file for this subdomain.

As #injekt said in the comments, you can't work with subdomains using localhost. The simplest alternative is to use the lvh.me domain which points all subdomains to 127.0.0.1. To do this, simply start your rails server and go to kausik.lvh.me:3000.
Also, if you simply need the first subdomain, you can use request.subdomain, which in this case would return "kausik".
There's a very nice RailsCast about subdomains in Rails.

Related

Adding subdomain for different user types

I am pretty new to Rails and most of my knowledge depends on tutorials :)
So, I followed this http://www.railstutorial.org tutorial and created really good site but now I run into a problem. For my users I have a special column in my database which shows which type of user he is. For example I have column 'student' which is 'true' if user is student and 'false' if he's not.
Now I would like to create a subdomain for students. So, when student wants to sign up or sign in he would be transferred to www.student.mysite.com instead of www.mysite.com.
How can I accomplish that?
Thank you :)
There are a number of ways to do this, specifically you'll be interested in looking up multi-tenancy in respect to rails
--
Multi Tenancy
Whilst multi tenancy is typically the definition of having multiple databases / assets (one for each user), however, as it's ridiculously difficult to get this working in rails (something we're currently working on), you can use the principle with a single stack of data
There are several tutorials on how to achieve this with Rails here:
Basecamp-style subdomains by DHH (although looks like the post is down)
Multitenancy with PostgreSQL (Railscasts)
Apartment Gem (achieving multi tenancy on Rails)
Although this is not directly related to your question, most of the "multi tenancy" questions
are typically based on "how do I create different subdomains for my users"
--
Subdomains
The basis of subdomains on Rails is to capture the request, and route it to the correct controller. We've managed to achieve that using the following setup:
#config/routes.rb
constraints Subdomain do #-> lib/subdomain.rb & http://railscasts.com/episodes/221-subdomains-in-rails-3
#Account
namespace :accounts, path: "" do #=> http://[account].domain.com/....
#Index
root to: "application#show"
end
end
#lib/subdomain.rb
class Subdomain
def self.matches?(request)
request.subdomain.present? && request.subdomain != 'www'
end
end
This will give you the ability to do the following:
#app/controllers/accounts/application_controller.rb
class Account::ApplicationController < ActionController::Base
before_action :set_account
def show
##account set before_action. If not found, raises "not found" exception ;)
end
private
#Params from Subdomain
def set_account
params[:id] ||= request.subdomains.first unless request.subdomains.blank?
#account = Account.find params[:id]
end
end
Ideally, we'd love to handle this in the middleware, but as it stands, this is what we've got!
This will give you the ability to call the data you need from the #account variable:
#app/views/accounts/application/show.html.erb
<%= #account.name %>

How do I Redirect non SSL traffic when SSL is not needed?

I have a section of my application that is using SSL with the force_ssl method, doing something like this in the related controllers:
class MyController < ApplicationController
force_ssl
end
This works fine, however, on the pages that are not part of this section, I don't want to use SSL. Is it possible to redirect back to the non-SSL protocol when it's not needed?
Why don't you do ssl redirects at an HTTP level? Doing it at an HTTP level is cleaner and faster (as the request is redirected before Rails).
If you see the force_ssl source code (http://apidock.com/rails/ActionController/ForceSSL/ClassMethods/force_ssl) it just makes a simple redirect.
You may also wan't to use the :protocol option of link_to.
I don't think that there is some automagic function for this. So you will have to had some kind of definitions where SSL is needed and where not. When you make that mapping it is trivial to make simple condition in ApplicationController.
like this, if you don't mind syntax
https_only = { auth:{'login','logout',...}, registration:{'new','edit',...},...}
https_only.each do |controller, actions|
if controller_name == controller
actions.each do |a|
if action_name == a
do_redirect_to_https_with_current_path
else
do_nothing
end
end
end

Determine if Journey::Path::Pattern matches current page

I'm trying to use the method outlined this post in conjunction with url_for to determine if the current path is in a mounted engine, but I'm having a hard time figuring out how exactly to use Journey::Path::Pattern (which is what is returned by the mounted_path method outlined in the other post).
class Rails::Engine
def self.mounted_path
route = Rails.application.routes.routes.detect do |route|
route.app == self
end
route && route.path
end
end
There doesn't seem to be too much discussion on it anywhere, aside from the official documentation, which wasn't particularly helpful. I'm sure the solution is relatively simple, and the gist of the helper method I'm trying to write is below:
def in_engine? engine
current_url.include?(engine.mounted_path)
end
Edit:
Some of my engines are mounted as subdomains and some are mounted within the app itself, preventing me from simply checking if the current subdomain is the same as the mounted path, or using path_for.
Not exactly a solution, but maybe a useful lead.
I found your question interesting, so I started delving deep inside rails source... what a scary, yet instructive trip :D
Turns out that Rails' router has a recognize method that accepts a request as argument, and yields the routes that match the request.
As the routes have an app method you can compare to your engine, and as you can have access to the request object (which takes into account the http method, subdomain, etc), if you find out how to have direct access to the router instance, you should be able to do something along the lines of :
def in_engine?(engine)
router.recognize(request) do |route,*|
return true if route.app == engine
end
false
end
EDIT
I think i found out, but it's late here in I have no rails app at hand to test this :(
def in_engine?(engine)
# get all engine routes.
# (maybe possible to do this directly with the engine, dunno)
engine_routes = Rails.application.routes.set.select do |route|
route.app == engine
end
!!engine_routes.detect{ |route| route.matches?(request) }
end
EDIT
also, maybe a simpler workaround would be to do this :
in your main app
class ApplicationController < ActionController::Base
def in_engine?(engine)
false
end
helper_method :in_engine?
end
then in your engine's application controller
def in_engine?(engine)
engine == ::MyEngine
end
helper_method :in_engine?

Detectmobilebrowsers.com script -- Rails

New to Rails here. I got a short script in Rails at detectmobilebrowsers.com to check and redirect mobile browsers. It looks like:
def redirect_mobile(url = "http://detectmobilebrowser.com/mobile")
redirect_to url .....
.....
(request.user_agent[0..3])
end
It looks like this is one big function. Where should I put this in the typical Rails directory structure (app with controller, helpers, models subdirectories / components / config / etc) so that when a user accesses the front page (index), they'll get redirected to a certain mobile address?
Thanks!
You can set this up as a before_filter on your ApplicationController. This controller is inherited by all of your other controllers, so its filters run on every request.
Under app/controllers/application_controller.rb,
class ApplicationController < ActionController::Base
before_filter :redirect_mobile
private
def redirect_mobile(...)
# ...
end
end
Though, I highly suggest that you modify that script to do smarter redirects, otherwise a mobile user visiting http://www.example.com/products/123?color=green will simply be redirected to the mobile homepage, rather than the mobile version of product 123 (in green!).

How to restrict public access to a rails app

Is there a way to upload an app, but only have it accessible by me? Or perhaps by a specific set of IP's?
Reason being, we want to run a few private online tests before opening the app up to the general public. So far I have come up with the following code:
class ApplicationController < ActionController::Base
before_filter :restrict_access
def restrict_access
whitelist = ['127.0.0.123', '10.0.1.7', '10.0.1.8'].freeze
unless( whitelist.include? request.env['REMOTE_ADDR'] )
render :file => "#{Rails.public_path}/500.html", :status => :unauthorized
return
end
end
end
However, the above code still renders the main layout file (app/views/layouts/application.html.erb) which exposes the logo and footer. For un-authorised access we want to display a page that says something like "Ooops, we are still doing a few tests and will be public soon!". No logo of the site, no nothing. Just a simple message.
We are using devise as our authentication gem. We don't want to add authentication functionality just to restrict access for private beta testing. We want to do it by IP instead.
Is such a thing possible? Perhaps the code above just needs working on? Or is there a gem that we can use solely for this requirement?
If you're deploying on Apache or Nginx, this should be easy enough to configure in the relevant site config files. Doesn't need to be in the app itself, in that case.
I'm not totally sure what the issue is with your existing code. Are you saying that the filter seems to be ignored, or that the file renders within the layout? If it's the latter, specifying :layout => false as a render option should take care of that.
class ApplicationController < ActionController::Base
before_filter :check, :except=>:unauth
def unauth
render(:layout=>false)
end
private
def check
whitelist = ['127.0.0.123', '10.0.1.7', '10.0.1.8'].freeze
if !(whitelist.include? request.env['REMOTE_ADDR'])
redirect_to('/unauth')
return
end
end
end
untested but should do it, now you can place a plain error msg or whatever in unauth.rhtml
I will suggest of use some Rack middelware a.e. rack-rewrite
require 'rack-rewrite'
#in your environment
config.middleware.insert_before(Rack::Lock, Rack::Rewrite) do
r301 %r{.*}, "YOUR REDIRECTPAGE$&" ,:if => Proc.new {|rack_env|
#puts here your conditions basing on rack_env
}
end
NOT TESTED

Resources