0

Newbie working on his first rails application after studying Hartl's Rails Tutorial book & video cast.

I'm using a STI model where:

class User < ActiveRecord::Base
class Kid < User
class Parent < User

User has the basic elements: name, email, etc.

The problem I'm having is with routing. I continue to work in determining which model will ultimately work best in this situation (STI or polymorphic). I've started with STI and I think I can get this to work if I can nail down the routing issues.

My problem is my edit is looking for an "update" action in the users controller when I want it to route to kidupdate I've been reading many SO posts about STI routing but I can't seem to figure out why this won't route correctly.

Rspec test. It the error comes from the "click_button"

describe "with valid information" do
  let(:new_first)   { "New First" }
  let(:new_last)    { "New Last" }
  let(:new_email)   { "new@example.com" }
  before do
    fill_in "First Name",               with: new_first
    fill_in "Last Name",                with: new_last
    fill_in "Email",                    with: new_email
    select  "Kid",  from: "Are you a Kid or Parent"
    fill_in "Password",                 with: kid.password
    fill_in "Confirmation",             with: kid.password
    click_button "Save changes"
  end

Rspec error:

     KidPages edit with valid information 
     Failure/Error: click_button "Save changes"
     AbstractController::ActionNotFound:
       The action 'update' could not be found for UsersController
     # (eval):2:in `click_button'
     # ./spec/requests/kids_pages_spec.rb:32:in `block (4 levels) in <top (required)>'

Routes:

       root        /                         static_pages#home
       help        /help(.:format)           static_pages#help
    contact        /contact(.:format)        static_pages#contact
     signup        /signup(.:format)         users#new
     signin        /signin(.:format)         sessions#new
    signout DELETE /signout(.:format)        sessions#destroy
    kidshow        /kids/:id(.:format)       users#kidshow
  kidupate PUT    /kids/:id(.:format)       users#kidupdate
    kidedit        /kids/:id/edit(.:format)  users#kidedit
      users GET    /users(.:format)          users#index
            POST   /users(.:format)          users#create
   new_user GET    /users/new(.:format)      users#new
  edit_user GET    /users/:id/edit(.:format) users#edit
       user GET    /users/:id(.:format)      users#show
            PUT    /users/:id(.:format)      users#update
            DELETE /users/:id(.:format)      users#destroy
   sessions POST   /sessions(.:format)       sessions#create
new_session GET    /sessions/new(.:format)   sessions#new
    session DELETE /sessions/:id(.:format)   sessions#destroy

routes.rb

  root to: 'static_pages#home'
  match '/help',    to: 'static_pages#help'
  match '/contact', to: 'static_pages#contact'
  match '/signup',  to: 'users#new'
  match '/signin',  to: 'sessions#new'
  match '/signout',  to: 'sessions#destroy', via: :delete
  match 'kids/:id', to: 'users#kidshow',  :as => 'kidshow'
  match 'kids/:id', to: 'users#kidupdate', :via => 'put', :as => 'kidupdate'
  match 'kids/:id/edit', to: 'users#kidedit',  :as => 'kidedit'
  resources :users
  resources :sessions, only: [:new, :create, :destroy]

I've been struggling with these concepts and this problem for weeks and I appreciate the help.

iamreff
  • 15
  • 3
  • Also, my two cents about STI and Polymorphism. They are actually used for every different use cases. You use STI when you have two types of users that differ maybe in a handful of columns. You use a "type" colomn to assign them in UserTypeA model and UserTypeB model. [Polymorphism in Rails(click)](http://railscasts.com/episodes/154-polymorphic-association) is something totally different. The improvement to STI is to use 2 profile tables to store their different users fields and use User table primarily for authentication data. – benzhang Oct 19 '12 at 14:05

1 Answers1

2

You can organize your controller better to avoid this issue altogether. See below

routes.rb

resources :kids 
resources :parents

This would directly give you named path:

edit_kid_path(kid_id) 
edit_parent_path(parent_id)

kids_controller.rb

class KidsController < ApplicationController
  def update
  end
end

parents_controller.rb

class ParentsController < ApplicationController
  def update
  end    
end

If you want to share controller behavior, You can potentially do

class KidsController < UsersController
end

class ParentsController < UsersController
end

and put the common actions in users_controller.rb and override them in the child controllers.

The decision on what model relationships to use should be independent of how you structure your controllers. Controllers and routing is one area of consideration. Modeling data is entirely separately consideration. And declaring resources are just shortcuts for some matching routes.

If you are not going to use users#update etc, declaring resources :users is unnecessary as you are not using any of the routes that declaration provides.

benzhang
  • 471
  • 4
  • 11
  • Thanks for the detailed response Ben. I actually started with separate controllers where I still had the "kidupdate" action still routing to users#update, when I think I've routed it to users#kidupdate. After reading Alex Reisner's popular post on STI inheritance I decided to simplify and try to host all the methods in one class. Yet the problem persists. – iamreff Oct 19 '12 at 14:07
  • Are you sure you actually hit the server with a put request on kids/:id? Can you paste your server log? tail -f logs/development.log should give you that information. If you used user_path(@kid) in your form view, that is going to use users#update. – benzhang Oct 19 '12 at 14:17
  • Yes it hits Rendered /Users/iamreff/.rvm/gems/ruby-1.9.3-p194/gems/actionpack-3.2.3/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb within rescues/layout (1.7ms) Started PUT "/users/1" for 127.0.0.1 at 2012-10-19 10:18:45 -0400 AbstractController::ActionNotFound (The action 'update' could not be found for UsersController): – iamreff Oct 19 '12 at 14:20
  • That is the problem. it is hitting /users/1 with put, that is going to use users#update. You either do "form_for kid, url: '/kid/kid_id' do |f|" or "form_for kid, url: kidupdate_path(kid) do |f|". There are lots of magic in "form_for @user", the assumption form_for makes does not work for your situation so you have to be more explicit! – benzhang Oct 19 '12 at 14:26
  • Thanks Ben. it seems my problem was I didn't know how to connect the form action with the route. Much appreciated! – iamreff Oct 20 '12 at 18:36