Having read this:
"No route matches" error?
I'm trying to figure out if there is a gem or way to monkey patch actionpack to get around this constraint.
Basically, I'm writing specs (they run fast), and I don't understand why actionpack throws this error when being applied to an object which isn't "saved".
For two reasons:
Why is it throwing a "No route matches" when it really should be throwing something more meaningful (e.g. object must be saved before a route can be constructed, or object ID is nil). The exception seems a little obscure.
I shouldn't have to save the object at all if all I am trying to do is generate a url for that object, given the ID is populated using a factory or something similar.
This constraint makes it a pain to write fast tests, unless I'm missing something...
True, the error message is a bit obscure. Regarding your second point, you don't need to save an object to generate a URL, the helper will work just as well with a literal value.
building_path(1) # GET /buildings/1 => BuildingsController#show, params={:id=>"1"}
So in the example the object can be replaced with any value:
get :show, :id => "1"
For example, if you use rails generate scaffold Article, RSpec will build a spec like this:
def mock_article(stubs={})
(#mock_article ||= mock_model(Article).as_null_object).tap do |article|
article.stub(stubs) unless stubs.empty?
end
end
describe "GET show" do
it "assigns the requested article as #article" do
Article.stub(:find).with("37") { mock_article }
get :show, :id => "37"
assigns(:article).should be(mock_article)
end
end
which does not hit the database.
Related
I want to redirect to 404 if request url contains invalid parameters, but I did not know what is the best way to achieve that.
For example:
The request url: http://example.com/a_path/?invalid_parameter=foo
In this case, I want rails return 404. My stupid way is iterating params and check if contains invalided keys:
def redirect_404_if_invalid_params
valid_params = [:valid_key_1, :valid_key_2, :valid_key_3]
params.each do |key, value|
if !valid_params.include?(key)
redirect 404
end
end
end
But is there any better way to do this trick?
Thanks.
You can use Strong Parameters to filter out the parameters you don't want in an action. To do this, you would have to set:
ActionController::Parameters.action_on_unpermitted_parameters = :raise
so Strong Parameters fails whenever you receive parameters that are not explicitly allowed. Then, specify the parameters you do want to be available, like:
def valid_params
params.require(:param_a).permit(:attr_a, :attr_b)
end
And consume your parameters like valid_params[:attr_a], instead of referencing params directly.
You can catch ActionController::UnpermittedParameters and throw your 404 accordingly or do anything else you may need, though I don't suggest you to this. 404 is the HTTP error for "Not Found", having invalid parameters sound more like 422, "Unprocessable Entity".
Assuming that you want to do exactly that for some reason, here's a compact way.
valid_params = [:valid_key_1, :valid_key_2, :valid_key_3]
if (params.keys - valid_params.map(&:to_s)).present?
not_found
end
Take all params, remove all known valid params. If there's something left, it must be invalid keys. In which case, raise.
BTW, not_found in code above is defined as shown in this answer.
Although, for most cases this is not needed and strong parameters should be used instead. With strong parameters it's another approach: we don't care about unknown parameters, we extract only known good parameters and discard the rest.
I had this same problem and researched extensively. This was my solution:
In my application_controller.rb I did this:
rescue_from ActiveRecord::RecordNotFound, with: :not_found
rescue_from Exception, with: :not_found
rescue_from ActionController::RoutingError, with: :not_found
def raise_not_found
raise ActionController::RoutingError.new("No route matches # {params[:unmatched_route]}")
end
def not_found
respond_to do |format|
format.html { render :file => "#{Rails.root}/public/404", :layout => false, :status => :not_found }
format.xml { head :not_found }
format.any { head :not_found }
end
end
And then in your routes.rb file do this:
get '*unmatched_route', to: 'application#raise_not_found'
What does the code do.
You probably have an idea what the rescue_from code line of code does.
raise_not_found if for the line of code you put in the routes file. This method triggers the RoutingError and when this happens, not_found is called.
Any url that does not exit in your routes file would yield the 404.html page.
I've spent several hours trying to understand why my Rails 4 app doesn't seem to want to route to the expected controller action.
In summary: every single action that I attempt in the browser URL seems to go to the index view, even though my routes appear to be correct. I've attempted to restart the server, etc hoping that might fix it, but right now I'm completely lost.
For example, if I try to access the URL at localhost:3000/leads#new I get the following error message:
Missing template leads/index, application/index with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder, :raw, :ruby, :jbuilder, :coffee]}. Searched in: * "/Users/me/Sites/azimuth/app/views"
If I add a template for index.html.erb to the app/views/leads folder, then I don't get the error message - however every single path goes to that same index view - leads#show, leads#edit, etc - all of them.
Here is the routes.rb:
Azimuth::Application.routes.draw do
# get 'leads', to: 'leads#new'
resources :applicants
resources :contacts
resources :leads
PagesController.action_methods.each do |action|
get "/#{action}", to: "pages##{action}", as: "#{action}_page"
end
root "pages#home"
end
note that the commented line - get 'leads', to: 'leads#new' - seems to be the only way to properly get the routing to work. Using resources :leads (which I understand is best practice?) is giving me fits.
Here's the leads_controller.rb:
class LeadsController < ApplicationController
def new
#lead = Lead.new
end
def create
#lead = Lead.new(lead_params)
if #lead.save
flash[:success] = "Thank you for reaching out! We'll be in touch soon."
redirect_to 'home'
else
render 'new'
end
end
def index
#lead = Lead.all
end
private
def lead_params
params.require(:lead).permit(:first_name, :last_name, :subject, :message)
end
end
Rake routes - appears that things should work fine. (Note this is just showing the routes relevant to the Leads object).
Prefix Verb URI Pattern Controller#Action
leads GET /leads(.:format) leads#index
POST /leads(.:format) leads#create
new_lead GET /leads/new(.:format) leads#new
edit_lead GET /leads/:id/edit(.:format) leads#edit
lead GET /leads/:id(.:format) leads#show
PATCH /leads/:id(.:format) leads#update
PUT /leads/:id(.:format) leads#update
DELETE /leads/:id(.:format) leads#destroy
I'm very confused, can't seem to track down what's going on, and would appreciate any help!
Correct me if you are wrong, but I think you are trying to access the wrong URL. You said you were visiting localhost:3000/leads#new in your browser. The correct URL for that route would be localhost:3000/leads/new
When are you are defining routes in your config/routes.rb file, the #'s are used to let rails know that you are specifying that a method of one of your controllers should respond to this URL. The actual URL's do not contain #'s (typically speaking).
I am attempting to validate some dates in a Rails 4 application and it's not working.
I looked at lots of similar code samples, like this Same custom validation for several fields in Rails and this http://railscasts.com/episodes/211-validations-in-rails-3. (And others more complicated). I don't understand why my example doesn't work. Trying to find the problem, I've stripped out the actual validation code, and left a stub, because the validation doesn't seem to run and that would seem the base problem (or at least, the first problem).
Here's the code in the validator (which is in app/validators/custom_date_validator.rb
class CustomDateValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
puts "Custom validator was called"
record.errors[attribute] << "Date Not Valid" unless false
end
end
Here's the code from the model:
class CaseInformation < ActiveRecord::Base
include ApplicationHelper
validates :started, :custom_date => true
The error message is:
argument out of range
Extracted source (around line #104):
respond_to do |format|
104 if ( #case_information.update_attributes(params[:case_information]) && #case_information.update_attributes(params[:law_guardians]) )
format.html { redirect_to #case_information, notice: 'Case information was successfully updated.' }
format.json { head :no_content }
The error is (I think) intentional, I put in a date of "1/123/2012", but the validator isn't catching it. Well, actually I stripped out all the validation code and have (I think) the validator writing to the log, as it doesn't seem the validator is even running, at least, there's nothing in the log that shows it ran.
I know that the validator code is being found because in the model, if I change the validation name even a bit I get an error that the validation can't be found.
Really stupid noob question I am sure, your patience is appreciated.
Thanks.
It's indeed failing before your validator gets run-- the "argument out of range" error is what happens when you call Time.parse on a date that can't exist. In irb:
2.0.0p247 :004 > require 'time'
=> true
2.0.0p247 :005 > Time.parse("1/123/2012")
ArgumentError: argument out of range
And I'm betting that started is a datetime or timestamp attribute, right? Rails tries to convert the string from the params hash to their types before any validations are run.
Can someone help me create mocks for the following code. I want to add a controller method in existing controller by following name and want to test its behavior to a movie class containing title, director, rating etc as table instances. Unfortunately I am unfamiliar with BDD commands to use here.
describe MoviesController do
describe "#find_same_director" do
before :each do
fake_movies = [mock('movie1'), mock('movie2')]
end
context "with invalid attributes" do
it "flashes no such director message" do
flash[:notice].should_not be_nil
end
it "redirects to the index method" do
response.should redirect_to movies_path
end
end
context "with valid attributes" do
it "calls model method to find all movies" do
movie = Movie.find_with_director, {:director => 'George Lucas'}
get :show, id: #fake_movies
assigns(:movie).should eq(#fake_results)
end
it "renders the #find_same_director view" do
get :find_same_director, id: #fake_movies
response.should render_template :find_same_director
end
end
end
end
Are you noticing that you are trying to test different things at different test cases? (the first contexts you aren't executing the action "get :x", the last ones you are doing "get :show"
First of all you should think about the behavior of your code, so, i can think of two contexts (what kind of situations do you have in this case):
# with valid parameters(for e.g.: i should pass the right data, before this context i must create the data for the text).
# with invalid parameters(for e.g: the parameters passed to the GET request should not be existent on the system).
Then you should think about what happens when this contexts are active.
context "with valid parameters" do
it "should return the other movies of the same director, and assign it to the #movies"
it "should render the template of the #find_same_director page"
end
context "with invalid parameters" do
it "should redirect to the movies_path"
it "should put a flash message that the director is invalid"
end
After you think about the test cases do you have to think about how to implement them, i'll give you a hint:
it "should return the other movies of the same director, and assign it to the #movies" do
# THINKING ABOUT BDD HERE YOU SHOULD THINK OF THIS CODE SECTIONS AS FOLLOW:
# GIVEN ( OR THE CONDITIONS FOR THE ACTION HAPPEN)
#director = Director.new
movies = [Movie.new, Movie.new]
#director.movies = movies
# HERE ILL FIX THE VALUES SO I CAN USE IT ON MY EXPECTATIONS
Director.stub!(:find).with(#director_id).and_return(#director)
# WHEN, THE ACTION HAPPENED
get :find_same_director, :id => #director_id
# THEN THE EXPECTATIONS THAT SHOULD BE MATCHED
assigns(:movies).should == #director.movies
end
For a more real experience with tests i recommend you to watch the screencasts: http://destroyallsoftware.com/
I have successfully been able to create a user using NanoStoreInMotion. I can query on it and see the object. What I am having an issue with is outputting data. For example:
User model:
class User < NanoStore::Model
:first_name => "Jason"
:last_name => "Beam"
user = User.find(:first_name => "Jason")
#This returns the object [#<User:0xab96ab0>]
end
With ruby, normally to call on a specific field I would just do user.first_name. This throws an error, "undefined method 'first_name' for [#]:Array (NoMethodError)
If I remember correctly find returns an Array so you need to do something like user[0].first_name
Based on my experience I would advise against NanoStoreInMotion. I wouldn't exclude my own inability to use it correctly, but for me it was unreasonably slow (storing thousands of relatively complex objects) and throwing a lot of cryptic errors.
So I moved to MotionModel and I am very happy with it.