Custom Ruby Routes in Model - ruby-on-rails

Okay, these two related questions are in reference to Railscast #21:
I'm having some trouble with routes. Two issues:
1) The routes in the tutorial seem to be relative to the root of the application; I want them to be relative to the root of the model. So
"http://example.com/login" I need to be "http://example.com/model/login" (and vice versa for logout).
I'm using permalinks to refer to my records, and I don't know how to specify an override, because every time I try to use "http://example.com/model/login" I get an error that says it can't find the record "login". How can I override this for login/logout?
2) Going to a custom route for me doesn't seem to keep the custom route in my address bar. So going to "http://example.com/login" gets me to the right page, but the browser now says "http://example.com/session/new" in the address bar. In the tutorial this doesn't happen: the app serves the correct page and keeps the custom route in the address bar. How can I get this to happen for me?
## Sessions Controller
class SessionController < ApplicationController
def create
session[:password] = params[:password]
flash[:notice] = "Successfully Logged In"
redirect_to :controller => 'brokers', :action => 'index'
end
def destroy
reset_session
flash[:notice] = "Successfully Logged Out"
redirect_to login_path
end
end
## Routes
ActionController::Routing::Routes.draw do |map|
map.resources :brokers, :session
map.login 'login', :controller => 'session', :action => 'create'
map.logout 'logout', :controller => 'session', :action => 'destroy'
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
end

What do you mean with "root of the model"? You should make routes to controllers and their actions. The controller should communicate with the model.
The error messages can be a bit confusing, but as far as I can see, this url:
http://example.com/model/login
would call the action called login in the controller called model with an empty id (wich probably doesn't exist), using this route:
map.connect ':controller/:action/:id'
If you want to have a "subfolder" in your route you can use namespaces, I don't remember every detail about it but you can find a lot of info about it here. A word of warning: namespaces makes the route debugging a lot harder, I've had a lot of fun figuring out wich route is really used. I ended up creating many very specific routes to be sure the correct one was used.

Related

Authlogic: How to gracefully rename the default routes?

So Authlogic ships with some pretty confusingly (for an end user) named routes. For example, instead of /login/new, you get /user_session/new, and so on. Then, when a user can't login, the error message appears as "This user session could not be saved."
It's a small thing, but that's just kind of... ugly, to me. What's a graceful way to rename the default session routes to something more meaningful (and easier to type)?
BTW, we are totally invested in Authlogic, so replacing it is a no-go
Rails lets you rename routes to your hearts desire. Assuming you're using Rails 2.x, the following routes should do the trick:
map.login, 'login', :controller =>'user_sessions', :action => 'new'
map.logout, 'logout', :controller => 'user_sessions', :action => 'destroy'
map.signup, 'signup', :controller => 'users', :action => 'new'
You can call them by using the following:
login_path
logout_path
signup_path

Ruby On Rails Routes

I can't figure out how to get the following routes. Here's an extract from my routes.rb file:
map.resources :treatments
map.root :controller => "home"
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
map.connect ':action', :controller => 'home' # replaces the need to manually map pure actions to a default controller
map.resources :bookings
map.resource :dashboard
map.resource :home
Now I do realise that the ordering matters, but I can't seem to get them to work correctly.
What I want is so http://localhost:3000/bookings/new actually takes you to an action http://localhost:3000/bookings/signmeup if you're either not signed in, or haven't got a login. The problem is that if I change my routes around, when I attempt to create a new booking after I have logged in, then it doesn't POST the form submission and just takes me back to the view page. This is definitely because of the routes as if I rearrange map.resources :bookings to be before all of them, then it works.
Any ideas?
As a rule of thumb, you want your resource routes to come before the generic :controller/:action/:id routes (in fact, I go so far as to delete the generic routes entirely), since routes that are defined first take precedence over the ones that are assigned later.
As for redirecting to /bookings/signmeup if the user is not logged in, you should handle that with a before_filter:
class BookingsController < ApplicationController
before_filter :check_login
# ...
protected
# This is a GENERIC example; change to fit your authentication method
def check_login
unless user_is_logged_in
redirect_to ...
end
end
end

How do I get a custom action to go to the right place?

I'm new to Ruby on Rails, and I'm sure I'm just missing something simple and stupid, but I can't figure out how to get my 'account/signup' action working.
This is what I have in my routs file:
map.connect ':controller/:action'
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
map.root :controller => "home"
I've added this to my accounts controller:
def signup
#account = Account.new
respond_to do |format|
format.html # signup.html.erb
format.xml { #account }
end
end
And I've added signup.html.erb to the accounts views folder.
Yet when I go to it in the browser I get this error:
ActiveRecord::RecordNotFound in AccountsController#show
Couldn't find Account with ID=signup
What am I doing wrong?
Add the following code right on top of your routes.rb file
ActionController::Routing::Routes.draw do |map|
map.connect 'account/signup', :controller => 'account', :action => 'signup'
...
...
...
end
Also I think you mean Account and not Accounts.
Here's a tip:
If you run rake routes it'll show you all the possible routes for your app.
It should then be obvious depending on what URL you are entering whether it'll be correctly resolved or not.
For a good overview of routes read this guide. There's really a lot of stuff you can do with routes so it's worth taking some time to read it.
If you want to follow the REST model, you controller should be called sessions and your signup action should be new, so in your routes you could do :
map.resources :sessions
This website is highly recommended to all newcomers to Rails :
http://guides.rubyonrails.org/
The following will do as well when added to the do |map| section of routes.rb
map.resource :account, :member => {:signup => :get}
Will create the standard routes for your accounts controller, as well as add the new route account/signup. It also provides the usual url helpers in addition to signup_account_url and signup_account_path

How do you check the URL Helpers for a Model Resource in Rails?

I'm currently following the Shovell tutorial in the Simply Rails 2 book. On page 168, it mentions URL Helpers for the Story Resource:
stories_path /stories
new_story_path /stories/new
story_path(#story) /stories/1
edit_story_path(#story) /stories/1/edit
The above is then used in the controller:
def create
#story = Story.new(params[:story])
#story.save
redirect_to stories_path
end
My routes.rb:
ActionController::Routing::Routes.draw do |map|
map.resources :stories
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
end
It looks like stories_path is the url name to /stories. Is that explicitly defined somewhere within my app, I can't seem to grep for that keyword. If not, is there a way that I can check the mapping above from the Rails console or somewhere else? In Django, url names are usually explicitly defined in urls.py, I just can't figure out how the above is being generated. Any documentation and pointers will help.
To get a list of the mapped routes:
rake routes
What map.resources :stories is doing is mapping your RESTful actions (index, show, edit etc.) from the stories_controller.rb to named routes that you can then use for simplicity.
routes.rb includes helpful tips on defining custom routes and it may be worth spending a little bit of time looking at resources in the API to get a better understanding:
http://api.rubyonrails.org/classes/ActionController/Resources.html#M000522
I think checking out the Rails Guides on Routing will help you a lot to understand what's going on
In short, by using the
map.resources :stories
the Router will automatically generate some useful (and RESTful) routes. Their path will take the model name (remember in Rails there is the Convention over Configuration motto), and, by default, will generate routes for all the REST actions.
This routes are available through your controller, views, etc.
If you want to check out which routes are generated from your mappings, you can use the "rake routes" command.
Now, given that, you can also write explicit URLs on your routes.rb file for actions or events that don't quite comply with the REST paradigm.
For that, you can use
map.connect "/some_kind_of_address", :controller => :pages, :action => "something_else"
Or
map.home "/home", :controller => :pages, :action => "home"
The last one will gave you both home_path and home_url routes you can use in your code.

How do I route user profile URLs to skip the controller?

Right now my user profile URLs are like so:
http://example.com/users/joeschmoe
And that points to the show method in the user controller.
What I'd ideally like to do is offer user profile URLs like this:
http://example.com/joeschmoe
So, what sort of route and controller magic needs to happen to pull that off?
I disagree with what jcm says about this. It's not a terrible idea at all and is used in production by the two biggest social networks Facebook and MySpace.
The route to match http://example.com/username would look like this:
map.connect ':username', :controller => 'users', :action => 'show'
If you want to go the subdomain route and map profiles to a URL like http://username.example.com/, I recommend using the SubdomainFu plugin and the resulting route would look like:
map.root :controller => 'users', :action => 'show' , :conditions => {:subdomain => /.+/}
These broad, catch all routes should be defined last in routes.rb, so that they are of lowest priority, and more specific routes will match first.
I also recommend using a validation in your User model to eliminate the possibility of a user choosing a username that will collide with current and future routes:
class User < ActiveRecord::Base
validates_exclusion_of :username, :in => %w( messages posts blog forum admin profile )
…
end
This does not make sense unless you have no controllers. What happens when you want to name a controller the same as an existing user? What if a user creates a username the same as one of your controllers? This looks like a terrible idea. If you think the /user/ is too long try making a new custom route for /u/
So your custom route would be...
map.connect 'u/:id', :controller => 'my/usercontroller', :action => 'someaction'
In routes.rb this should do the trick:
map.connect ":login", :controller => 'users', :action => 'show'
Where login is the name of the variable passed to the show method. Be sure to put it after all other controller mappings.
Well, one thing you need is to ensure that you don't have name collisions with your users and controllers.
Once you do that you, can add a route like this:
map.connect ':username', :controller => 'users', :action => 'show'
Another thing people have done is to use subdomains and rewrite rules in the web server, so you can have http://joeshmoe.example.com
In Rails 4 to skip controller from url you have to do add path: ''.
resources :users, path: '' do
end

Resources