I have nested resources like so
resources :users do
resources :widgets
end
When I have #widget, to get a proper routing from my helpers i need to use user_widget_path(#widget.user, #widget) is there any way to tell rails to automatically pull out the user from #widget object? So i could just use user_widget_path(#widget)
#apneadiving is totally right. But you can improve a little your approach:
link_to "user", user_widget_path(#widget.user, #widget)
can be presented shorter:
link_to "user", [#widget.user, #widget]
UPD
Also you can rewrite user_widget_path as you want:
class ApplicationController < ActionController::Base
helper_method :user_widget_path
private
def user_widget_path(widget)
super(widget.user, widget)
end
end
You should also rewrite edit_user_widget_path, new_user_widget_path. And better to wrap it as an external Module.
There is no automatic method to do this. But you could create your own application helper, it's pretty straight.
Related
I have seen this question, but the only answer there suggests creating a custom route, which I am reluctant to do. What are the alternatives, using Rails' default RESTful routes?
In particular, I've begun using this setup:
# routes.rb
# Note the singular resource
resource :all_apples, path: 'apples/all', only: :destroy
# all_apples_controller.rb
class AllApplesController
def destroy
# Something like:
User.find(params[:id]).apples.delete_all
end
end
Then I can do DELETE /apples/all to delete all apple records.
I would also have a separate ApplesController with the standard individual CRUD actions, so I can still do, for example, GET /apples.
Would this be a "RESTful" way to solve the problem? Are there any notable issues with it?
Note
My actual use case has to do with token revocation. I want an endpoint that revokes all of a user's web tokens. I'm currently using DELETE /users/:id/tokens/all, as described above.
I have been thinking about this.
I really don't like to do anything like this on a controller.
I would create a special nested route for users
# config/routes.rb
resources :users do
delete '/delete_all_apples', to: 'users#delete_all_apples'
end
So now we have a special route to delete all apples from users
on the console you can find this routes like this
rails routes | grep delete_all_apples
# user_delete_all_apples DELETE /users/:user_id/delete_all_apples(.:format) users#delete_all_apples
On your view your link should be
<%= link_to 'Destroy All Apples', user_delete_all_apples_path(user.id), data: {:confirm => 'Are you sure?'}, :method => :delete %>
Your Controller
class UsersController < ApplicationController
def delete_all_apples
User.find(params[:id]).delete_all_apples
end
end
On your Model
class User < ActiveRecord::Base
has_many :apples
def delete_all_apples
apples.delete_all
end
end
I have a Users table which also has a manager's id to implement a self-join. when I login as a a manager and click on "My subordinates", I should see my subordinates. The subordinates are also from the User table.
So my question is
What should I say here <%= link_to "My Subordinates", ????_path %>(I mean like user_path.).
How should the model and controller logic be?
I would do something like #ryanfelton said, but instead of overwriting the index method, i would create a new one specifically for the subordinates.
class Manager::UsersController < ApplicationController
before_action :ensure_manager! #this one check the manager_id or any other condition to be manager
def sobordinates
#subordinates = #user.subordinates
end
end
#routes.rb
namespace :manager do
resources :users do
collection do
get :subordinates
end
end
end
This way you can maintain the index of users and you have a method only for the subordinates.
Be aware that you need to create a subordinates.html.erb inside the users folder >
app/views/manager/users/subordinates.html.erb
EDIT:
You where asking for the model and the link also so, here it goes:
The link: after editing the routes.rb, go to the console and use rake routes
and search for the subordinates link. Add the _path or _url depending on the use you are whiling for that path.
The model, I strongly recommend you to read the official documentation about relations: http://guides.rubyonrails.org/association_basics.html. That would help you more than having the answer for copying and pasting :)
I would recommend namspacing a users_controller.rb.
So it would be in the folder app/controllers/manager/users_controller.rb
class UsersController < ApplicationController
before_action :ensure_manager!
def index
#manager.users
end
end
In the routes.rb you would have this route:
namespace :manager do
resources :users
end
So ultimately your path would be manager_users_path
I have a like model, recording which user liked which record. I used polymorphic association so a user can like many models.
Currently I use nested-resources to handle likes.
POST /items/:item_id/likes
DELETE /items/:item_id/likes/:id
Now for some reasons I want to get rid of the use of like_id by designing a better route. This is because it will be easier to cache a fragment view.
Note that item model is only one of a few models which are likable, and I want to avoid code duplication if possible.
What's a good way to design routes and controllers that will not use like_id but also allows better code reuse in controller?
Possible implementation
I was thinking of routes like this:
POST /items/:item_id/like
DELETE /items/:item_id/like
I won't use nested like resource. Instead I place a like action in items controller. It will determine if the request is a POST or a DELETE and act accordingly. This however doesn't feel DRY.
I don't know about Rails necessarily, but in Zend Framework I would create a front controller plugin to route all requests with methods 'LIKE' and 'UNLIKE' to a particular controller which then deduces which route was requested, and subsequently which resource was requested, and then performs the necessary actions to 'like' or 'unlike' that resource in the name of the requesting user.
Why? Because the user is 'like'-ing or 'unlike'-ing the resource in question, not 'creating a like' or 'deleting a like'. Sure, in the backend, the 'like' is a record in a cache or database that gets created or deleted -- but the semantics of a resource are not necessarily equivalent that of whichever method is used to persist that resource.
What you need is Singular Resources.
routes.rb
resources :items do
resource :like, only: [:create, :destroy]
end
likes_controller.rb
class LikesController < ApplicationController
before_action :load_likeable
def create
#like = Like.where(likeable: #likeable, user: current_user).first_or_create
redirect_back(fallback_location: #likeable)
end
def destroy
#like = Like.find_by(likeable: #likeable, user: current_user).destroy
redirect_back(fallback_location: #likeable)
end
private
def load_likeable
klass = [Recording].detect { |c| params["#{c.name.underscore}_id"] }
#likeable = klass.find(params["#{klass.name.underscore}_id"])
end
end
likes_helper.rb
module LikesHelper
def like_button_for(item)
if item.liked
form_tag recording_like_path(item), method: :delete do
button_tag "UnLike"
end
else
form_tag recording_like_path(item), method: :post do
button_tag "Like"
end
end
end
end
item.liked is method from Item model
So currently I have something like /users/1/ when I want to view a user profile. How can I go through routes.rb to change that to /user/chiggins/ where chiggins is a unique username?
You need is to override to_param method in User model:
class User
def to_param
username
end
end
Then rails will use it automagically for routing. See http://api.rubyonrails.org/classes/ActiveRecord/Base.html#method-i-to_param
Another possibility to consider would be the friendly_id gem - https://github.com/norman/friendly_id
Nowadays there is a :param argument on the resource declaration.
http://guides.rubyonrails.org/routing.html#overriding-named-route-parameters
You can get per-resource identifier customization by redefining the member_scope and nested_scope methods on the Resource instance.
resources :users do
#scope[:scope_level_resource].tap do |u|
def u.member_scope
"#{path}/:username"
end
def u.nested_scope
"#{path}/:#{singular}_username"
# member_scope also usable here, assuming username will be nowhere in nested routes.
end
end
end
Regarding the question about #nested_scope below: It gets used when you do something like this in routing:
resources :members do
resources :playlists, only: :index
end
Then, the param would be :member_username instead of just :username. This is useful in the playlists controller when assembling the collection so you can infer the scope of the request.
The best way is to define a route with a custom param:
match "/users/:username" => "users#show"
In your controller, the plain old params[:id] will be params[:username], and you can get the user from de DB using:
User.find_by_username(params[:username])
I'm using nested routes and I want to provide some sort of a shortcut method. (I'm using RoR 3.0)
The routes look like this.
resources :countries do
resources :regions do
resources :wineries
end
end
To access a winery route I want to be able to define a function that removes the need to specify a country and region each time. Like:
def winery_path(winery)
country_region_winery_path (winery.country, winery.region, winery)
end
Where should I do this? How can I get that to be available whereever url_for is available?
I would put it into your app/controller/application_controller.rb
class ApplicationController < ActionController::Base
helper_method :winery_path
def winery_path(winery)
country_region_winery_path (winery.country, winery.region, winery)
end
end
Now it's available in every controller and view