RESTful route overriden by named route - ruby-on-rails

When defining a resources in routes.rb in Rails, I have the following problem: My resource supports the standard CRUD operations and has a custom function/route, which allows filtering. Now this custom routes matches the edit route and jumps in before the actual RESTful route.
Is there a way to prioritize the RESTful routes so that they match first?
resources :items do
get ':category(/:level)', :action => :filter, :on => :collection, :as => 'filter'
end

You should just set a simple get route ( if it is a GET request )
get 'filter', :to => "items#filter"
If you have any problems there are always RoR Guides :)
http://guides.rubyonrails.org/routing.html

Related

Rails routes: Nested, Member, Collection, namespace, scope and customizable

I am trying to understand more about Rails routes.
Member and Collection
# Example resource route with options:
resources :products do
member do
get 'short'
post 'toggle'
end
collection do
get 'sold'
end
end
Namespace and Scope
# Example resource route within a namespace:
namespace :admin do
resources :products
end
scope :admin do
resources :products
end
Constraints, Redirect_to
# Example resource route with options:
get "/questions", to: redirect {|params, req|
begin
id = req.params[:category_id]
cat = Category.find(id)
"/abc/#{cat.slug}"
rescue
"/questions"
end
}
Customization:
resources :profiles
original url from resource profiles for edit.
http://localhost:3000/profiles/1/edit
I want to make it for users available only through click edit profile and see url like in below.
http://localhost:3000/profile/edit
Also, is there advanced routing, How most big companies design their routes in rails ? I would be really glad to see new kind of routes if there exist.
Thank You !
**Collection & Member routes**
A member route requires an ID, because it acts on a member.
A collection route doesn't require an ID because it acts on a
collection of objects
:member creates path with pattern /:controller/:id/:your_method
:collection creates path with the pattern /:controller/:your_method
For example :
map.resources :users, :collection => { :abc => :get } => /users/abc
map.resources :users, :member => { :abc => :get } => /users/1/abc
**Scopes & Namespaces routes**
namespace and scope in the Rails routes affect the controller
names, URIs, and named routes.
The scope method gives you fine-grained control:
scope 'url_path_prefix', module: 'module_prefix', as: 'named_route_prefix' do
resources :model_name
end
For Example :
scope 'foo', module: 'bar', as: 'baz' do
resources :posts
end
produces routes as :
Prefix Verb URI Pattern Controller#Action
baz_posts GET /foo/posts(.:format) bar/posts#index
POST /foo/posts(.:format) bar/posts#create
new_baz_post GET /foo/posts/new(.:format) bar/posts#new
edit_baz_post GET /foo/posts/:id/edit(.:format) bar/posts#edit
baz_post GET /foo/posts/:id(.:format) bar/posts#show
PATCH /foo/posts/:id(.:format) bar/posts#update
PUT /foo/posts/:id(.:format) bar/posts#update
DELETE /foo/posts/:id(.:format) bar/posts#destroy
The namespace method is the simple case — it prefixes everything.
namespace :foo do
resources :posts
end
produces routes as :
Prefix Verb URI Pattern Controller#Action
foo_posts GET /foo/posts(.:format) foo/posts#index
POST /foo/posts(.:format) foo/posts#create
new_foo_post GET /foo/posts/new(.:format) foo/posts#new
edit_foo_post GET /foo/posts/:id/edit(.:format) foo/posts#edit
foo_post GET /foo/posts/:id(.:format) foo/posts#show
PATCH /foo/posts/:id(.:format) foo/posts#update
PUT /foo/posts/:id(.:format) foo/posts#update
DELETE /foo/posts/:id(.:format) foo/posts#destroy
**Constraints & Redirect**
Rails routes are executed sequentially, you can mimic conditional
login in the following manner:
match '/route' => 'controller#action', :constraints => Model.new
match '/route' => 'user#action'
The first line checks whether the conditions of the constraint are met (i.e., if the request is emanating from a Model domain). If the constraint is satisfied, the request is routed to controller#action.
We can add constraints to routes for multiple uses like for ip-matching, params matching, restrict format parameter, request-based restrictions etc as :
- ip-matching
=> resources :model, constraints: { ip: /172\.124\.\d+\.\d+/ }
- filtering id params
=> match 'model/:id', to: 'model#show' ,constraints: { id: /\d+/}, via: :get
- restrict format params
=> match 'model/:id', to: 'model#show' ,constraints: { format: 'json' }, via: :get
- request-based constraints
=> get 'admin/', to: 'admin#show', constraints: { subdomain: 'admin' }
Use a singular resource for it:
resource :profile
and in controller manipulate the profile of current user.
As for complex routes - usually namespaces, nested resources with shallow routes and custom actions are all that is needed.
You can go through this answer which answers you first part of the question.
To answer second part of your question. You can treat "profile" as your singular resource (the singularity of the noun itself represents a singular resource). For a detailed description you can refer to this link.

Understanding rails routing that include (=>) and (:as)

While reading about rails routing I found routing that include =>. But I don't understnad what it means. Also I found some routing example with :as. It would be nice if someone explained a little bit about it. I have read rails guide but still I am not quite clear about them.
Please explain what this means
get 'customer_details' => "customers#details"
and
get 'customer_details' => "customers#details", :as => :customerdetails
Each time you define a route, you have to define a controller#action for that route:
#config/routes.rb
get :customer_details => "customers#details"
get :customer_details, to: "customers#details"
get :customer_details, controller: :customers, action: :details
The routing module provides URL rewriting in native Ruby. It's a way to redirect incoming requests to controllers and actions.
The following symbols are special:
:controller maps to your controller name
:action maps to an action with your controllers
Other names simply map to a parameter as in the case of :id.
Using => is simply a shortcut for the to: option...
When a pattern points to an internal route, the route's :action and :controller should be set in options or hash shorthand. Examples:
match 'photos/:id' => 'photos#show', via: :get
match 'photos/:id', to: 'photos#show', via: :get
match 'photos/:id', controller: 'photos', action: 'show', via: :get
In short, it's another way to pass the required "controller#action" arguments to the Rails router.
--
Of course, this is negated by using the resources directive, which sets the controller & actions implicitly:
#config/routes.rb
resources :customers, only: [], path: "" do
get :customer_details, on: :collection #-> url.com/customer_details
end
The routing middleware (ActionDispatch::Routing) takes inbound URL paths, and matches them against your defined routes -- sending the user to the appropriate controller / action.
The entire routing structure (even when you use link_to) depends on having the controller & action set for a route; especially true with a path helper.
Setting as: gives you the ability to explicitly define the name of the path, for example:
#config/routes.rb
get "search/:query", to: "application#search", as: :app_search
... the above will create the helper <%= app_search %>
Update
In response to your comment, you'll want to use either of the following:
#config/routes.rb
get "customers/details", to: "customers#details" #-> url.com/customers/details
- or -
resources :customers do
get :details, on: :collection #-> url.com/customers/details
end
If you're defining a single route, only use symbols if Ruby can interpret that data without any interpolation. For example get :details can be treated as get "details", however get "customers/details" cannot be treated as a symbol.

Why resources on collection has different routing helper then regular post

In my routes.rb I had this resource
resources :home_screen_buttons do
post :update_multiple, :on => :collection
end
update_multiple helper is update_multiple_home_screen_buttons
Then I decided to remove resource because i need only update_multiple method in my controller, so I changed routes.rb to
post "home_screen_buttons/update_multiple"
It create helper home_screen_buttons_update_multiple instead of update_multiple_home_screen_buttons
Why it has different routing helper name?
It makes sense that :on => :collection has different helper then :on => :member, but is there any other way then adding :as => :update_multiple_home_screen_buttons to my post method for same behavior?
This is how Rails does this. When match is used, it maps the URI to the action and creates the corresponding helper path as controller_action_path
but when it is used as collection, that becomes RESTful action for that resource and Rails gives it a logical name relating to a collection. As quoted as an example here:
resources :photos do
collection do
get 'search'
end
end
generates search_photos_path.
You could have done this:
resources :home_screen_buttons, :only => [:update_multiple] do
post :update_multiple, :on => :collection
end

Rails 3: making a catch-all route easier to read and amend

I'm trying to write a catch-all route in Rails 3, but I want to reserve some terms in it. I'm specifically following the example put forth in this post, in the answer by David Burrows: Dynamic routes with Rails 3
The syntax I am using is the following:
match '*path' => 'router#routing', :constraints => lambda{|req| (req.env["REQUEST_PATH"] =~ /(users|my-stuff)/).nil? }
Now, that syntax works just fine - if a user visits a page with "user" or "my-stuff" in the path, it falls through the catch-all and goes to a specific place. If the user goes to any other URL, it goes to my routing logic.
My question is more about readability - is there a way I can match the route against something other than a regex? Is there a way to provide an array of terms to match against? Also, is there a way to match specific segments of the route, as opposed to the entire thing?
Obviously Rails has built-in routing, but this project has a requirement that for certain routes, the controller not be present in the URL. Hence, the catch-all.
Thanks for any help
Here's the updated routes file per the answer below:
class RouteConstraint
RESERVED_ROUTES = ['users', 'my-stuff']
def matches?(request)
!RESERVED_ROUTES.map {|r| request.path.include?(r)}.empty?
end
end
App::Application.routes.draw do
resources :categories
resources :sites
match '*path' => 'router#routing', :constraints => RouteConstraint.new
devise_for :users, :path_names =>{ :sign_in => 'login', :sign_out => 'logout', :registration => 'register' }
root :to => "router#routing"
end
You can use a class to specify the constraints if you want something cleaner once you have multiple routes to try:
class MyConstraint
BYPASSED_ROUTES = ['users', 'my-stuff']
def matches?(request)
BYPASSED_ROUTES.map {|r| request.path.include?(r)} .empty?
end
end
TwitterClone::Application.routes.draw do
match "*path" => "router#routing", :constraints => MyConstraint.new
end
This example is adapted from the rails routing guide.
It's taking a lambda; you can use whatever criteria you want.

Rails 3 route appends _index to route name

I am migrating a Rails 2.3.8 version to Rails 3.0 and so ive rewritten my routes file. When i list the routes using rake routes, i see some route names have _index appended to them. I cant figure out why this is.
Relevant routes:
Rails 2.3.8:
map.namespace "tracker", :path_prefix => "" do |planner|
planner.resources :planner, :collection => {:step1 => :get,
:add => :get,
:unsubscribe => [:get, :post] }
end
Rails 3.0 route:
namespace "tracker", :path => "" do
resources :planner do
collection do
get :step1
get :add
get :unsubscribe
post :unsubscribe
end
end
end
Output from rake routes
Rails 2.3.8
step1_tracker_planner GET /planner/step1(.:format)
add_tracker_planner GET /planner/add(.:format)
unsubscribe_tracker_planner GET /planner/unsubscribe(.:format)
POST /planner/unsubscribe(.:format)
Rails 3.0
step1_tracker_planner_index GET /planner/step1(.:format)
add_tracker_planner_index GET /planner/add(.:format)
unsubscribe_tracker_planner_index GET /planner/unsubscribe(.:format)
POST /planner/unsubscribe(.:format)
Any ideas as to why this _index is being added would be much appreciated.
It is because your resource is named :planner instead of :planners that Rails decided to add the _index to any collection nested underneath. My guess it is there for readability.
The action named in the collection normally translates to a verb, so I can see why this makes sense. Take the typical photos resource example given in the routing docs:
resources :photos do
collection do
get 'search'
end
end
search_photos GET /photos/search(.:format)
But if instead we called the resources 'photo'...
resources :photo do
collection do
get 'search'
end
end
search_photo_index GET /photo/search(.:format)
In the first case, you search the "photos", and in the second case you search the "photo index".
You should be using either resource :planner either resources :planners depending on what you need. To learn about singular resource and it differences check out Rails Guides.
Following on from Semyon Perepelitsa's response, note that resource :planner expects the controller's name is PlannersController, whereas resources :planners expects PlannerController.
If you don't want to rename your controller when changing from resources to resource, you can override the default by specifying resource :planner, controller: :planner.

Resources