I have a small web app in Ruby on Rails which lists antique documents and the locations that are referenced by the documents. I can update locations that are already attached to documents, but when trying to create a new document, I get the following error
undefined method `locations' for nil:NilClass
The NilClass occurs in LocationController#new:
def new
#location = #document.locations.new
end
This is called from the view as such
<%= form_for(#location,:url=>new_document_location_path(#document)) do |f| %>
<%= render :partial => "document_locations/form", :locals =>{:f=>f} %>
<%= f.submit "Save",:class=>"span2 btn" %>
<%end%>
So for some reason, the #document class isn't being referenced by the controller. I know the class is accessible to the view and in scope as I am able to view document attributes within the form (eg <%= #document.id %>
So, why is the Controller not able to reference the #document variable? I'm assuming this is down to how I am passing the class variable. Eitherway, I'd be very grateful for some pointers.
This is how the models are defined
class Document < ActiveRecord::Base
has_many :locations, :dependent => :destroy
end
class Location < ActiveRecord::Base
belongs_to :document
end
Here's my routes
Pastpaper::Application.routes.draw do
# added in to test
match '/documents/:id/locations/create' => 'locations#create'
resources :documents do
collection do
match 'permanent_delete/:id' => 'documents#permanently_delete',:as => :permanent_delete
end
match '/authorinfo' => 'documents#authorinfo',:as => :authorinfo
match '/publicationinfo' => 'documents#publicationinfo',:as => :publicationinfo
match '/images' => 'documents#itemimages' ,:as => :itemimages
match '/locations' => 'documents#locations',:as => :locations
match '/itempeople' => 'documents#itempeople' ,:as => :itempeople
match '/people_facts_locations' => 'documents#people_facts_locations',:as => :people_facts_loc
resources :locations, :controller=>"locations"
resources :people ,:controller => "document_people"
resources :document_facts
resource :facts
resources :document_photos
end
resources :home do
collection do
get 'document_search','simple_location_search','date_search','simple_people_search','simple_organisation_search','document_filter'
get 'search_results'
post 'search_results'
end
end
match 'documents/publicationinfo/:id' => 'documents#publicationinfo',:as => :publicationinfo
match 'documents/update/publishinginfo/:id' => 'documents#publishinginfo',:as => :publishinginfo
match 'documents/document_image_remove/:id' => 'documents#remove_image',:as=>"remove_image"
match 'documents/make_primary_image/:id' => 'documents#make_primary_image',:as => :make_primary_image
match 'person_detail/:id' => 'documents#person_detail',:as=>'person_detail'
match 'about', :to=> 'pages#about'
match 'contact', :to => 'pages#contact'
match 'privacy', :to => 'pages#privacy'
match 'terms', :to => 'pages#terms'
match 'help', :to => 'pages#help'
namespace :admin do
resources :document_types
resources :statuses
resources :attribute_types
resources :event_types
resources :users
resources :orders
resources :documents
match 'restore_document/:id' => 'Documents#restore_document', :as => 'restore_document'
match 'report' => 'report#index' ,:as=>:report
match 'report/surname_report' => 'report#surname_report',:as=>:surname_report
match 'report/location_report' => 'report#location_report',:as=>:location_report
end
root :to => 'home#index'
end
And here's the relevant Controllers:
DocumentController
class DocumentsController < ApplicationController
before_filter :prepare_document ,:only => [:locations]
def locations
#locations = #document.locations.order("id asc")
#location = #document.locations.new
end
private
def prepare_document
if params[:id]
#document = Document.find(params[:id], :include => [:document_attributes])
elsif params[:document_id]
#document = Document.find(params[:document_id], :include => [:document_attributes])
end
end
end
LocationsController
class LocationsController < ApplicationController
before_filter :prepare_document
def new
#location = #document.locations.new
end
def create
#location = #document.locations.build
if #location.save
redirect_to document_locations_url(#document)
else
render "new"
end
end
def update
#location = #document.locations.find(params[:id])
if #location.update_attributes(params[:location])
redirect_to document_locations_url(#document)
else
render "edit"
end
end
def prepare_document
if params[:document_id]
if current_user.is_admin?
#document = Document.find(params[:document_id], :include => [:document_attributes])
else
#document = current_user.documents.find(params[:document_id], :include => [:document_attributes])
end
end
end
end
This is how my routes rake out
logout GET /logout(.:format) {:action=>"destroy", :controller=>"sessions"}
login POST /login(.:format) {:action=>"create", :controller=>"sessions"}
forgot_password /forgot_password(.:format) {:action=>"new", :controller=>"password_resets"}
user_home GET /profile(.:format) {:action=>"index", :controller=>"users"}
deactivateaccount /account/deactivate(.:format) {:controller=>"users", :action=>"accountdeactivate"}
changepassword /account/changepassword(.:format) {:controller=>"users", :action=>"changepassword"}
register /user/registration(.:format) {:controller=>"users", :action=>"new"}
users GET /users(.:format) {:action=>"index", :controller=>"users"}
POST /users(.:format) {:action=>"create", :controller=>"users"}
new_user GET /users/new(.:format) {:action=>"new", :controller=>"users"}
edit_user GET /users/:id/edit(.:format) {:action=>"edit", :controller=>"users"}
user GET /users/:id(.:format) {:action=>"show", :controller=>"users"}
PUT /users/:id(.:format) {:action=>"update", :controller=>"users"}
DELETE /users/:id(.:format) {:action=>"destroy", :controller=>"users"}
password_resets GET /password_resets(.:format) {:action=>"index", :controller=>"password_resets"}
POST /password_resets(.:format) {:action=>"create", :controller=>"password_resets"}
new_password_reset GET /password_resets/new(.:format) {:action=>"new", :controller=>"password_resets"}
edit_password_reset GET /password_resets/:id/edit(.:format) {:action=>"edit", :controller=>"password_resets"}
password_reset GET /password_resets/:id(.:format) {:action=>"show", :controller=>"password_resets"}
PUT /password_resets/:id(.:format) {:action=>"update", :controller=>"password_resets"}
DELETE /password_resets/:id(.:format) {:action=>"destroy", :controller=>"password_resets"}
password_reset_path /password_resets/:id/edit(.:format) {:controller=>"password_resets", :action=>"edit"}
/documents/:id/locations/create(.:format) {:controller=>"locations", :action=>"create"}
permanent_delete_documents /documents/permanent_delete/:id(.:format) {:controller=>"documents", :action=>"permanently_delete"}
document_authorinfo /documents/:document_id/authorinfo(.:format) {:controller=>"documents", :action=>"authorinfo"}
document_publicationinfo /documents/:document_id/publicationinfo(.:format) {:controller=>"documents", :action=>"publicationinfo"}
document_itemimages /documents/:document_id/images(.:format) {:controller=>"documents", :action=>"itemimages"}
document_locations /documents/:document_id/locations(.:format) {:controller=>"documents", :action=>"locations"}
document_itempeople /documents/:document_id/itempeople(.:format) {:controller=>"documents", :action=>"itempeople"}
document_people_facts_loc /documents/:document_id/people_facts_locations(.:format) {:controller=>"documents", :action=>"people_facts_locations"}
GET /documents/:document_id/locations(.:format) {:action=>"index", :controller=>"locations"}
POST /documents/:document_id/locations(.:format) {:action=>"create", :controller=>"locations"}
new_document_location GET /documents/:document_id/locations/new(.:format) {:action=>"new", :controller=>"locations"}
edit_document_location GET /documents/:document_id/locations/:id/edit(.:format) {:action=>"edit", :controller=>"locations"}
document_location GET /documents/:document_id/locations/:id(.:format) {:action=>"show", :controller=>"locations"}
PUT /documents/:document_id/locations/:id(.:format) {:action=>"update", :controller=>"locations"}
DELETE /documents/:document_id/locations/:id(.:format) {:action=>"destroy", :controller=>"locations"}
document_people GET /documents/:document_id/people(.:format) {:action=>"index", :controller=>"document_people"}
POST /documents/:document_id/people(.:format) {:action=>"create", :controller=>"document_people"}
new_document_person GET /documents/:document_id/people/new(.:format) {:action=>"new", :controller=>"document_people"}
edit_document_person GET /documents/:document_id/people/:id/edit(.:format) {:action=>"edit", :controller=>"document_people"}
document_person GET /documents/:document_id/people/:id(.:format) {:action=>"show", :controller=>"document_people"}
PUT /documents/:document_id/people/:id(.:format) {:action=>"update", :controller=>"document_people"}
DELETE /documents/:document_id/people/:id(.:format) {:action=>"destroy", :controller=>"document_people"}
document_document_facts GET /documents/:document_id/document_facts(.:format) {:action=>"index", :controller=>"document_facts"}
POST /documents/:document_id/document_facts(.:format) {:action=>"create", :controller=>"document_facts"}
new_document_document_fact GET /documents/:document_id/document_facts/new(.:format) {:action=>"new", :controller=>"document_facts"}
edit_document_document_fact GET /documents/:document_id/document_facts/:id/edit(.:format) {:action=>"edit", :controller=>"document_facts"}
document_document_fact GET /documents/:document_id/document_facts/:id(.:format) {:action=>"show", :controller=>"document_facts"}
PUT /documents/:document_id/document_facts/:id(.:format) {:action=>"update", :controller=>"document_facts"}
DELETE /documents/:document_id/document_facts/:id(.:format) {:action=>"destroy", :controller=>"document_facts"}
document_facts POST /documents/:document_id/facts(.:format) {:action=>"create", :controller=>"facts"}
new_document_facts GET /documents/:document_id/facts/new(.:format) {:action=>"new", :controller=>"facts"}
edit_document_facts GET /documents/:document_id/facts/edit(.:format) {:action=>"edit", :controller=>"facts"}
GET /documents/:document_id/facts(.:format) {:action=>"show", :controller=>"facts"}
PUT /documents/:document_id/facts(.:format) {:action=>"update", :controller=>"facts"}
DELETE /documents/:document_id/facts(.:format) {:action=>"destroy", :controller=>"facts"}
document_document_photos GET /documents/:document_id/document_photos(.:format) {:action=>"index", :controller=>"document_photos"}
POST /documents/:document_id/document_photos(.:format) {:action=>"create", :controller=>"document_photos"}
new_document_document_photo GET /documents/:document_id/document_photos/new(.:format) {:action=>"new", :controller=>"document_photos"}
edit_document_document_photo GET /documents/:document_id/document_photos/:id/edit(.:format) {:action=>"edit", :controller=>"document_photos"}
document_document_photo GET /documents/:document_id/document_photos/:id(.:format) {:action=>"show", :controller=>"document_photos"}
PUT /documents/:document_id/document_photos/:id(.:format) {:action=>"update", :controller=>"document_photos"}
DELETE /documents/:document_id/document_photos/:id(.:format) {:action=>"destroy", :controller=>"document_photos"}
documents GET /documents(.:format) {:action=>"index", :controller=>"documents"}
POST /documents(.:format) {:action=>"create", :controller=>"documents"}
new_document GET /documents/new(.:format) {:action=>"new", :controller=>"documents"}
edit_document GET /documents/:id/edit(.:format) {:action=>"edit", :controller=>"documents"}
document GET /documents/:id(.:format) {:action=>"show", :controller=>"documents"}
PUT /documents/:id(.:format) {:action=>"update", :controller=>"documents"}
DELETE /documents/:id(.:format) {:action=>"destroy", :controller=>"documents"}
paypal_cancel /payments/cancel(.:format) {:controller=>"payments", :action=>"paypal_cancel"}
paypal_return /payments/success(.:format) {:controller=>"payments", :action=>"paypal_return"}
paypal_ipn /payments/ipn(.:format) {:controller=>"payments", :action=>"create"}
document_search_home_index GET /home/document_search(.:format) {:action=>"document_search", :controller=>"home"}
simple_location_search_home_index GET /home/simple_location_search(.:format) {:action=>"simple_location_search", :controller=>"home"}
date_search_home_index GET /home/date_search(.:format) {:action=>"date_search", :controller=>"home"}
simple_people_search_home_index GET /home/simple_people_search(.:format) {:action=>"simple_people_search", :controller=>"home"}
simple_organisation_search_home_index GET /home/simple_organisation_search(.:format) {:action=>"simple_organisation_search", :controller=>"home"}
document_filter_home_index GET /home/document_filter(.:format) {:action=>"document_filter", :controller=>"home"}
search_results_home_index GET /home/search_results(.:format) {:action=>"search_results", :controller=>"home"}
POST /home/search_results(.:format) {:action=>"search_results", :controller=>"home"}
home_index GET /home(.:format) {:action=>"index", :controller=>"home"}
POST /home(.:format) {:action=>"create", :controller=>"home"}
new_home GET /home/new(.:format) {:action=>"new", :controller=>"home"}
edit_home GET /home/:id/edit(.:format) {:action=>"edit", :controller=>"home"}
home GET /home/:id(.:format) {:action=>"show", :controller=>"home"}
PUT /home/:id(.:format) {:action=>"update", :controller=>"home"}
DELETE /home/:id(.:format) {:action=>"destroy", :controller=>"home"}
publicationinfo /documents/publicationinfo/:id(.:format) {:controller=>"documents", :action=>"publicationinfo"}
publishinginfo /documents/update/publishinginfo/:id(.:format) {:controller=>"documents", :action=>"publishinginfo"}
remove_image /documents/document_image_remove/:id(.:format) {:controller=>"documents", :action=>"remove_image"}
make_primary_image /documents/make_primary_image/:id(.:format) {:controller=>"documents", :action=>"make_primary_image"}
person_detail /person_detail/:id(.:format) {:controller=>"documents", :action=>"person_detail"}
about /about(.:format) {:action=>"about", :controller=>"pages"}
contact /contact(.:format) {:action=>"contact", :controller=>"pages"}
privacy /privacy(.:format) {:action=>"privacy", :controller=>"pages"}
terms /terms(.:format) {:action=>"terms", :controller=>"pages"}
help /help(.:format) {:action=>"help", :controller=>"pages"}
def new
#location = #document.locations.build
end
Use build not new. And make your prepare_document private in your controller.
Also
<%= form_for([#document, #location]) do |f| %>
Your url parameter was wrong, you shouldn't post forms to the new action, you should post them to the update action like I showed above.
I'm confused by my rake routes output. For an example (trimmed):
profil GET /profil/:id(.:format) {:action=>"show", :controller=>"profil"}
PUT /profil/:id(.:format) {:action=>"update", :controller=>"profil"}
login GET /login(.:format) {:action=>"new", :controller=>"sessions"}
POST /login(.:format) {:action=>"create", :controller=>"sessions"}
logout GET /logout(.:format) {:action=>"destroy", :controller=>"sessions"}
I've always thought:
Line 2: Route can be accessed using profil_path with PUT method.
Line 4: Route can be accessed using login_path with POST method.
Conclusion: Lines with the first column empty (line 2 and 4) would follow the one above it.
However, I've been experimenting with adding parameter to the url. So, I added these codes in my routes.rb:
namespace :admin do
resources :pengguna_bulk, :only => [:new, :create]
resources :pengguna do
collection do
get 'index/:page', :action => :index
end
end
end
New rake routes output (trimmed):
admin_pengguna_bulk_index POST /admin/pengguna_bulk(.:format) {:action=>"create", :controller=>"admin/pengguna_bulk"}
new_admin_pengguna_bulk GET /admin/pengguna_bulk/new(.:format) {:action=>"new", :controller=>"admin/pengguna_bulk"}
GET /admin/pengguna/index/:page(.:format) {:action=>"index", :controller=>"admin/pengguna"}
admin_pengguna_index GET /admin/pengguna(.:format) {:action=>"index", :controller=>"admin/pengguna"}
POST /admin/pengguna(.:format) {:action=>"create", :controller=>"admin/pengguna"}
new_admin_pengguna GET /admin/pengguna/new(.:format) {:action=>"new", :controller=>"admin/pengguna"}
edit_admin_pengguna GET /admin/pengguna/:id/edit(.:format) {:action=>"edit", :controller=>"admin/pengguna"}
admin_pengguna GET /admin/pengguna/:id(.:format) {:action=>"show", :controller=>"admin/pengguna"}
PUT /admin/pengguna/:id(.:format) {:action=>"update", :controller=>"admin/pengguna"}
DELETE /admin/pengguna/:id(.:format) {:action=>"destroy", :controller=>"admin/pengguna"}
My question is, why is the 3rd route looks like it's under the 2nd route? Is it empty because Rails do not know what to name it and I'd have to use get 'index/:page', :action => :index, :as => :page to name it?
So, this means, route with an empty first column doesn't always follow the above path?
I've always thought:
Line 2: Route can be accessed using profil_path with PUT method.
Line 4: Route can be accessed using login_path with POST method.
Conclusion: Lines with the first column empty (line 2 and 4) would
follow the one above it.
Everything's correct except the conclusion. profil_path expands to /profil/:id(.:format). If it is called with method GET it responds to your first route, if its called with method PUT it responds to your second route.
But same doesn't hold true for second set of routes. You don't have any named helper for /admin/pengguna/index/:page(.:format). If you want a named helper, you should define the route like:
get 'index/:page', :action => :index, :as => :what_ever_named_helper_you_want
I have a group model which has_many article model. And I want to use the following url pattern "{group_id}/{article_id}".
so I wrote these route codes:
resource :groups do
resource :articles
end
match ':group_id/:id(.:format)', :to => 'articles#show', :as => :article
match ':id', :to => 'groups#show', :as => :group
But rails fail to generate correct url for group records and article records. How can I replace the automatic generated article_path and group_path to match my routes?
You're running into problems because you aren't watching out for pluralization. When you define a singular resource route, Rails doesn't treat it as a collection where you would refer to each member with an id. You instead want a plural resources for both groups and articles:
resources :groups do
resources :articles
end
Generates the following routes:
group_articles GET /groups/:group_id/articles(.:format) {:action=>"index", :controller=>"articles"}
POST /groups/:group_id/articles(.:format) {:action=>"create", :controller=>"articles"}
new_group_article GET /groups/:group_id/articles/new(.:format) {:action=>"new", :controller=>"articles"}
edit_group_article GET /groups/:group_id/articles/:id/edit(.:format) {:action=>"edit", :controller=>"articles"}
group_article GET /groups/:group_id/articles/:id(.:format) {:action=>"show", :controller=>"articles"}
PUT /groups/:group_id/articles/:id(.:format) {:action=>"update", :controller=>"articles"}
DELETE /groups/:group_id/articles/:id(.:format) {:action=>"destroy", :controller=>"articles"}
groups GET /groups(.:format) {:action=>"index", :controller=>"groups"}
POST /groups(.:format) {:action=>"create", :controller=>"groups"}
new_group GET /groups/new(.:format) {:action=>"new", :controller=>"groups"}
edit_group GET /groups/:id/edit(.:format) {:action=>"edit", :controller=>"groups"}
group GET /groups/:id(.:format) {:action=>"show", :controller=>"groups"}
PUT /groups/:id(.:format) {:action=>"update", :controller=>"groups"}
DELETE /groups/:id(.:format) {:action=>"destroy", :controller=>"groups"}
If you want to leave off the groups and articles segments you can pass :path => '' to each of the resources definitions, but you are going to have to tread carefully because any request to http://example.com/1/2 will map to an article under groups and be uninformative to end users and bots alike.
I have the following in my routing config:
resources :users do
resources :apps, :controller => :user_apps
end
rake routes includes the following:
user_apps GET /users/:user_id/apps(.:format) {:action=>"index", :controller=>"user_apps"}
user_apps POST /users/:user_id/apps(.:format) {:action=>"create", :controller=>"user_apps"}
new_user_app GET /users/:user_id/apps/new(.:format) {:action=>"new", :controller=>"user_apps"}
edit_user_app GET /users/:user_id/apps/:id/edit(.:format) {:action=>"edit", :controller=>"user_apps"}
user_app GET /users/:user_id/apps/:id(.:format) {:action=>"show", :controller=>"user_apps"}
user_app PUT /users/:user_id/apps/:id(.:format) {:action=>"update", :controller=>"user_apps"}
user_app DELETE /users/:user_id/apps/:id(.:format) {:action=>"destroy", :controller=>"user_apps"}
However, when I try to access eg user_apps_path(1,2) I get /users/1/apps.2 rather than /users/1/apps/2.
Where am I going wrong?
I'm using rails 3.
The correct route is user_app_path(1,2) The pluralized version goes to the index action, making the second argument the format / extension of the request.
Is it possible to have a variable namespace? I have restful resources like the following:
resources :articles
resources :persons
But I need to scope these inside a variable namespace, such that it responds to URLs of the form:
':edition/:controller/:action/:id'
for example:
/foobar/article/edit/123 or /bazbam/person/edit/345
for each of the resources. Is this possible with the resources method, or must I hand-craft these? I will not know the possible values for :edition ahead of time; these get looked up in a before_filter in my ApplicationController.
Is this all I need to do?
scope ':edition' do
resources :articles
resources :matches
resources :teams
end
UPDATE: When using the scope directive above, I get routes like I want:
articles GET /:edition/articles(.:format) {:action=>"index", :controller=>"articles"}
POST /:edition/articles(.:format) {:action=>"create", :controller=>"articles"}
new_article GET /:edition/articles/new(.:format) {:action=>"new", :controller=>"articles"}
edit_article GET /:edition/articles/:id/edit(.:format) {:action=>"edit", :controller=>"articles"}
article GET /:edition/articles/:id(.:format) {:action=>"show", :controller=>"articles"}
PUT /:edition/articles/:id(.:format) {:action=>"update", :controller=>"articles"}
DELETE /:edition/articles/:id(.:format) {:action=>"destroy", :controller=>"articles"}
matches GET /:edition/matches(.:format) {:action=>"index", :controller=>"matches"}
POST /:edition/matches(.:format) {:action=>"create", :controller=>"matches"}
new_match GET /:edition/matches/new(.:format) {:action=>"new", :controller=>"matches"}
edit_match GET /:edition/matches/:id/edit(.:format) {:action=>"edit", :controller=>"matches"}
match GET /:edition/matches/:id(.:format) {:action=>"show", :controller=>"matches"}
PUT /:edition/matches/:id(.:format) {:action=>"update", :controller=>"matches"}
DELETE /:edition/matches/:id(.:format) {:action=>"destroy", :controller=>"matches"}
teams GET /:edition/teams(.:format) {:action=>"index", :controller=>"teams"}
POST /:edition/teams(.:format) {:action=>"create", :controller=>"teams"}
new_team GET /:edition/teams/new(.:format) {:action=>"new", :controller=>"teams"}
edit_team GET /:edition/teams/:id/edit(.:format) {:action=>"edit", :controller=>"teams"}
team GET /:edition/teams/:id(.:format) {:action=>"show", :controller=>"teams"}
PUT /:edition/teams/:id(.:format) {:action=>"update", :controller=>"teams"}
DELETE /:edition/teams/:id(.:format) {:action=>"destroy", :controller=>"teams"}
I'm now able to reference :edition in my ApplicationController:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :authenticate_user!
before_filter :get_edition
def get_edition
#edition = Edition.first(:conditions => { :FriendlyName => params[:edition] } )
end
end
Now I just want to make sure this is the best way to accomplish this.
Actually, you can just do the following :
my_var = "persons"
resources my_var.to_sym
The to_sym method on a string changes it to a symbol
If you don't know the possible values for edition - then you can't use namespaces which seem like they might have solved this issue.
That said, I'd just handcraft them - your case here seems like the ideal case foregoing resources and going straight to a handcrafted path.