Custom route mistaken for object id in Rails - ruby-on-rails

I have the following route:
view_all_styles /styles/view_all(.:format) styles#view_all
When I point my broswer at xyz.com/styles/view_all I receive the error:
ActiveRecord::RecordNotFound at /styles/view_all
Couldn't find Style with id=view_all
I'm also routed to the show action??
Request parameters
{"action"=>"show", "controller"=>"styles", "id"=>"view_all"}

It sounds like you've got your routes defined in the wrong order - you'll want to define your custom route before the resource routes of styles. Otherwise, you'll run into exactly this problem.
Since your route, /styles/view_all also fits into the route for #show, /styles/:id ('view_all' being the :id), it will match and pass along the request to #show before it even tries to match your custom route.

Related

Rails Routing Error w/ key

So I made this custom Route
on my routes.rb
get'dashboard_report_m/:date/:branch_id'=>'reports#monthly_and_branch'
I'm Getting Routing Error, No route matches [GET] "/dashboard_report_m"
on my rake routes I have this
on rake routes
GET /dashboard(.:format) reports#today_admin
GET /dashboard_report/:date/:branch_id(.:format) reports#date_and_branch
GET /dashboard_report_m/:date/:branch_id(.:format) reports#monthly_and_branch
all the other routes I made are fine but this one just doesn't seem to work.
I tried removing the keys ':date/:branch_id'
and it would work just fine.
I have already made similar routes and they all work just fine except for this one.
Some things to check:
Is there a ReportsController with a ​monthly_and_branch action?
Does the error occur if you visit /dashboard_report_m/2016-09-20/1234 directly or are you using a path helper?
UPDATE
OK so you are accessing the path http://localhost:3000/dashboard_report_m/?date=2016-09&branch_id=1 - you are passing the parameters in as query params, this is not how your route is set up. The way you have it now it is expecting dashboard_report_m/2016-09/1. You need to either remove the date and branch_id params from your route or change the way you access the URL. I suggest reading the Rails Routing from the Outside In guide.
When you access the following route:
localhost:3000/dashboard_report_m/?date=2016-09&branch_id=1
This is a GET request to 'dashboard_report_m', with query parameters: params['date'] = '2016-09' and params['branch_id'] = '1'.
What you should instead be doing is accessing this route:
localhost:3000/dashboard_report_m/2016-09/1
This is a GET request to 'dashboard_report_m/:date/:branch_id' - i.e. using the bound parameters of date and branch_id.
Further reading: Understand the difference between bound parameters and the query string. This is by no means specific to Rails; it's at the core of how all web applications work.

Accessing an arbitrary action in rails

I am somewhat confused as to how to properly access an action in a rails controller that is NOT associated with one particular model. The routing file, by default, seems to be routing the action name to "id". So if I type, say, /user/login, I end up with an error that says: "Couldn't find User with 'id'= login"
What is the proper way to access arbitrary action names in rails?
Make a route for it, obviously. The request goes this way:
first it hits a route
from there it hits a controllers' action
[an action may invoke a model] (optional, but common)
controller specified a view and fetches data to render
view is sent back in response to a request
resource and resources might not be obvious about what they do. But in fact, they are shorthands to corresponding collections of routes used quite often, like this is what resources adds. And they're not monoliths, they can be customised to fit your needs. Providing options is just a start, you can provide a block to define your custom action routes for this resource like so:
resources :users do
get :login
end
This will add a /users/login route that maps to UsersController#login, following Rails' conventions.
See this guide for more details and don't forget to run rake routes to see what you have at the moment.

how to hit a put route

I have an additional method in one of my otherwise restfull controllers called 'importdata'. As I'm actually changing the data (importing csv in the database), I understood that it should be a put route instead of get.
Initially I had
resource data_set do
put 'importdata', on: :method
end
what I also tried is:
put 'data_sets/:id/importdata', "data_sets#importdata'
rake routes shows the route I want in both cases.
What I did when I had the method on (1st example) route in the controller was
redirect_to import_data_sets_path id: dataset.id
And with the second example:
redirect_to controller: "data_sets", action: "importdata", id: dataset.id
The message I get in both cases is:
No route matches [GET] "/data_sets/28/importdata"
Which is correct, because it's a put route. The only way I get this to work is to change the put for a get:
get 'data_sets/:id/importdata', "data_sets#importdata'
How can I get that to work on a put route? Should it be a put route in the first place?
Thanks for your time.
Simply put you can't 'upgrade' a HTTP request issued by an user. redirects only work over GET. If the user is changing something do it through a form and make sure it's a PUT request as you're modifying an existing resource.
If the PUT is conditional there's several options, either figure out how to solve this in the UI, use an HTTP client to issue the PUT(which doesn't make sense for an local call) or extract the editing of the resource in some other kind of class and use it in the controller.
However, even if the edit is optional it makes more sense to let the user fire a PUT in the first place.
Hope that helps.

Bound Parameters in Ruby on Rails

I’ve previously asked a question related to this topic as well and with the aid of that question I’ve found the name of exactly what I wanted: “bound parameters”.
I have managed to create a routing like "user/:id/dosomething" by adding a member to the routes.rb. However, this type of URL is not the solution I was looking for.
My question is, how do you provide the 'user/dosomething/:id' to match with action user's dosomething action with id sent as parameter?
Here is the section I’ve read in order to get the idea:
3.1 Bound Parameters
When you set up a regular route, you supply a series of symbols that
Rails maps to parts of an incoming HTTP request. Two of these symbols
are special: :controller maps to the name of a controller in your
application, and :action maps to the name of an action within that
controller. For example, consider this route:
get ':controller(/:action(/:id))'
If an incoming request of /photos/show/1 is processed by this route
(because it hasn’t matched any previous route in the file), then the
result will be to invoke the show action of the PhotosController, and
to make the final parameter "1" available as params[:id]. This route
will also route the incoming request of /photos to
PhotosController#index, since :action and :id are optional
parameters, denoted by parentheses.
You can add a custom route:
get 'user/doesomething/:id' => 'users#do_something', :as => 'do_something_user'
This will route HTTP GET requests that match the URL user/dosomething/:id to the UsersController's do_something action. The :as => 'do_something_user' part names the route, so that you can use do_something_user_path and do_something_user_url helpers to generate the URLs.
For more information on routing, see Rails Routing from the Outside In.

rails_3_question :as => why is my /posts/new routing to posts/show after setting up a slug

I'm using Rails 3 and after setting up slugs, I found that posts/new no longer works.
posts/:id, posts/:id/edit and all the other CRUD operations work.
However /posts/new gives me a routing error
No route matches {:action=>"show", :controller=>"posts"}
Now for some reason posts/new is routing to posts#show. In my routes, its just
resources :posts
My theory is that since /posts/:slug now matches against things other than numbers ids, the show verb is being routed to first. However it doesn't make sense since posts/grr a nonexistent entry gives a different error than posts/new and posts/first comes out just fine with all its associated paths working fine as well.
Anyone know what might be going on?
I've uploaded the repo to https://github.com/cultofmetatron/cassowary/tree/photogallary
I know my code sucks, I'm still learning the ins and outs of the system and I'd appreciate any insight into whats going on.
In your comment the first part seems fine: add a column to the Post column called slug and so on, and the contents of that will become some or all of the URL used to display a specific post. (I'll assume the other CRUD operations should work as normal)
To find the URL, the router has to know how to know which controller and action will handle this URL (as compared to others). A normal resources :posts route will match all of the RESTful methods, e.g. mapping a GET request onto a path starting with the controller name, and if an id is specified (/posts/1) map to the posts#show controller method, if not, it will map to posts#index method. If the request is a PUT, or DELETE or POST, different actions around a standardized URL format will occur.
Two changes are needed:
URL with the post slug format needs to map to the posts#show method (which is modified accordingly), and
Any links to the show page that are generated on your site need to use the post slug instead of the id
I'll assume you're OK with URLs start with /posts (if not, you'll need to identify some other unique pattern).
The first change requires that you override the specific case of the show method using route globbing, my adding something like match 'posts/*slug before the standard resource route. Here's a link to the guide on route globbing: http://guides.rubyonrails.org/routing.html#route-globbing
The next change, modify the existing posts#show method so that it looks for slug instead of id, e.g.
def show
#post = Post.where("slug = ?", params[:slug])
...
end
Finally, change the way Rails handles the URL helper posts_path. Do this by overriding to_param in your Post model, e.g.
def to_param
"/posts/#{slug}"
end
And then you're done. Maybe.
After that, see how the friendly_id gem does the same thing :-) https://github.com/norman/friendly_id

Resources