Ruby Rails - NoMethodError , drop-down list - ruby-on-rails

I am trying to follow this tutorial. It has written in previous version of Rails and I am using Rails 4. There was a drop-down list the selection does not appear, I am getting following error:
NoMethodError in Book#show
Showing C:/Ruby193/mylibrary/app/views/book/show.html where line #3 raised:
undefined method `name' for nil:NilClass
Extracted source (around line #3):
1 <h1><%= #book.title %></h1>
2 <p><strong>Price: </strong> $<%= #book.price %><br />
3 <strong>Subject: </strong> <%= #subject.name %><br />
4 </p>
5 <p><%= #book.description %></p>
6 <hr />
Rails.root: C:/Ruby193/mylibrary
Here is the show.html
<h1><%= #book.title %></h1>
<p><strong>Price: </strong> $<%= #book.price %><br />
<strong>Subject: </strong> <%= #book.subject.name %><br />
</p>
<p><%= #book.description %></p>
<hr />
<%= link_to 'Back', {:action => 'list'} %>
Here is migrate/258412_subjects.rb
class Subjects < ActiveRecord::Migration
def self.up
create_table :subjects do |t|
t.column :name, :string
end
Subject.create :name => "Physics"
Subject.create :name => "Mathematics"
Subject.create :name => "Chemistry"
Subject.create :name => "Psychology"
Subject.create :name => "Geography"
end
def self.down
drop_table :subjects
end
end
Here is migrate/05465_books.rb
class Books < ActiveRecord::Migration
def self.up
create_table :books do |t|
t.column :title, :string, :limit => 32, :null => false
t.column :price, :float
t.column :subject_id, :integer
t.column :description, :text
end
end
def self.down
drop_table :books
end
end
Here is models/book.rb
class Book < ActiveRecord::Base
belongs_to :subject
validates_presence_of :title
validates_numericality_of :price, :message=>"Error Message"
end
Here is my controller class book_controller.rb
class BookController < ApplicationController
def list
#books = Book.all
end
def show
#book = Book.find(params[:id])
end
def new
#book = Book.new
#subjects = Subject.all
end
def create
#book = Book.new(book_params)
if #book.save!
redirect_to :action => 'list'
else
#subjects = Subject.all
render :action => 'new'
end
end
def edit
#book = Book.find(params[:id])
#subjects = Subject.all
end
def update
#book = Book.find(params[:id])
if #book.update_attributes(book_params)
redirect_to :action => 'show', :id => #book
else
#subjects = Subject.all
render :action => 'edit'
end
end
def delete
Book.find(params[:id]).destroy
redirect_to :action => 'list'
end
private
def book_params
params.permit(:title, :price, :description)
end
end
What should I do?, Thank you in advance

You should write
#book.subject.name
Of course then you should test if the subject is set. One way to do that is to write
#book.subject.try(:name)
This will will try name if subject is not nil.
And a general remark: do not seed the database from within a migration. Normally you will migrate only once, but when you go to production or on test, you will run rake db:setup which will create the database schema without running the migrations.
Instead use db/seed.rb to seed the database. This is then a repeatable process, which will also be needed when running the tests (which start from a clean/empty database).

According to your error
undefined method `name' for nil:NilClass
Extracted source (around line #3):
1 <h1><%= #book.title %></h1>
2 <p><strong>Price: </strong> $<%= #book.price %><br />
3 <strong>Subject: </strong> <%= #subject.name %><br />
#subject is nil in your show.
Since your controller has
def show
#book = Book.find(params[:id])
end
for your show, it seems likely that this is the case.
The code you have posted for your show, however, has the line as
<strong>Subject: </strong> <%= #book.subject.name %><br />
I would advise you to try restarting your server, as it seems to have cached an older version of your view.

Related

Getting error uninitialized constant when attempting to save form to database (Ruby on Rails)

I am getting an error "uninitialized constant Admins::ProductsController::Product", I have been trying to figure out what I'm doing wrong all day and not getting anywhere.
I am trying to submit data to a database through a form. I also have the controller and models in a subdirectory "admins".
controllers/admins/products_controller.rb
class Admins::ProductsController < ApplicationController
def new
end
def show
#product = Product.find(params[:id])
end
def create
#product = Product.new(product_params)
#product.save
redirect_to #product
end
private def product_parms
params.require(:product).permit(:title, :content)
end
end
views/admins/products/new.html.erb
<%= render 'layouts/adminsidebar' %>
<%= form_for :product, url: admins_products_path do |f| %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :content%>
<%= f.text_area :content%>
<%= f.submit %>
<% end %>
routes.rb
namespace :admins do
get 'new' => 'products#new'
resources :products
end
devise_for :users
root 'pages#index'
get 'about' => 'pages#about'
get 'productsdis' => 'products#productsdis'
get 'adminpanel' => 'admins#adminpanel'
get 'admin' => 'admins#admin'
end
Migration file
def change
create_table :admin_products do |t|
t.string :title
t.text :content
t.timestamps
end
end
end
models/admin/product.rb
class Admin::Product < ApplicationRecord
end
Your calling Product.new in your Admin::ProductsController where it should be Admin::Product.new

No route matches {:action=>"show", :controller=>"items"} missing required keys: [:id]

Update: I've been trying to debug my files, so most of the files have changed recently
I am getting a strange error when trying to use a "new" action to my items_controller. Essentially, a wishlist has_many items and an item belongs_to wishlist. The error message is as follows:
Code
Here is my items_controller:
class ItemsController < ApplicationController
def show
#item = Item.find(params[:id])
end
def new
#item = Item.new
end
def create
#item = Item.new(item_params)
if #item.save
redirect_to "/wishlist", :notice => "Success!"
else
redirect_to "/wishlist", :notice => "Failure, try again later!"
end
end
def edit
#item = Item.find(params[:id])
end
def update
#item = Item.find(params[:id])
if #item.update_attributes(item_params)
redirect_to(:action => 'show', :id => #item.id)
else
render 'edit'
end
end
private
def item_params
params.require(:item).permit(:name,:size,:qty,:priority)
end
private
def create_params
params.require(:item).permit(:name,:size,:qty,:priority,:wishlist_id)
end
end
And my routes.rb:
Rails.application.routes.draw do
get '/wishlist' => 'wishlists#index', as: :wishlists
get '/wishlist/:id' => 'wishlists#show', as: :wishlist
get '/wishlist_items/new' => 'items#new'
get '/wishlist_items/:id' => 'items#show', as: :items
get '/wishlist_items/:id/edit' => 'items#edit', as: :edit_items
patch '/wishlist_items/:id' => 'items#update'
resources :items
And finally, my new.html.erb for the items model:
<h1>New Item</h1>
<div class="wishlist-new">
<% if true %>
<%= form_for(#item) do |f| %>
<%= f.text_field :name, :placeholder => "Name" %>
<%= f.text_field :size, :placeholder => "Specifications" %>
<%= f.text_field :qty, :placeholder => "Quantity" %>
<%= f.text_field :priority, :placeholder => "Priority" %>
<%= f.text_field :id, :placeholder => "Wishlist ID" %> # changing :id to :wishlist_id doesn't seem to do anything
<%= f.submit "Create Item", class: "btn-submit" %>
<% end %>
<% end %>
</div>
My migration files (so you know how my databases are structured:
# Migration file for items
class CreateItems < ActiveRecord::Migration
def change
drop_table :items
create_table :items do |t|
t.string :name
t.string :size
t.string :qty
t.integer :priority
t.references :wishlist
t.timestamps
end
end
end
# Migration File for Wishlists
class CreateWishlists < ActiveRecord::Migration
def change
drop_table :wishlists
create_table :wishlists do |t|
t.string :title
t.timestamps
end
end
end
Attempts to Debug
It seems like the routes.rb is sending requests to different methods in the items_controller because the error seems to say that /wishlist_items/new is accessing a show method even though its new method takes priority. To support this, the page loads properly when I comment out get '/wishlist_items/:id' => 'items#show', as: :items in the routes file. What happens is the page loads properly, and the Item is created properly (when I fill out the form) except that when I go into the console, it says that the Item created has a property of wishlist_id: nil even though I specified for it to be 1 in the form.
The method mentioned above has two problems: (1) it doesn't work entirely correctly, and (2) it becomes impossible to show a specific Item in the wishlist.
The error occurs before the inner section of the form_for is loaded, so the problem either is (a) a weird routing thing (as mentioned above) or (b) something weird happening to the #item variable.
Thanks in advance!

Ruby on Rails Difficult Form/Associations

I have a Rails 4.2 app which has 'Rooms', 'Bookings' and 'Extras'.
When making a booking it is for a room e.g. website.com/rooms/1/bookings/1
I have extras which I want to be associated with the booking for that room via check-boxes.
How can this be implemented? I've been reading about has_many :foo, :through => :bar associations but I'm not sure if that's the way to go.
The relevant code looks like this:
<!-- app\views\bookings\_form.html.erb -->
<%= form_for([#room, #booking]) do |f| %>
<p>
<%= f.label 'Select Customer:' %>
<%= f.collection_select :user_id, User.all, :id, :customer_name %>
</p>
<p>
<%= f.label 'start_time', 'Start Date and Time:' %>
<%= f.datetime_select :start_time, { minute_step: 15 } %>
</p>
<p>
<%= f.label 'length', 'Length of booking in hours:' %>
<%= f.number_field 'length', min: 1 %>
</p>
<p>
<%= f.label 'Room Price:' %>
<%= number_to_currency #room.price, unit: "£" %>
</p>
<p>
<%= f.label 'Extras:' %>
<%= f.collection_check_boxes :extra_ids, Extra.all, :id, :extra_info %>
</p>
<%= f.submit 'Submit' %>
<% end %>
# app\models\booking.rb
class Booking < ActiveRecord::Base
belongs_to :room
belongs_to :user
has_many :additions
has_many :extras, :through => :additions
end
# app\models\extra.rb
class Extra < ActiveRecord::Base
belongs_to :extracat
has_many :additions
has_many :bookings, :through => :additions
def extra_info
"#{name}"
end
end
# This model is for the has_many through testing I tried
# app\models\addition.rb
class Addition < ActiveRecord::Base
belongs_to :booking
belongs_to :extra
end
# Relevant section of schema
create_table "additions", force: :cascade do |t|
t.integer "booking_id"
t.integer "extra_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "bookings", force: :cascade do |t|
t.datetime "start_time"
t.datetime "end_time"
t.integer "length"
t.integer "room_id"
t.integer "user_id"
t.integer "extra_id"
end
EDIT - The section within the bookings show page.
# app\views\bookings\show.html.erb
<% #booking.extras.each do |e| %>
<%= e.name %>,
<% end %>
EDIT - Adding bookings controller
class BookingsController < ApplicationController
respond_to :html, :xml, :json
before_action :find_room
def index
#bookings = Booking.where("room_id = ? AND end_time >= ?", #room.id, Time.now).order(:start_time)
respond_with #bookings
end
def new
#booking = Booking.new(room_id: #room.id)
end
def create
#booking = Booking.new(params[:booking].permit(:room_id, :start_time, :length))
#booking.room = #room
if #booking.save
redirect_to room_bookings_path(#room, method: :get)
else
render 'new'
end
end
def show
#booking = Booking.find(params[:id])
end
def destroy
#booking = Booking.find(params[:id]).destroy
if #booking.destroy
flash[:notice] = "Booking: #{#booking.start_time.strftime('%e %b %Y %H:%M%p')} to #{#booking.end_time.strftime('%e %b %Y %H:%M%p')} deleted"
redirect_to room_bookings_path(#room)
else
render 'index'
end
end
def edit
#booking = Booking.find(params[:id])
end
def update
#booking = Booking.find(params[:id])
# #booking.room = #room
if #booking.update(params[:booking].permit(:room_id, :start_time, :length))
flash[:notice] = 'Your booking was updated succesfully'
if request.xhr?
render json: {status: :success}.to_json
else
redirect_to resource_bookings_path(#room)
end
else
render 'edit'
end
end
private
def save booking
if #booking.save
flash[:notice] = 'booking added'
redirect_to room_booking_path(#room, #booking)
else
render 'new'
end
end
def find_room
if params[:room_id]
#room = Room.find_by_id(params[:room_id])
end
end
def booking_params
params.require(:booking).permit(:user_id, :extra_id)
end
end
How is it possible to associate the extras with a booking? As so far they are not being saved with the booking into the database. Is this a controller issue?
You're not permitting the parameters correctly - the name is extra_ids. In addition since the parameter is an array you need to permit it like so:
params.require(:booking).permit(:room_id, :start_time, :length, :extra_ids => [])
Personally I recommend setting action controller to raise an error when unpermitted parameters are encountered in development or tests - very easy otherwise to miss the log messages

NoMethodError in Eurekamoments#new | undefined method `field' for #<Eurekamoment:0x52c6f80>

I am getting the following error:
NoMethodError in Eurekamoments#new
undefined method `field' for #<Eurekamoment:0x52c6f80>
on this line:
<%= f.text_field :field %>
from this view
This is new view
<%= form_for(#eurekamoment) do |f| %>
<% if #eurekamoment.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#eurekamoment.errors.count, "error") %> prohibited this link from being saved:</h2>
<ul>
<% #eurekamoment.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :eurekamoment %><br />
<%= f.text_field :eurekamoment %>
</div>
<div class="field">
<%= f.label :field %><br />
<%= f.text_field :field %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
This is my controller:
class EurekamomentsController < ApplicationController
def index
#eurekamoment = Eurekamoment.paginate(:page => params[:page], :per_page => 3 )
params[:per_page] ||= 25
params[:page] ||= 1
end
def show
#eurekamoment = Eurekamoment.find(params[:id])
#comment = Comment.new
#vote = Vote.new
end
def new
#eurekamoment = Eurekamoment.new
end
def create
#eurekamoment = Eurekamoment.new(link_params)
if #eurekamoment.save
redirect_to #eurekamoment
else
render action: 'new'
end
end
def destroy
#eurekamoment = Eurekamoment.find(params[:id])
if #eurekamoment.destroy
redirect_to action: 'index'
else
render action: 'show'
end
end
def edit
#eurekamoment = Eurekamoment.find(params[:id])
end
def update
#eurekamoment = Eurekamoment.find params[:id]
if #eurekamoment.update(eurekamoment_params)
redirect_to #eurekamoment
else
render action: 'edit'
end
end
private
def eurekamoment_params
params.require(:eurekamoment).permit(:id, :eurekamoment, :field, :description, :plan)
end
end
This is my model:
class Eurekamoment < ActiveRecord::Base
has_many :comments
belongs_to :user
has_many :votes
end
And my db:
class CreateEurekamoments < ActiveRecord::Migration
def change
create_table :eurekamoments do |t|
t.integer :user_id
t.string :field
t.string :eurekamoment
t.string :description
t.string :plan
t.timestamps
end
end
end
I've wasted last hour trying to figure this out at a hackathon and can't fix this issue, do you guys see anything wrong? Thanks.
Your code looks good - the error will almost certainly be a DB issue, as discussed in your comments
I think this will be the real cause of your problem: I've wasted last hour trying to figure this out at a hackathon and can't fix this issue -- it's best to take a step back, deep breath and work through it logically
I'd start with a total db refresh. Drop your tables & then run rake db:migrate again, or you could use rake db:migrate VERSION=0, or rake db:migrate:reset - How can I migrate my database with rails to the first revision without dropping the database first?

Rails 3: upload file to public and save path in database using multi-model form

I stuck with file uploading.
I'm use a complex form. Here is a model:
class Project < ActiveRecord::Base
has_one :task
accepts_nested_attributes_for :task
end
class Task < ActiveRecord::Base
has_one :project
accepts_nested_attributes_for :project
end
Here is a view:
<%= form_for(#project) do |f| %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<%= f.fields_for :task_attributes do |a| %>
<div class="field">
<%= a.label :name %><br />
<%= a.text_field :name %>
<%= form_tag 'project/upload', :multipart => true do %>
<label for="file">Upload text File</label><%= a.file_field :path %>
<% end %>
</div>
<% end %>
<% end %>
Here is a controller:
class ProjectsController < ApplicationController
def new
#project = Project.new
#project.build_task
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #project }
end
end
def upload
uploaded_io = params[:upload][:path]
File.open(Rails.root.join('public', 'uploads', uploaded_io.original_filename), 'w') do |file|
file.write(uploaded_io.read)
end
end
def create
#project = Project.new(params[:project])
respond_to do |format|
if #project.save
format.html { redirect_to(#project, :notice => 'Project was successfully created.') }
format.xml { render :xml => #project, :status => :created, :location => #project }
else
format.html { render :action => "new" }
format.xml { render :xml => #project.errors, :status => :unprocessable_entity }
end
end
end
end
Migration:
class CreateProjects < ActiveRecord::Migration
def self.up
create_table :projects do |t|
t.integer :id
t.string :name
t.timestamps
end
end
def self.down
drop_table :projects
end
end
class CreateTasks < ActiveRecord::Migration
def self.up
create_table :tasks do |t|
t.string :name
t.integer :project_id
t.string :path
t.timestamps
end
end
def self.down
drop_table :tasks
end
end
When I create new project with task via form, I have both records in database. I see task file path in database. But I can't see file in my public directory. I can not figure out how to save the file to public directory.
Please, help me to solve this problem!
You cannot have a nested form in html. You have nested forms. On submitting a form, it will send the data to server as one request and one action within a controller will be called. In your case on submit of the form the create action will be called and not the upload action wont be called.
Get rid of the inner form and add multipart to the outer form. It would suffice for your case.
It is an overhead to handle file uploads manually. I would suggest you to use paperclip or carrierwave. Anyways that is not your problem here.

Resources