Today I came across some strange (and very inconvenient) Ruby on Rails behavior that even persistent combing of the net did not yield a satisfying answer to.
Note: I translated the method and route names to be easier to read in English, and hope I did not introduce any inconsistencies.
Situation
Environment
Ruby on Rails 4.2.0 executing under Ruby 2.0 (also tested under Ruby 2.2.0)
Relevant Code
consider a controller with these actions, among others:
class AssignmentsController < ApplicationController
def update
...
end
def takeover_confirmation
...
end
end
routes.rb
Since I use a lot of manually defined routes, I did not use resources in routes.rb. The routes in question are defined as follows:
...
post 'assignments/:id' => 'assignments#update', as: 'assignment'
post 'assignments/takeover_confirmation' => 'assignments#takeover_confirmation'
...
The relevant output of rake routes:
assignment POST /assignments/:id(.:format) assignments#update
assignments_takeover_confirmation POST /assignments/takeover_confirmation(.:format) assignments#takeover_confirmation
Problem
When I do a POST to the assignments_takeover_confirmation_path, rails routes it to the update method instead. Server log:
Started POST "/assignments/takeover_confirmation" for ::1 at ...
Processing by AssignmentsController#update as HTML
Mitigation
If I put the update route definition after the takeover_confirmation one, it works as intended (didn't check a POST to update though).
Furthermore, after writing all this I found out I used the wrong request type for the update method in routes.rb (POST instead of PATCH). Doing this in routes.rb does indeed solve my problem:
patch 'assignments/:id' => 'assignments#update', as: 'assignment'
However, even when defining it as POST, Rails should not direct a POST request to the existing path "/assignments/takeover_confirmation" to a completely different action, should it?
I fear the next time I use two POST routes for the same controller it will do the same thing again.
It seems I have a severe misconception of Rails routing, but cannot lay my finger on it...
Edit: Solution
As katafrakt explained, the above request to /assignments/takeover_confirmation matched the route assignments/:id because Rails interpreted the "takeover_confirmation" part as string and used it for the :id parameter. Thus, this is perfectly expected behavior.
Working Example
For the sake of completeness, here is a working (if minimalistic) route-definition that does as it should, inspired by Chris's comment:
resources :assignments do
collection do
post 'takeover_confirmation'
end
end
In this example, only my manually created route is explicitly defined. The routes for update, show, etc. (that I defined manually at first) are now implicitly defined by resources: :assignments.
Corresponding excerpt from rake routes:
...
takeover_confirmation_assignments POST /assignments/takeover_confirmation(.:format) assignments#takeover_confirmation
...
assignment GET /assignments/:id(.:format) assignments#show
PATCH /assignments/:id(.:format) assignments#update
PUT /assignments/:id(.:format) assignments#update
DELETE /assignments/:id(.:format) assignments#destroy
....
Thanks for the help!
However, even when defining it as POST, Rails should not direct a POST request to the existing path "/assignments/takeover_confirmation" to a completely different action, should it?
It should. Rails routing is matched in exact same order as defined in routes.rb file (from top to bottom). So if it matches a certain rule (and /assignments/takeover_confirmation matches assignments/:id rule) it stops processing the routing.
This behaviour is simple and efficient. I imagine that any kind of "smart" matching the best route would result in cumbersome and unexpected results.
BTW that's why catch-all route used to be defined at the very bottom of routing file.
Related
I just want to redirect all the break url to a specific method ..
Is there any short cut method for doing that ..
Someone help me find solution for this
duplicate question
Rails catch-all route
put
match '*path' => 'your_controller#your_action' at the end of the
routes.rb file. This is important, since the routes are stepped
through top down.
See also http://guides.rubyonrails.org/routing.html -> 3.10
all routes that are not catched prior to this one will be dispatched to your_controller#your_action
I have a Customer_ratings model that allows users to leave feedback on each other. The web app is working properly, and feedback is collected, stored and displayed.
I wanted to go in and delete some feedback through the rails console, but when I enter Customer_rating.all, I get the following error:
LoadError: Unable to autoload constant Customer_rating, expected /Users/myapps/app/models/customer_rating.rb to define it
Similarly, if I enter Customer_rating[0], I get:
RuntimeError: Circular dependency detected while autoloading constant Customer_rating
I don't have this issue while accessing other tables through my console.
What could be causing the issue, and why wouldn't this error prohibit Customer_ratings from working properly through the web app?
It seems like a case of messed up naming convention.
As per Rails naming convention, file names should be in snake_case and class names in CamelCase. In your scenario, the file name should be customer_rating.rb and class name should be CustomerRating.
After making these changes, use CustomerRating.all(as the updated class name is CustomerRating) to fetch all the customer ratings. Do not use Customer_rating.all.
I'd also like to add a scenario of this problem that I found for future reference.
I'm running Rails 4.0 and I had this same problem but what happened was I had a model named Student inside student.rb that was contained in a folder called Student. I didn't realize it at first but the folder name was the problem. Changing the folder name to something other than a model name solved the problem.
If the naming convention is not off, like in this question, it may be an issue on initial first load if you're making a lot of requests at the same time. I experienced this with nested controllers Api::LocationsController.
I solved it by enabled eager_load in development env:
Rails.application.configure do
...
# Enabled this to avoid crash unable to autoload controller
# Error happens when you start and stop server on initial requests
# solution found via https://github.com/rails/rails/issues/32082#issuecomment-367715194
config.eager_load = true
I based this off of rails issues comments: https://github.com/rails/rails/issues/32082#issuecomment-367715194
You just need to modify the name of the Module
For example if the link is http://sairam.esy.es/users/customer_rating then
you controller should like be
module Users
class RatingController
# ...
def customer_rating
# ...
end
# ...
end
end
I am testing my views with RSpec. Due to some changes, there is now a call to url_for in the view and all the spec that I wrote for show actions are failing:
No route matches {:controller=>"events", :action=>"show"}
The :id part is missing and because of that the call fails.
(I know that I can just stub that failing method call)
The only thing I found is pretty old and looks like a bad workaround. Also the RSpec documentation does not show something helpful.
Is there a proper way to tell RSpec that it should be on something like events/123?
Some more context:
In the view
I call a helper, something like this:
def some_helper_method
url_for(only_path: false)} + "something_else"
end
The call to that method fails with
Failure/Error: render
ActionView::Template::Error:
No route matches {:controller=>"events", :action=>"show"}
I'm currently fixing it by stubbing the call to that method view.stub(some_helper_method: 'SOME_URL')
Okay, I think I found your answer. I cloned your repo and debug a lot =P until I found that you need to infer the desired parameters in your view spec, since rspec-rails only provides the :controller parameter for the view being rendered. Well, in an attempt to understand better what the issue is, I found this explanation in the rspec-rails documentation:
View specs infer the controller name and path from the path to the view template. e.g. if the template is "events/index.html.erb" then:
controller.controller_path == "events"
controller.request.path_parameters[:controller] == "events"
This means that most of the time you don't need to set these values. When spec'ing a partial that is included across different controllers, you may need to override these values before rendering the view
In other words, the previous link that you posted still works and is still the solution for the parameters "issue" in your view spec. You can follow the discussion about the topic in this PullRequest thread.
So, the conclusion is that if you really don't want to stub out your call to url_for helper (or the helper that is calling it), you need to supply the desired :id parameter since there's no route for any show action without an :id param (as pointed out by you). Our final result (tested) is indeed:
controller.request.path_parameters[:id] = event.id
If this a workaround ? I really don't think so, since rspec-rails itself is internally using this feature to provide the :controller param.
I'm sorry I couldn't be more helpful before, but I think that this closes all of our main doubts.
My opinion ? If you really don't need to make any assertions based on the url_for behavior (or any helper behavior for that matter), just stub it, since it is expected to already have been tested in isolation. Otherwise, use the :id param, and don't worry about it. It is not a workaround.
Cheers friend !
If you will face the id problem the try this in your route.rb
get "events/show/:id", :to=> "events#show"
I have a Rails 4.1.0 mountable engine. In the engine's application_helper.rb:
module MyEngine
module ApplicationHelper
def test123
"test123"
end
end
end
The method is in the dummy app's view general/index.html.erb view:
%<= test123 %>
This works. However, when I change the string returned by def test123 and refresh the browser, the new string is not displayed.
Of course, restarting the web server in the dummy app shows the new string.
So the question is, how to reload the engine's files without having to restart the web server?
PS. I am preferably looking for a way to do this using Rails itself, or a specific gem that solves this problem (but not the generic gems like Guard, Spork etc. although if all else fails, I will consider those too.)
PPS. There are similar questions on SO, but I have tried them all (even though they are for Rails 2.x, 3.x), and they have not worked for me.
You should explicitly require dependent helpers:
# engines/my_engine/app/controllers/my_engine/application_controller.rb
require_dependency "my_engine/application_helper" # This is a key point!
module MyEngine
class ApplicationController < ::ApplicationController
helper ApplicationHelper
...
you can use some thing like zeus which is helping a lot in watching project files for changes except in some cases when you change the configuration files it doesn't work and needs to be manually restarted.
but overall in most cases this works more than awesome
I just played with ember routing example. It looks quite interesting. Especially if you are going to build all your application on Ember framework.
But parameters in url follows after '#'. That means you can't copy and send a link to someone if client has to login with postback (if only to set a cookie with login parameters). Is there a better option - maybe use '?' instead of '#'?
You may also have a look at Ember.Router.
There are two good start points # https://gist.github.com/2679013 and https://gist.github.com/2728699
A lot of fixes have been made the last couple of days.
EDIT
A brand new guide is now available # https://emberjs-staging-new.herokuapp.com/guides/outlets#toc_the-router
Here is a full example courtesy of https://github.com/jbrown
http://jsfiddle.net/justinbrown/C7LrM/10/