Simple rails routing / url question - ruby-on-rails

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

Related

Stop Rails checking for params in URL?

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!

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

How can I implement 'irwi (wikipedia gem)' to my existing app?

I added irwi ( https://github.com/alno/irwi ) to my app.
I'd like it accessible when user access to http://example.com/shop/:shop_name/wiki
(I need to know how routes.rb should be)
Anyone can show me how to make it?
:shop_name is slug so that it could be various pattern.
Of course each shop record should have a wikipedia ( one-to-one here. When shop is being created, one wikipedia page for it should be created automatically at the same time. )
I'm using Cancan, and devise for authentication so if possible, I want it only the registered user can edit and update wikipedia page.
Thanks.
see - http://guides.rubyonrails.org/routing.html for how to do your routing if you want more detailed help you will have to provide more code. Will be something along the lines of-
resource :wiki, :controller => :shop, :only => [] do
get :wiki
post :wiki
end
depending on what you require wiki to be able to do, your controllers etc. you should just be able to use the
before_filter :authenticate_user!
for your authentication

Set Up Admin Users to Edit Users, Articles, Collaborators

I am trying to solve an issue I'm having with setting up admin on my site. Here is a simple version of my routes.
resources :users
resources :articles
resources :collaborators
end
resources :admin
Admin users are very similar to Collaborators, because they can edit and create Articles, but Admin has the ability to also c.r.u.d. Users, Collaborators, and Articles for every User. Collaborators can only c.r.u.d. Articles that are associated with the User they have been assigned to.
The part where this gets a little bit confusing is how to set up the controller and views that Admin uses for CRUD operations. As of now the way I am trying to implement it is to create separate CRUD views within admin/. The problem with this is that the functionality is so simimlar to a collaborator, I feel like it could be DRYer somehow.
Anybody know the most basic way to implement something like this? Thanks!
Update: I'd like to update this to say that it's super easy to google admin tools for rails. I'm more wondering how to implement this without an admin tool, or if that sounds like a bad idea, why?
The answer I came up with is to create Collaborators with an admin field true / false. I also set up CanCan and it's working pretty nicely.
Here is my CanCan ability.rb
def initialize(user)
if user.admin?
can :manage, :all
else
can :manage, Article, :id => user.article_id
cannot :index, Article
end
end
I just used "user" because it's more readable and shorter than collaborator and basically a user...
Try ActiveAdmin (http://activeadmin.info/). I think it is the most simplest way to build administrator backend for your application.

Why would you want to use the same controller to handle a singular and a plural route?

I'm working on a rails app and using a singular resource. However the controller name for the singular resource is plural.
Eg map.resource activity_report expectes the activity_reports_controller.
The explanation given in the rails 3 guide is: "... you might want to use the same controller for a singular route and a plural route..." That is a reasonable explanation, but what is the use case for using the same controller to handle a singular route and a plural route?
In a RESTful Rails application there is usually a mapping of one controller per RESTful resource. For example, let's say we wanted a controller to process user logins (/session) but also to provide a list of users who are currently logged in (/sessions). Logically we could put both of those responsibilities within a SessionsController:
class SessionsController < ApplicationController
# GET /sessions
# Display a list of logged in users
def index
...
end
# GET /session/new
# Display the login form
def new
...
end
# POST /session
# Authenticate a user
def create
...
end
end
An alternative would be to split the functionality for listing logged in users out into a separate administration controller.
You can use it.
class UsersController < Application
end
map.resource :user
map.resources :users
Another situation in which I can imagine using it would be, let's say (and this isn't necessarily the business model you'd want, but stay with me for a moment) you are going to make a site of film reviews, and film information. So, on the one hand you'd have the link to your list of the latest reviews be a plural resource route, something like this:
http://yoursite.com/reviews?count=5
So, in this case, you have a controller for the collection, right? But you're only going to review each movie once. So what if you wanted to provide an easy access to a movie's review?
http://yoursite.com/movies/pirates_of_the_carribean_2/review
Well, there's a nested single resource route, because a movie has_one review, right?

Resources