I find the resource route method quite convenient, but I totally hate that it does not create create and destroy path helpers.
I understand that writing
<% form_for(#object) %>
is supposed to automatically get the route name, and that we can play with arrays or symbols to automatically get the namespace/prefixes when they exist, but I have many routes with complicated scope definitions, and not being able to get create_xxx helpers totally annoys me
Is there no simpler solution than to write ? (I am trying to keep the default RESTful URLs while generating the helpers)
complicated_scope do
resources :my_resources, except: [:create, :destroy] do
post '', on: :collection, action: :create, as: 'create' # plus this generates a pluralized version, not very intuitive `create_complicated_scope_my_resourceS_path`
delete '', on: :member, action: :destroy, as: 'destroy'
end
end
EDIT. My example of 'somewhat complicated scope'
# Company access routes under /company/
namespace :company do
# I need a company id for all nested controllers (this is NOT a resource strictly speaking, and using resources :companies, only: [] with 'on: :collection' doesn't generate appropriate urls)
scope ':company_id' do
# Company administrators
namespace :admin do
# There is a lot of stuff they can do, not just administration
namespace :administration do
# There are several parameters grouped in different controllers
resources :some_administrations do
... # finally RESTful actions and others here
end
end
end
end
end
Resourceful routing does create create and destroy helpers, but they're implied by the type of HTTP request being made (POST and DELETE respectively) so the routing helper methods should work fine with the code you've provided.
Suppose you have the following route definition:
complicated_scope do
resources :my_resources
end
end
As a simple example, in the case of delete, you could use a named route like so:
link_to "Delete [resource]", complicated_scope_resource_path(id: #my_resource.id), method: :delete
Since the HTTP verb disambiguates the controller action this helper method routes to the destroy method of the controller.
Alternatively, you should be able to use the array syntax as well.
link_to "Delete [resource]", [:complicated_scope, #my_resource], method: :delete
The same goes for forms:
<%= form_for [:complicated_scope, #my_resource] do |f| %>
If #my_resource is a new object (not persisted), as in the case of a new action this would be equivalent to sending a post request to /complicated_scope/my_resource with the form params going in the body of the request.
Alternatively if #my_resource exists, as in the case of an edit action, the above would be equivalent to sending a PUT/PATCH which will route to the update action of your controller with /complicated_scope/my_resource/:id/update.
Related
get 'users/:id/edit/settings' => 'users#account'
What is the dry way to reference this path in link_to?
As a side note, I use 'users/:id/edit' to edit name/location/age etc and I am using the route above to edit password and email, because I wish to force the user to authenticate their :current_password before editing these more sensitive attributes. I mention this just to make sure my routing logic is correct.
Just run rake routes and you will see all the routes that you have in you app. It should be to the far right
You can use the as: option to setup a named route.
However I would set it up with conventional rails routes:
Rails.application.routes.draw do
resources :users do
resource :settings, only: [:edit, :update], module: :users
end
end
This would create an idiomatically correct RESTful route.
Using the singular resource creates routes without an id parameter. Also you should only use the name :id for the rightmost dynamic segment in a route to avoid violating the principle of least surprise.
rake routes will show you the following routes:
Prefix Verb URI Pattern Controller#Action
edit_user_settings GET /users/:user_id/settings/edit(.:format) users/settings#edit
user_settings PATCH /users/:user_id/settings(.:format) users/settings#update
PUT /users/:user_id/settings(.:format) users/settings#update
...
As a side note, I use 'users/:id/edit' to edit name/location/age etc
and I am using the route above to edit password and email, because I
wish to force the user to authenticate their :current_password before
editing these more sensitive attributes. I mention this just to make
sure my routing logic is correct.
Your route will in no way enforce this authorization concern.
Instead you should do a check in your controller:
# app/models/users/settings_controller.rb
class Users::SettingsController
before_action :set_user
before_action :check_password, except: [:edit]
def edit
# ...
end
def update
# ...
end
private
def set_user
#user = User.find(params[:user_id])
end
def check_password
# this is an example using ActiveModel::SecurePassword
unless #user.authorize(params[:current_password])
#user.errors.add(:current_password, 'must be correct.')
end
end
end
change it to:
get 'users/:id/edit/settings' => 'users#account', as: :edit_user_settings
and then you can just reference it as:
link_to edit_user_settings_path(#user)
rake routes will probably give you a path something like users_path which you can link to using something like
<%= link_to 'Users', users_path(#id) %>
I'm working on a simple reservation application. Here are my routes
resources :users do
get 'reservations', on: :member
end
resources :listings do
resources :reservations
end
When I try to make a reservation, action reservations#new takes me to reservations_path . Of course I'm getting error as this path doesn't exist. I'd like action new to take me to listing_reservations_path instead. I was hopping it will be done automatically since resources :reservations is in nested resources. I read about routes and tried many things but can't find any working way of doing it. Is it possible?
You seem to be unclear on the nature of routes. The action reservations#new exists independently from any route. A route is just a way to map a URL path to a controller and action. If you are trying to do something like:
redirect_to controller: :resources, action: :new
You will have trouble, as all of your routes require some context. Instead, you need to provide whatever the URL helper you're using with a context:
redirect_to listing_reservations_path(#listing)
link_to "New Reservation", new_listing_reservation_path(#listing)
link_to "Reservation", [#listing, #reservation]
I have a Model (Show) in Rails that is accessed via a subdomain rather than a standard REST URL. In the file app/helpers/url_helper.rb I have the following method:
def show_url(show)
root_url(subdomain: show.subdomain)
end
In controllers, this works perfectly. I can test it with puts show_url(#show) and it outputs the subdomain of the show as expected: http://test.example.com. In integration tests, however, the method doesn't work, and the default one generated by rails is used instead. If I run puts show_url(#show) there, I just get http://example.com. How do I use this custom URL helper in my integration tests?
Edit:
routes.rb section regarding this subdomain stuff:
constraints(lambda do |request|
request.subdomain.present? && request.subdomain != 'www'
end) do
get '/' => 'shows#show', as: :show
get '/edit' => 'shows#edit', as: :edit_show
end
This is based loosely around a Railscast on subdomain matching.
Try defining its route without the default "show" action:
# config/routes.rb
resources :show, except: :show
Sounds a bit confusing since your model is called Show, but what it's doing is defining all the standard restful routes (index, new, create, edit, update, delete) except for "show", e.g.
Or another way:
resources :show, only: %w(index new create edit update delete)
I would really consider doing some refactoring and renaming the Show model.
I am putting a randomize def in my controller and would like to access it with a restful route. The route should be accessed with the following:
<%= link_to "Randomize", random_reader_path %>
But I cannot figure out how to get this route to appear in rake routes or configure it correctly in my routes.rb file.
The random method will do the same thing as index only provide a random page content #variable
Currently I have my reader Controller as
resources :reader
in my routes.rb
Add more RESTful actions!
resources :reader do
get 'random', on: :collection
end
The route will be random_readers_path, though.
In my UserController I have:
def join
end
I have a join.html.erb in my /views/user/ folder.
My routes has a :
resources :user
When I go to:
http://localhost:3000/user/join
I get:
The action 'show' could not be found for UserController
Re: why isn't the join action found?
To answer your specific question, what's happening is that you want to have an action "join" for your User model.
Your problem is that you haven't defined a route matching the url http://localhost:3000/user/join
The line resources :user in your routes file only defines routes for the seven standard rest verbs/actions:
index, new, create, show, edit, update, destroy
See: http://apidock.com/rails/ActionController/Resources/resources
Added: to fix, you'll need to add an explicit or generic route. Routing docs
Added: Re: why am I seeing the error message re show? To be ultra-precise, the route selector "GET /usr/:id" (created by your resource call) is being used to select the SHOW action for the User resource. The :id value is being set to "join". Since you don't have a Show method defined in your controller, that's the error that you're seeing.
You're using resources, but have a non-REST action, so you need to add the join action to the route with the appropriate HTTP verb:
map.resources :users, :member => { :join => :get }
Place:
def show
end
in your UserController.
To be certain:
app/controllers/users_controller.rb
def join
end
app/views/users/join.html.erb
config/routes.rb
resources :users