I am working on a Rails API (Rails 4.2.0.beta4), and in turn since the client will be responsible for generating the new and edit forms, my RESTful controllers will only need 5 actions. Rather than having to do something like this:
resources :media, except: [:new, :edit]
resources :media_collections, except: [:new, :edit]
etc...
Is there some way I could define all of my resources inside a block that has except: [:new, :edit] stated in one spot or something? Seems crazy to have to append that to ever resource declaration, right?
You can create a method that abstracts this. I would create a new module for this, and use extend to make the methods available in routes.rb
module ApiResource
def api_resources(name, options = {}, &block)
resources name, options.merge({:except => [:new, :edit]}, &block)
end
end
# in routes.rb:
MyApp::Application.routes.draw do
extend ApiResource
api_resources :media
api_resources :media_collections
# ...
end
Related
Because it's been a while since I was using Rails monolith instead of GrapeAPI I've silly question. I want to create a route for path - users/portfolios/1/portfolio_reports/archived_reports where I will displays PortfolioReports.where(status: 'archived'). I created routes:
namespace :users do
resources :portfolios, only: [:index, :show] do
resources :archived_report, only: [:index, :show]
resources :portfolio_report, only: [:index, :show]
end
end
So I've got two questions: Should the routes file look like my current routes.rb ? and if I have Portfolio and PortfolioReport models like below, the portfolio_reports_controller should be inside app/controllers/users/portfolio_reports_controller.rb or app/controllers/portfolio_reports_controller.rb ?
class Portfolio
has_many :portfolio_reports
end
class PortfolioReport
belongs_to :portfolio
end
In Rails you can use "Shallow Nesting" which basically says to only nest the index, new and create actions under the parent resource. For the other actions you don't need to nest the routes, because through the record itself you have access to the associated record, so there is no need to have the id in the url.
So your routes will be:
users/portfolios/ # Portfolios#index
users/portfolios/1 # Portfolios#show
users/portfolios/1/portfolio_reports # PortfolioReports#index
users/portfolio_reports/1 # PortfolioReports#show
users/portfolios/1/archived_reports # ArchivedReports#index
users/archived_reports/1 # ArchivedReports#show
And routes.rb should look like this:
namespace :users do
resources :portfolios, only: [:index, :show] do
resources :archived_report, only: [:index]
resources :portfolio_report, only: [:index]
end
resources :archived_report, only: [:show]
resources :portfolio_report, only: [:show]
end
(If you'd use all 7 routes you could use the helper shallow as mentioned in the docs).
No need to nest archived_reports under portfolio_reports like you mentioned in your question!
Find more info on shallow nesting here: https://guides.rubyonrails.org/routing.html#shallow-nesting
For the user namespace:
Your controllers should live in a subfolder user because you have the namespace user:
app/controllers/user/portfolio_reports_controller.rb
I have the following route:
resources :trees, shallow: true, controller: 'base', param: :ed_node_id, only: %i[index show] do
resources :courses, shallow: true, controller: 'base', param: :ed_node_id, only: %i[index show]
end
Which gives me:
api_v1_tree_courses GET /api/v1/trees/:tree_ed_node_id/courses(.:format)
api_v1_course GET /api/v1/courses/:ed_node_id(.:format)
api_v1_trees GET /api/v1/trees(.:format)
api_v1_tree GET /api/v1/trees/:ed_node_id(.:format)
What i’m trying to avoid is on that first route, the param not being prefixed by :tree. Is there a way in nested routes to force the param not to have a parent route prefix or possibly using a regex to give the desired route?
Unfortunately Rails just generates the param key for nested resources by combining the singular form of the name with the param option:
module ActionDispatch
module Routing
module Mapper
class Resource
...
def nested_param
:"#{singular}_#{param}"
end
end
end
end
end
There is no option to actually alter the entire nested param - it would make a good feature request though.
The alternative is to use scope:
resources :trees, only: :index
scope '/trees/:ed_node_id' do
resources :courses,
only: :index,
as: :tree_courses
end
# shallow nesting won't work so we have to define this separately.
resources :courses, only: [:show]
I get a weird behavior when trying to do a super easy task:
adding
resources :something do
...
end
to the routes.rb generates all routes except #new.
when trying to browse 'localhost:3001/somethings/new' it handled by #show action.
I'm using rails 5.1.4 version. Any ideas?
======== UPDATE ========
I added s and still have the same problem: Here is the full code example and the result:
namespace :api do
namespace :webapp do
resources :user do
resources :documents
end
end
end
I was able to make it work when added:
resources :documents , only: [:show, :index, :update, :edit, :new, :create]
If I wish to create rails' routes with only API specific methods, i.e. index, show, create, update and destroy and DO NOT wish to create new and edit.
I can do it in the following way,
resources :aalu, except: [:new, :edit]
Or
resources :tamaatar, only: [:index, :show, :create, :update, :destroy]
Do I have to keep on repeating this except or only on every resource declaration?
Is there any other way to achieve this by specifying it only once?
You can indeed, using with_options. Like this:
with_options except: [:new, :edit] do |opt|
opt.resources :aalu
opt.resources :tamaatar
end
Let me know if that helps!
[edit]
You don't even need to pass the argument to the block in this case. So you can use:
with_options except: [:new, :edit] do
resources :aalu
resources :tamaatar
end
Which I think is a bit neater.
You can specify your included or excluded methods as:
included = {only: [:index, :show, :create, :update, :destroy]}
excluded = {except: [:new, :edit]}
And then while defining the resource, you can use it like
resources :aalu, excluded
OR
resources :aalu, included
In my rails 4 application, I have a piece in routes.rb that looks like this:
namespace :settings do
resources :profile, only: [:index] do
put :update_user, on: :collection
end
end
The controller is located in app/controllers/settings/profile_controller.rb and looks like this:
class Settings::ProfileController < ApplicationController
def index
end
def update_user
end
end
This results in these paths from rake routes:
PUT update_user_settings_profile_index -> /settings/profile/update_user(.:format)
GET settings_profile_index -> /settings/profile(.:format)
What I don't understand is why these paths have _index in them. I would like to get rid of it. Can I? If so, how?
I think that's because you're using a singular name profile for your resources definition, which should be plural by convention. You can try using resources :profiles instead.
For me, with routes it is always easier to work backwards, asking what are the paths I want? I'm not sure what paths you want, but I'll give you some ideas here, I hope.
I'm going to assume you want one index path, since you explicitly include the the only: [:index] statement, but you do not want not a indexed path on your resources.
Try moving your only: [:index] statement into the outer do loop for settings and add an only: [:update] to your profile (or whichever action you're looking for)
namespace :settings, only: [:index] do
resources :profile, only: [:update] do
put :update_user, on: :collection
end
end
Gets you here:
update_user_settings_profile_index PUT /settings/profile/update_user(.:format) settings/profile#update_user
settings_profile PATCH /settings/profile/:id(.:format) settings/profile#update
PUT /settings/profile/:id(.:format) settings/profile#update