Subdomains rails 3 - ruby-on-rails-3

I have a simple question I can't figure out.
I have a rails 3 app using subdomains.
A firm have many users. When a user log in, I want to redirect them to their firms subdomain.
I've used Ryan Bates screencast to get subdomains working.
http://railscasts.com/episodes/221-subdomains-in-rails-3
In my user_sessions_controller I have.
def create
#user_session = UserSession.new(params[:user_session])
if #user_session.save
#firm = current_user.firm
flash[:notice] = "Successfully logged in."
redirect_to root_url(:subdomain => #firm.subdomain)
else
render :action => 'new'
end
end
This sends the user in the firm with subdomain lizz to this url
http://lvh.me:3000/?subdomain=lizz
when the user is logged in this link works
<%= link_to current_firm.subdomain, root_url(:subdomain => current_firm.subdomain) %>
Do you have any ideas on how to redirect from the controller to the subdomain?

I think you problem is that you are url the named url root_url. The helper method url_for that you modified (if you followed the Railscasts closely) is probably not used for the named url.
Try using url_for instead.
Edit
The names urls are generated in actionpack-3.0.x/lib/action_dispatch/routing/route_set.rb and will not use your custom url_for method. However, does support a :host parameter so you need to write something like
... root_url(:host => "#{current_firm.subdomain}.domain.tld") ...

Related

Changing the default rails route

In the rails the default behavior shows the id in the url like
http://0.0.0.0:3000/users/1
Now to change the url to show me something other than the id, I know I could put this in model
def to_param
name
end
And then get something like http://0.0.0.0:3000/users/mikey
I am wondering how can achieve a twitter style url. Currently the model name is still showing in the url. Something like this http://0.0.0.0:3000/mikey without the model name
Get the name of the database column storing your users’ names (let’s say it’s ‘name’).
In config/routes.rb add, somewhere above the default route:
match 'users/:name', :controller => 'users', :action => 'show'
Now, in users_controller, find def show and change it to:
#user = User.find_by_name(params[:name])
Lastly, all the id-based urls pointing to your users need to be updated to reflect the name-based change. Like the one your Users index.html.erb file.
link_to #user.name, 'users/#{#user.name}'
Add a route, near the bottom of your routes file (right above your root route!)
get '/:id', :to => "users#show", :as => :friendly_user
This will act as a fall-through route, so /anything that wasn't caught by an earlier route will route to users#show and pass an :id accordingly. You can then use friendly_user_path(#user) to generate links to that user's Twitter-style profile.
Be sure that this is one of the lowest-priority routes, as you wouldn't want a user to be able to sign up with a username like "logout" and replace your /logout page with their profile!

Correct routing for short url by username in Rails

I am trying to replace user profile views of the sort
/users/1
with
/username
I'm aware this means I'll need to check for collisions of various kinds. I've consulted the following similar SO questions:
Ruby on rails routing matching username
customize rails url with username
Routing in Rails making the Username an URL:
routing error with :username in url
Here are the various failed routes.rb route definitions I've tried, and associated errors:
match "/:username" => "users#show", via: "get"
Here's the error:
ActiveRecord::RecordNotFound in UsersController#show
Couldn't find User without an ID
app/controllers/users_controller.rb:7:in `show'
Here is my corresponding users_controller:
6 def show
7 #user = User.find(params[:id])
8 end
match "/:username" => 'users#show', :as => :profile
Same error as above.
match "/:username", :controller => "users/:id", :action => 'show'
Routing Error
uninitialized constant Users
Try running rake routes for more information on available routes.
match '/:username', :controller => 'users', :action => 'show'
Same error as 1.
match '/:username', to: 'users/:id', via: 'show'
Server does not start.
match "/:username" => redirect("/users/:id")
Error:
ActiveRecord::RecordNotFound in UsersController#show
Couldn't find User with id=:id
Any idea why my routing is not working the same way that everyone else who asks this question's is?
Update
Just to take this issue out of the comments and put it in the question more cleanly. After making the change by #Ryan Bigg below, I had a routing problem in my redirect to profile when a new one is created. Here's my create code:
def create
#user = User.new(params[:user])
if #user.save
session[:user_id] = #user.id
flash[:success] = "Thank you for signing up."
redirect_to ('/'+#user.username)
#redirect_to #user, notice: "Thank you for signing up!"
else
render "new"
end
end
And here is my user.rb
def to_param
self.username
#username
end
However, the commented out redirect, which I think should work with the to_param update, doesn't work, while the ugly hackish one above it does. Why is the to_param overwrite, which worked for other people, not working on my app? My #update and #edit methods are also not working, as their redirects go to "users/1/edit" instead of "username/edit" if overwriting to_param doesn't take care of this.
The first one is correct, but isn't working because you're still attempting to do something like this inside your controller:
User.find(params[:username])
When you should instead be doing this:
User.find_by_username!(params[:username])
The first one will attempt to find by the primary key of your table, where the second one will, correctly, query on the username field instead.
In addition to the update for to_params, the bottom of the routes file needs the following line:
resources :users, :path => '/'

How can you make sure a username won't conflict with an existing route?

So I'd like to have urls on my site like http://foobar.com/hadees that goes to someone's profile. However when registering usernames how do I make sure they don't pick something that will conflict with my existing routes?
I'm guessing I need to get a list of the existing routes but I'm not sure how to do it.
A short google search gives me that:
http://henrik.nyh.se/2008/10/validating-slugs-against-existing-routes-in-rails
In rails 3 the method has moved to Rails.application.routes.recognize_path
So I summarize :
class User < ActiveRecord::Base
validates_format_of :name, :with => /\A[\w-]+\Z/
validates_uniqueness_of :name
validate :name_is_not_a_route
protected
def name_is_not_a_route
path = Rails.application.routes.recognize_path("/#{name}", :method => :get) rescue nil
errors.add(:name, "conflicts with existing path (/#{name})") if path && !path[:username]
end
end
Good question. Through a little tinkering, I found that you can get the routes in your app via:
Rails.application.routes.routes.collect{|r| r.path}

Redirecting from a namespaced controller using a hash

I have a double namespace situation, where my controllers look like this:
CandidateController
Candidate::PerformanceController
Candidate::Performance::ReviewController
In Rails 2, I was able to use redirect_to from the Candidate::Performance::ReviewController controller in order to redirect to an action in the CandidateController, like so:
class Candidate::Performance::ReviewController < ApplicationController
before_filter :ensure_manager
# ...
def ensure_manager
if !current_user.manager?
flash[:warning] = t(:must_be_manager)
redirect_to :controller => '/candidate', :action => :index
end
end
end
The / in controller => '/candidate' would allow Rails to redirect from app.com/performance/reviews to app.com/candidate.
However, this seems to not work the same in Rails 3.1. Instead, my redirect_to goes to app.com/candidate//candidate. What is the correct way to specify a "absolute" controller within a redirect_to hash (ie. without using a path helper)?
Update: I know this would be infinitely easier if I just use named route helpers (ie. candidate_path). Unfortunately, there is a lot of legacy code in our codebase which doesn't use RESTful routing and instead uses the default "catch-all" route; ie. we have a lot of actions with no named route to fallback on.
I wonder if something else is wrong. In the doc:
In particular, a leading slash ensures no namespace is assumed. Thus,
while url_for :controller => ‘users‘ may resolve to
Admin::UsersController if the current controller lives under that
module, url_for :controller => ’/users‘ ensures you link to
::UsersController no matter what.
And I don't think it changed...
Also, shouldn't the catch-all routes be after the default routes in your config?
I think that redirect_to :controller => ... uses url_for to build the url, so in the end, if your custom routes catches /candidates, I don't really see the difference.
Some people have the same problem: https://github.com/rails/rails/issues/2575
Patching actionpack/lib/action_dispatch/routing/route_set.rb line 434
as follows fixes this: if !named_route && different_controller? &&
!controller.starts_with?('/')
If anyone else runs into this problem, it seems to be a known issue (unsure whether they consider it a bug or not, given the lack of response on the issue page from anyone working on Rails).
Until they patch it (assuming they do), I've added the following little monkey patch into an initializer, based on the code given in the original post of that issue:
module ActionDispatch
module Routing
class RouteSet
class Generator
def use_relative_controller_with_absolute_paths!
return if controller.starts_with?('/')
use_relative_controller_without_absolute_paths!
end
alias_method_chain :use_relative_controller!, :absolute_paths
end
end
end
end
Hope this can help someone else!
Update: It seems that this was fixed in Rails here.

Change devise route from ID to Username

I have tried using the post to change the routes to point at the username. I know this is simple, but for some reason I can't compute it at the moment. I have tried everything on the devise documentation page as well.
Drawing username routes
I just want to have the routes layout to use the username instead of id and not have the users prefix. Like:
http://example.com/username
instead of
http://example.com/users/1
class User < ActiveRecord::Base
def to_param
username
end
end
in your controller make
#user = User.find_by_username(params[:id])
instead of
#user = User.find(params[:id])
this will make your routes like http://example.com/users/username
to make what you want, you can do route like:
resources :users, :path => '' do
# nested resources...
end
so, user_path(#user) will make url http://example.com/username
but It's not a good practice, cause it's not a REST. I advise you to leave urls like http://example.com/users/username