Stop Rails checking for params in URL? - ruby-on-rails

The Application Concept:
I'm building a Ruby on Rails [3.2] application at the moment, which consists of 2 very basic controllers - accounts for the user authentication, and messages which belong to the accounts.
# app/models/account.rb
class Account < ActiveRecord::Base
has_many :messages
end
# app/models/message.rb
class Message < ActiveRecord::Base
belongs_to :accounts
end
I then, of coarse, setup my routes.rb to allow users to view their messages nested under accounts:
# config/routes.rb
resources :accounts do
resources :messages
end
The Question:
I want users to access their accounts without using an ID parameter in the URL like this: example.com/accounts/, which works perfectly fine.
Whenever I try and go to: example.com/accounts/messages however, rails treats "messages" as a parameter for the accounts_controller! The only way I can access messages now is by going: /accounts/5236/messages - which is NOT what I want.
My question is, is there a way to block/mask rails from checking parameters on my accounts controller so that I can access my messages like the example above? I'm really puzzled on this one, so please share your thoughts and ideas!

Your defined routes
resources :accounts do
resources :messages
end
implies you can only have URL like this :
/accounts/
/accounts/:id
/accounts/:id/:action
/accounts/:id/messages/
/accounts/:id/messages/:id
/accounts/:id/messages/:id/:action
If you want specify the URL /accounts/messages/, you must specify it in the routes
resources :accounts, :collection => { :messages => :get } do
resource :messages
end

Just to add some closure to this question, I've decided to go with this solution in my routes.rb file:
get "/accounts/messages" => "messages#index", :as => :message
This works well, but the only downside is that it has to be manually added each time if you add controlers under the accounts namespace down the track. Oh well.
ForgetTheNorm also has a fantastic alternate solution below, so give that a shot if this doesn't work for you!

Related

Nested Rails Resource

I have a resource Library and a resource Books. A library can have lots of books, obviously. Something like Library.first.books works perfectly.
In my routes.rb file I've got:
resources :libraries
resources :books
What I'd really like to be able to do is request something like /library/3/books and get all of the books in the library with an id of three. I tried this:
resources :libraries, :shallow => true do
resources :books
end
resources :books
But when I request /library/3/books I get all of the books, not just library three's books. Is there a built-in/easy way to make this happen?
/library goes to index
/library/3 goes to show
/library/3/books goes to books with params[:id] = 3. You must render #books = Library.find(3).books.
I suggest you to build another model as a relationship model.
then use :
has_many :book, through: :somemodel
it works like tagging http://railscasts.com/episodes/382-tagging
In this way you do not need to worry about the nested routes.

Rails nested resources, how do I create two new resources when I need the other's id in the GET request?

Pretty much as the title suggest. I have two models, routes like this:
resources :users do
resources :books
end
- which gives me exactly the kind of urls I'm looking for. The problem is that I need to have a logic where a not logged in user can go in and click "create book", and in the new-page chose to click "new user" or "I have an account", which via javascript loads a form for a new user with all its params, or loads a form with only email and password. How do I create this route to work? With these routes it seems I get the path /users/:user_id/books/new, but then I need the user's ID, which I don't have.
Any Ideas?
By the way. User has_many :books, Book belongs_to :user.
Just add the route without the user id:
# with user_id
resources :users do
resources :books
end
# without user_id
resources :books
It looks dangerous btw to have a user id in the URL. That means that anyone can access the the books of any user, just by changing the URL. The ID of the current user should not appear in your routes at all, but should always use cookies or sessions.

Rails: Canned/RESTful way for accessing data returned by a method of a model

In my app I have a User model which defines a history method that returns a list of Activity objects, showing the last N actions the user has carried out. The UserController#history method wires this with a view.
The code looks as follows:
class UserController < ApplicationController
def history
user = User.find(params[:id])
#history = user.history(20)
end
end
class User < ActiveRecord::Base
has_many :activities
def history(limit)
...
end
end
Naturally, I also added this line to my routes.rb file:
match '/user/:id/:action', :controller => 'user'
so now when I go to localhost:3000/user/8/history I see the history of user 8. Everything works fine.
Being a Rails NOOB I was wondering whether there is some canned solution for this situation which can simplify the code. I mean, if /user/8 is the RESTful way for accessing the page of User 8, is it possible to tell Rails that /user/8/history should show the data returned by invoking history() on User 8?
First of all the convention to name controllers is in the plural form unless it is only for a single resource, for example a session.
About the routes I believe you used the resources "helper" in your routes, what you can do is specify that the resource routes to users also has a member action to get the history like this
resources :users do
member do
get :history
end
end
I think there is no cleaner way to do this
You can check it here http://guides.rubyonrails.org/routing.html#adding-more-restful-actions
As far as the rails standards are concerned, it is the correct way to show the history in your case. In rails controllers are suppose to be middle-ware of views and model, so defining an action history seems good to me.
And you can specify the routes in better way as:
resources :user do
get 'history', :on => :member #it will generate users/:id/history as url.
end

Nested resources segments misplacement

I'm trying to implement basic social network features to allow users to add, delete friends, accept and decline friedship requests.
my user resource looks like this:
resources :users
resources :friends, :controller => :relations
end
which generates this route user_friend DELETE /users/:user_id/friends/:id
But the problem is when I access /users/1, the generated link to the delete_user_friend_path looks like this: http://localhost:3000/users/5/friends/1
You need to pass the user into the helper:
delete_user_friend_path(#user, #friend)
It seems that you were doing:
delete_user_friend_path(#friend)
Which will fill in the :user_id parameter, and assume you want the same :id parameter as the page you are currently on.

Simple rails routing / url question

I'm using Ryan Bates' nifty authentication in my application for user signup and login. Each user has_many :widgets, but I'd like to allow users to browse other users' widgets. I'm thinking that a url scheme like /username/widgets/widget_id would make a lot of sense--it would keep all widget-related code in the same place (the widgets controller). However, I'm not sure how to use this style of URL in my app.
Right now my codebase is such that it permits logged-in users to browse only their own widgets, which live at /widgets/widget_id. What changes would I need to make to routes.rb, my models classes, and any place where links to a given widget are needed?
I've done Rails work before but am a newb when it comes to more complicated routing, etc, so I'd appreciate any feedback. Thanks for your consideration!
Look into nested routes. You could nest widgets inside users, like this:
map.resources :users do |users|
users.resources :widgets
end
Which would give you URLs like these:
/users/1/widgets # all of user 1's widgets
/users/1/widgets/1 # one of user 1's widgets
Check out the routing guide for more details.
The easiest would be to go with InheritedResources plugin which handles most of the legwork for you.
# routes:
map.resources :users do |user|
user.resources :widgets
end
class WidgetsController < InheritedResources::Base
# this will require :user_id to be passed on all requests
# #user will be set accordingly
# and widget will be searched in #user.widgets
belongs_to :user
end
# no changes required to the models

Resources