Rails: No route matches [POST] nested resources - ruby-on-rails

In the routes.rb I have this nested resource
# OBSERVATIVE SESSIONS
resources :observative_sessions do
# OBSERVATIONS
resources :observations
end
In observations_controller.rb
def new
#observative_session = ObservativeSession.find(params[:observative_session_id])
#observation = Observation.new
#observation.observative_session_id = #observative_session.id
end
def create
#observative_session = ObservativeSession.find(params[:observative_session_id])
#observation = #observative_session.observations.build(observation_params)
#observation.user_id = current_user.id
respond_to do |format|
if #observation.save
format.html { redirect_to [#observative_session, #observation], notice: 'Observation was successfully created.' }
format.json { render :show, status: :created, location: #observation }
else
format.html { render :new }
format.json { render json: #observation.errors, status: :unprocessable_entity }
end
end
end
And in observations_controller_test.rb I set up both observation and observative session. The test of new works just fine.
class ObservationsControllerTest < ActionDispatch::IntegrationTest
include Devise::Test::IntegrationHelpers
setup do
#observative_session = observative_sessions(:one)
#observation = observations(:two)
sign_in users(:admin_user)
end
test "should get new" do
get new_observative_session_observation_path(#observative_session)
assert_response :success
end
test "should create observation" do
assert_difference('Observation.count') do
post observative_session_observation_path(#observative_session, #observation), params: { observation: { start_time: #observation.start_time, description: #observation.description, rating: #observation.rating, notes: #observation.notes, celestial_body_name: #observation.celestial_body_name, telescope_name: #observation.telescope_name, binocular_name: #observation.binocular_name, eyepiece_name: #observation.eyepiece_name, filter_name: #observation.filter_name, user_id: #observation.user_id, observative_session_id: #observation.observative_session_id }}
end
But this is the error I get in the creation test
test_should_create_observation
ActionController::RoutingError: No route matches [POST] "/observative_sessions/980190962/observations/298486374"
I can't understand what I'm doing wrong.
Thanks for your help.

When you say POST observation_session_observation_path(#observation_session, #observation) you are telling it to post to a url the has both :observation_session_id and an :id in the params, where the id is that of #obseravtion. However, POST paths for create actions don’t take that last id param (ostensibly you are creating a new record with that action).
Try dropping #observation from your path helper (and make sure you are using the correct create path: observation_session_observations_path(#observation_session).
You can do rake routes to see your routes in your terminal, or localhost:3000/rails/info/routes to see it in the browser.
I also see in your new action you are assigning the observation_session_id manually. I recommend you either do what you do later and call #obervation_session.observations.build, or Observation.new(observation_session: #observation_session). You should avoid setting ids like that.

Related

Confused about default behavior of model initializer

Given the following...
Model:
class Equipment < ApplicationRecord
belongs_to :user
end
Controller:
class EquipmentController < ApplicationController
before_action :set_equipment, only: %i[ show edit update destroy ]
# GET /equipment/new
def new
#equipment = Equipment.new
end
# POST /equipment or /equipment.json
def create
#equipment = Equipment.new(equipment_params)
respond_to do |format|
if #equipment.save
format.html { redirect_to [#equipment.user, #equipment], notice: "Equipment was successfully created." }
format.json { render :show, status: :created, location: #equipment }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #equipment.errors, status: :unprocessable_entity }
end
end
end
Test:
class EquipmentControllerTest < ActionDispatch::IntegrationTest
setup do
#equipment = equipment(:one)
sign_in_as(#equipment.user)
end
test "should get new" do
get new_user_equipment_url(#equipment.user.id)
assert_response :success
end
My test fails here, because equipment.user is null.
<%= form_with(model: [equipment.user, equipment]) do |form| %>
However, when I visit the corresponding URL in the development environment in the browser, it works. Debugging the development environment, the Equipment.new sets the user on the new equipment object. However, when running the test, the user is not set.
Making this change in the controller fixes the issue, but I don't understand the difference in behavior between the integration test and using the browser. Can someone explain?
#equipment = Equipment.new user: Current.user
I think this would be a lot less confusing if you wrote the test properly:
class EquipmentControllerTest < ActionDispatch::IntegrationTest
setup do
#user = users(:one)
sign_in_as(#user)
end
test "should get new" do
get new_user_equipment_url(#user)
assert_response :success
end
end
You should not use the equipment fixture when you're testing the new/create action - after all what your test is supposed to cover is the form for a completely new resource. The equipment fixture would be used when showing and updating an existing equipment.
Then I think you're falling for a classic trap and confusing the #equipment instance variable in your test and the instance variable in your controller with the same name.
Remember that your test and the controller are completely separate instances of different classes - sometimes even running in a completely different processes. Data doesn't just magically teleport itself from the test to the controller - it has to be passed through parameters or be loaded from the database.
#equipment in your controller is just a new Equipment instance that you have created there which doesn't have a user assigned. Thats why you need to assign the user.
Note that unless your users need to be able to create equipment for other users you don't actually need to have a nested route like this. You could just use GET /equipment/new and fetch the user from the session (or a token) instead of a parameter.
For example given a typical Devise setup you would do:
class User < ApplicationRecord
has_many :equipment
end
class EquipmentController < ApplicationController
before_action :set_equipment, only: %i[ show edit update destroy ]
before_action :authenticate_user! # ensure that a user is logged in.
# POST /equipment or /equipment.json
def create
#equipment = current_user.equipment.new(equipment_params)
respond_to do |format|
if #equipment.save
format.html { redirect_to #equipment, notice: "Equipment was successfully created." }
format.json { render :show, status: :created, location: #equipment }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #equipment.errors, status: :unprocessable_entity }
end
end
end

Testing Rails 4 Controller

I'm having some trouble understanding what my be wrong with my test, but I keep getting No Route Match when testing the update method for a controller. Submiting a form through a browser works, though.
My Routes file:
namespace :merchant do
resources :users
get '/signup', to: "users#new"
end
My Controller:
def update
respond_to do |format|
if #merchant_user.update(user_params)
format.html { redirect_to #merchant_user, notice: 'User was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'show' }
format.json { render json: #merchant_user.errors, status: :unprocessable_entity }
end
end
end
My Test:
test "should update user" do
user = users(:jon)
user.first_name="Jonas"
put :update, :merchant_user =>user.attributes
assert_response :success
end
Result:
1) Error:
Merchant::UsersControllerTest#test_should_update_user:
ActionController::UrlGenerationError: No route matches {:merchant_user=> {"id"=>"846114006", "email"=>"jon.sims#whatever.com", "first_name"=>"Jonas", "last_name"=>"Sims", "password_digest"=>"$2a$10$LVbV7pkd7li8sobYEauoS.4JVA2ZHzAXgPFbyiojYqgcDBHUE9bXW", "type"=>"Merchant::User", "created_at"=>"2013-07-11 22:59:41 UTC", "updated_at"=>"2013-07-11 22:59:41 UTC"}, :controller=>"merchant/users", :action=>"update"}
test/controllers/merchant/users_controller_test.rb:45:in `block in <class:UsersControllerTest>'
Any hint?
You need to pass in the id of the user for the test to run correctly.
Try this:
put :update, id: user.id, merchant_user: {}
You're seeing this error because the router expects resource/:id, but your are not passing the id.
Users#update is a member action, which requires an id. The router expects a user id param: /users/:id/update. Without the id the method has no way of finding the user you want to update.
From the way your routes look it seems like it's expecting an id parameter.
If you do rake routes from the command line it will probably show a route that looks like
/merchant/users/:id/update
If you pass in the id like this put :update, id: user.id, merchant_user: user.attributes it should work.

Rails 3.2 redirection skips Create action

I have a Rails 3 blog-style application, where I've got an admin namespace for backend purposes and a controllers/admin subfolder containing the respective posts_controller.rb.
So the page's root url is set to "admin/posts#index", and post creation works fine, except when I configure the routes file to redirect the user to root_url if he types "/admin/articles".
This is my routes file:
BlogDos::Application.routes.draw do
# Index
root to: "admin/posts#index"
# If I uncomment these two lines below, the post#create function doesn't work. When I
# submit the "new post" form, the controller just skips the function entirelly and
# redirects me to admin/posts#index without creating the new post.
# match "admin/posts" => redirect("/")
# match "admin/posts/" => redirect("/")
namespace :admin do
resources :cpanel
resources :posts do
resources :comments, :only => [:create, :destroy]
end
root to: "cpanel#index"
end
..
end
And this is my posts_controller.rb
def create
#usuario = current_user
#post = #usuario .posts.create(params[:post])
respond_to do |format|
if #post.save
format.html { redirect_to article_dir_path(#post.year, #post.month, #post.slug), notice: 'Article was successfully created.' }
format.json { render json: article_dir_path(#post.year, #post.month, #post.slug), status: :created, location: article_dir_path(#post.year, #post.month, #post.slug) }
else
format.html { render action: "new" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
Strangely, this only happens with the Create action, If i edit an article and update it, everything works fine.
I have sorted out almost everything from looking at Rails tutorials and QA websites, except this little problem, I'm sure it is something rather simple, but i'm new to Rails and not very familiar with its routing mechanics yet.
The form that creates the post submits to
/admin/posts
If you redirect that route to the index page, the controller action is never called.

How to send params with FactoryGirl (as opposed to manually sending the params as a hash)?

I have the following rspec test that works:
it "redirects to the created api_key" do
post :create, :api_key => {:api_identifier => "asdfadsf", :verification_code =>
"12345"}
response.should redirect_to(ApiKey.last) #(or any other test function)
end
But I use Factory girl so I don't have to manually create api_keys.
How can I replicate the above functionality, but use factory girl?
Using:
it "redirects to the created api_key" do
test = FactoryGirl.build(:api_key)
post :create, :api_key => test
response.should redirect_to(ApiKey.last) #(or any other test function)
end
or:
it "redirects to the created api_key" do
post :create, FactoryGirl.build(:api_key)
response.should redirect_to(ApiKey.last) #(or any other test function)
end
Gives me null values for the :api_key value when I arrive at my controller.
For reference, here is my create action that this test is testing:
def create
#api_key = ApiKey.new(params[:api_key])
#api_key.user = current_user
pp #api_key
respond_to do |format|
if #api_key.save
format.html { redirect_to #api_key, notice: 'Api key was successfully created.' }
format.json { render json: #api_key, status: :created, location: #api_key }
else
format.html { render action: "new" }
format.json { render json: #api_key.errors, status: :unprocessable_entity }
end
end
end
try:
post :create, :api_key => FactoryGirl.attributes_for(:api_key)
Using build doesn't actually create a record. It just pretends it did. Using attributes_for will give you the attributes of an object. This is mostly used in the context you describe. Note that this too will not create an object.
What I would do is this if the response is successful/redirect:
response.should be_redirect
Or even better use expect.

post functional test failing

I am trying to create a functional test that tests the create method in one of my controllers. For the life of me, I cannot figure out why this is failing. I am getting one failure, and zero errors:
1) Failure:
test_should_create_order(OrdersControllerTest) [/Users/user/rails_work/depot/test/functional/orders_controller_test.rb:38]:
"Order.count" didn't change by 1.
<3> expected but was
<2>.
So, Im pretty sure this means that my functionals test was unable to make an Order. Here is my test:
setup do
#order = orders(:one)
end
test "should create order" do
assert_difference('Order.count') do
post :create, order: #order.attributes.slice(Order.accessible_attributes)
end
assert_redirected_to store_url
end
my orders fixture:
one:
name: Dave Thomas
address: MyText
email: dave#example.org
pay_type: Check
and my Order#create controller:
def create
#order = Order.new(params[:order])
#order.add_line_items_from_cart(current_cart)
respond_to do |format|
if #order.save
Cart.destroy(session[:cart_id])
session[:cart_id] = nil
format.html { redirect_to store_url, notice: 'Thank you for your order' }
format.json { render json: #order, status: :created, location: #order }
else
#cart = current_cart
format.html { render action: "new" }
format.json { render json: #order.errors, status: :unprocessable_entity }
end
end
end
Now, if I change the setup method in my functional test to:
#order = Order.create(orders(:one))
Instead of:
#order = orders(:one)
The failure disappears, but I get about 8 of these errors:
NoMethodError: undefined method `stringify_keys' for #<Order:0x007f8c62dbb960>
If anyone can help me fix this functional test, I would more than appreciate it. Any and all input is welcome.
Bottom line: assign each order attribute individually.
I'm not familiar with the 'mass assignment' vulnerability (new to Rails), but here (Pragmatic Forums) is a case of someone having difficulty with that specific test because of it.
Try spelling out each attribute of the order individually. Instead of
post :create, order: #order.attributes.slice(Order.accessible_attributes)
use
post :create, order: {
address: #order.address,
email: #order.email,
name: #order.name,
pay_type: #order.pay_type
}
The test as a whole will be this:
test "should create order" do
assert_difference('Order.count') do
post :create, order: { address: #order.address, email: #order.email, name: #order.name, pay_type: #order.pay_type }
end
assert_redirected_to store_path
end

Resources