Rails Tutorial: undefined method for nil:NilClass - ruby-on-rails

I successfully finished the Rails Tutorial by Michael Hartl where you are building an app like twitter. Now I want to add some other things and got stuck trying to add a like-function to the microposts.
As a basis for this I took the relationships between followers.
So a user has_many (microposts) 'liked' and a micropost has_many 'likers' (users).
I keep getting the error
ActionView::Template::Error: undefined method 'likers' for nil:NilClass
in the Microposts Interface Test and in the Users Profile Test.
Here's my code:
routes.rb:
Rails.application.routes.draw do
root 'static_pages#home'
get 'help' => 'static_pages#help'
get 'about' => 'static_pages#about'
get 'contact' => 'static_pages#contact'
get 'signup' => 'users#new'
get 'login' => 'sessions#new'
post 'login' => 'sessions#create'
delete 'logout' => 'sessions#destroy'
resources :users do
member do
get :following, :followers, :liked
end
end
resources :microposts do
member do
get :likers
end
end
resources :account_activations, only: [:edit]
resources :password_resets, only: [:new, :create, :edit, :update]
resources :microposts, only: [:create, :destroy]
resources :relationships, only: [:create, :destroy]
end
app/models/like.rb:
class Like < ActiveRecord::Base
belongs_to :liker, class_name: "User"
belongs_to :micropost, class_name: "Micropost"
validates :liker_id, presence: true
validates :micropost_id, presence: true
end
app/models/micropost.rb:
class Micropost < ActiveRecord::Base
belongs_to :user
has_many :passive_likes, class_name: "Like",
foreign_key: "micropost_id",
dependent: :destroy
has_many :likers, through: :passive_likes, source: :micropost
default_scope -> { order(created_at: :desc) }
mount_uploader :picture, PictureUploader
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 140 }
validate :picture_size
private
# Validates the size of an uploaded picture.
def picture_size
if picture.size > 5.megabytes
errors.add(:picture, "maximal 5MB")
end
end
end
app/models/user.rb:
class User < ActiveRecord::Base
has_many :microposts, dependent: :destroy
has_many :active_relationships, class_name: "Relationship",
foreign_key: "follower_id",
dependent: :destroy
has_many :passive_relationships, class_name: "Relationship",
foreign_key: "followed_id",
dependent: :destroy
has_many :active_likes, class_name: "Like",
foreign_key: "liker_id",
dependent: :destroy
has_many :liked, through: :active_likes, source: :micropost
has_many :following, through: :active_relationships, source: :followed
has_many :followers, through: :passive_relationships, source: :follower
mount_uploader :avatar, AvatarUploader
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, length: { minimum: 6 }, allow_blank: true
# Returns the hash digest of the given string.
def self.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def self.new_token
SecureRandom.urlsafe_base64
end
# Remembers a user in the database for use in persistent sessions.
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# Returns true if the given token matches the digest.
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
# Forgets a user.
def forget
update_attribute(:remember_digest, nil)
end
# Activates an account.
def activate
update_attribute(:activated, true)
update_attribute(:activated_at, Time.zone.now)
end
# Sends activation email.
def send_activation_email
UserMailer.account_activation(self).deliver_now
end
# Sets the password reset attributes.
def create_reset_digest
self.reset_token = User.new_token
update_attribute(:reset_digest, User.digest(reset_token))
update_attribute(:reset_sent_at, Time.zone.now)
end
# Sends password reset email.
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
# Returns true if a password reset has expired.
def password_reset_expired?
reset_sent_at < 2.hours.ago
end
# Defines a proto-feed.
# See "Following users" for the full implementation.
# Returns a user's status feed.
def feed
following_ids = "SELECT followed_id FROM relationships
WHERE follower_id = :user_id"
Micropost.where("user_id IN (#{following_ids})
OR user_id = :user_id", user_id: id)
end
# Follows a user.
def follow(other_user)
active_relationships.create(followed_id: other_user.id)
end
# Unfollows a user.
def unfollow(other_user)
active_relationships.find_by(followed_id: other_user.id).destroy
end
# Returns true if the current user is following the other user.
def following?(other_user)
following.include?(other_user)
end
# Likes a micropost
def like(any_post)
active_like.create(micropost_id: any_post.id)
end
# Unlikes a micropost
def unlike(any_post)
active_like.find_by(micropost_id: any_post.id).destroy
end
# Returns true if the current user is liking the micropost
def liked?(any_post)
liked.include?(any_post)
end
private
# Converts email to all lower-case.
def downcase_email
self.email = email.downcase
end
# Creates and assigns the activation token and digest.
def create_activation_digest
self.activation_token = User.new_token
self.activation_digest = User.digest(activation_token)
end
end
app/views/users/show.html.erb:
<% provide(:title, #user.name) %>
<div class="row">
<aside class="col-md-4">
<section>
<h1>
<%= gravatar_for #user %>
<%= #user.name %>
</h1>
</section>
<section class="stats">
<%= render 'shared/stats' %>
</section>
</aside>
<div class="col-md-8">
<%= render 'follow_form' if logged_in? %>
<% if #user.microposts.any? %>
<h3>Posts (<%= #user.microposts.count %>)</h3>
<ol class="microposts">
<%= render #microposts %>
</ol>
<%= will_paginate #microposts %>
<% end %>
</div>
</div>
app/views/microposts/_micropost.html.erb:
<li id="micropost-<%= micropost.id %>">
<%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
<span class="user"><%= link_to micropost.user.name, micropost.user %></span>
<span class="content">
<%= micropost.content %>
<%= image_tag micropost.picture.url if micropost.picture? %>
</span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
<%= render 'shared/morestats' %>
<%= render 'microposts/like_form' if logged_in? %>
<% if current_user?(micropost.user) %>
<%= link_to "löschen", micropost, method: :delete,
data: { confirm: "Diesen Post wirklich löschen?" } %>
<% end %>
</span>
</li>
app/views/microposts/_like_form.html.erb:
<% unless current_user?(#user) %>
<div id="like_form">
<% if #micropost.liked?(#user) %>
<%= render 'unlike' %>
<% else %>
<%= render 'like' %>
<% end %>
</div>
<% end %>
app/views/microposts/_like.html.erb:
<%= form_for(current_user.active_likes.build) do |f| %>
<div><%= hidden_field_tag :micropost_id, #user.id %></div>
<%= f.submit "Like", class: "btn btn-primary" %>
<% end %>
app/views/microposts/_unlike.html.erb:
<%= form_for(current_user.active_likes.build) do |f| %>
<div><%= hidden_field_tag :micropost_id, #user.id %></div>
<%= f.submit "Like", class: "btn btn-primary" %>
<% end %>
If there's any code missing let me know. Thanks in advance!
Here's the full error:
ERROR["test_micropost_interface", MicropostsInterfaceTest, 4.72409967]
test_micropost_interface#MicropostsInterfaceTest (4.72s)
ActionView::Template::Error: ActionView::Template::Error: undefined method `likers' for nil:NilClass
app/views/shared/_morestats.html.erb:4:in `_app_views_shared__morestats_html_erb___939926434685355917_93681000'
app/views/microposts/_micropost.html.erb:10:in `_app_views_microposts__micropost_html_erb___1029196025817541101_93766560'
app/views/users/show.html.erb:19:in `_app_views_users_show_html_erb___2389533090581269630_85562520'
test/integration/microposts_interface_test.rb:33:in `block in <class:MicropostsInterfaceTest>'
app/views/shared/_morestats.html.erb:4:in `_app_views_shared__morestats_html_erb___939926434685355917_93681000'
app/views/microposts/_micropost.html.erb:10:in `_app_views_microposts__micropost_html_erb___1029196025817541101_93766560'
app/views/users/show.html.erb:19:in `_app_views_users_show_html_erb___2389533090581269630_85562520'
test/integration/microposts_interface_test.rb:33:in `block in <class:MicropostsInterfaceTest>'
code for test/integration/microposts_interface_test.rb:
require 'test_helper'
class MicropostsInterfaceTest < ActionDispatch::IntegrationTest
def setup
#user = users(:peter)
end
test "micropost interface" do
log_in_as(#user)
get root_path
assert_select 'div.pagination'
# Invalid submission
assert_no_difference 'Micropost.count' do
post microposts_path, micropost: { content: "" }
end
assert_select 'div#error_explanation'
# Valid submission
content = "This micropost really ties the room together"
assert_difference 'Micropost.count', 1 do
post microposts_path, micropost: { content: content }
end
assert_redirected_to root_url
follow_redirect!
assert_match content, response.body
# Delete a post.
assert_select 'a', text: 'löschen'
first_micropost = #user.microposts.paginate(page: 1).first
assert_difference 'Micropost.count', -1 do
delete micropost_path(first_micropost)
end
# Visit a different user.
get user_path(users(:archer))
assert_select 'a', text: 'löschen', count: 0
end
end
code for test/integration/users_profile_test.rb:
require 'test_helper'
class UsersProfileTest < ActionDispatch::IntegrationTest
include ApplicationHelper
def setup
#user = users(:peter)
end
test "profile display" do
get user_path(#user)
assert_template 'users/show'
assert_select 'title', full_title(#user.name)
assert_select 'h1', text: #user.name
assert_select 'h1>img.gravatar'
assert_match #user.microposts.count.to_s, response.body
assert_select 'div.pagination'
#user.microposts.paginate(page: 1).each do |micropost|
assert_match micropost.content, response.body
end
end
end
code for shared/_morestats.html.erb:
<% #micropost %>
<div class="morestats">
<strong id="likers" class="morestat">
<%= #micropost.likers.count %>
</strong>
likers
</div>

The issue is that you wrote #micropost referring to a variable visible to all the views (a #micropost object that you never assigned). If you write micropost without the #, you are referring to a local variable that was assigned with the :locals => { :micropost => micropost }.
In shared/morestats, line 4, you are doing #micropost.likers but if you pay attention, you are not passing any micropost to the partial (see micropost, line 10):
<%= render 'shared/morestats' %>
You have to change that to something like this:
<%= render :partial => 'shared/morestats', :locals => { :micropost => micropost } %>
And also remove the '#' from the micropost in the morestats. Just like this:
<% micropost %>
<div class="morestats">
<strong id="likers" class="morestat">
<%= micropost.likers.count %>
</strong>
likers
</div>
You have the same issue here:
<%= render 'microposts/like_form' %>
You have to change that to something like this:
<%= render :partial => 'microposts/like_form', :locals => { :micropost => micropost } %>
And like_form to this:
<% unless current_user?(#user) %>
<div id="like_form">
<% if #user.liked?(micropost) %>
<%= render 'unlike' %>
<% else %>
<%= render 'like' %>
<% end %>
</div>
<% end %>
Another solution, if you don't want to change the # you can just modify the _micropost partial like this:
<% #micropost = micropost %>
<%= render 'shared/morestats' %>
But this is less elegant than the previous solution.

<%= #book.likes.count %>
<% like = current_user.likes.find_by(book: #book) %>
<%# if like.nil? %>
<%= button_to "like", likes_path, params: {like: {book_id: #book.id }}, method: :post %>
<%# else %>
<%#= button_to "UnLike", like_path(like), method: :delete %>
<% #end %>
-->

Related

Error validations from child model Ruby on Rails

I got a problem which I can't solve. I made two models; one model called Film is the parent model and another model called Review is the child model. I got validation conditions on the child model but it does not display on the view.
Film model
class Film < ApplicationRecord
has_many :reviews
validates_presence_of :filmtitle, presence: true
validates_presence_of :filmdescription, presence: true
validates_presence_of :filmdirector, presence: true
validates_presence_of :filmrating, presence: true
validates_presence_of :filmstarname, presence: true
end
Review model
class Review < ApplicationRecord
validates :rating, presence: true
validates :commenter, presence: true
validates :body, presence: true
belongs_to :film
end
Review Controller
class ReviewsController < ApplicationController
def create
#film = Film.find(params[:film_id])
#review = #film.reviews.create(review_params)
redirect_to film_path(#film)
end
private
def review_params
params.require(:review).permit(:commenter, :body, :rating)
end
end
Film show.html.erb
<% if #film.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#film.errors.count, "error") %></h2>
<ul>
<% #film.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= form_for([#film, Review.new]) do |f| %>
<p>
<%= f.label :commenter %><br>
<%= f.text_field :commenter, :placeholder => 'Your name' %>
</p>
<p>
<%= f.label :body %><br>
<%= f.text_area :body, :placeholder => 'Your comment' %>
</p>
<p>
<%= f.label :rating %><br>
<%= f.select :rating, ['1 Star', '2 Stars', '3 Stars', '4 Stars', '5 Stars'] %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
Film Controller
class FilmsController < ApplicationController
before_action :set_film, only: [:show]
# GET /films
# GET /films.json
def index
#films = Film.all.paginate(page: params[:page], per_page: 30)
#reviews = Review.new
end
# GET /films/1
# GET /films/1.json
def show
end
# GET /films/new
def new
end
# GET /films/1/edit
def edit
end
# POST /films
# POST /films.json
def create
end
# PATCH/PUT /films/1
# PATCH/PUT /films/1.json
def update
end
# DELETE /films/1
# DELETE /films/1.json
def destroy
end
private
# Use callbacks to share common setup or constraints between actions.
def set_film
#film = Film.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def film_params
params.require(:film).permit(:filmtitle, :filmdescription)
end
end
route.rb
Rails.application.routes.draw do
resources :films do
resources :reviews
end
resources :rentals
resources :buys
resources :admin
resources :adminrentals
resources :adminfilms
resources :logins
resources :admins_login
resources :games
get '/adminCool' => 'admins_login#index'
get '/adminlogin' => 'admins_sessions#new'
post '/adminlogin' => 'admins_sessions#create'
get '/adminlogout' => 'admins_sessions#destroy'
get '/adminsignup' => 'admins#new'
post '/admins' => 'admins#create'
get '/login' => 'sessions#new'
post '/login' => 'sessions#create'
get '/logout' => 'sessions#destroy'
get '/signup' => 'users#new'
post '/users' => 'users#create'
get '/cool' => 'logins#index'
end
Please try
def create
#film = Film.find(params[:film_id])
#review = #film.reviews.new(review_params)
if #review.save
redirect_to film_path(#film)
else
render "films/#{#film.id}"
end
end

Rails - annotate PDF in browser

In my (very first) rails app, I have a model called "Annotations"; this will take some generic data as well as an attachment (PDF). Next, I need to the ability to do the actual annotations to this attachment/PDF ("annotate") and store the results on a field on the "Annotations" model (as a JSON?).
Currently, I think I should create a new method "annotate" in the AnnotationsController (needs to update the annotations object) and call a new view called "annotate.html.erb".
Any advice how to go about "the rails way" ?
Update
meanwhile, I have:
model (annotation.rb)
class Annotation < ApplicationRecord
has_many :comments, dependent: :destroy
belongs_to :documenttype
has_attached_file :file, styles: { large: "600x600>", medium: "500x500>", thumb: "150x150#" }, default_url: "/images/:style/missing.png"
accepts_nested_attributes_for :documenttype
validates_attachment_content_type :file, content_type: ['image/jpeg', 'image/png', 'image/gif', 'application/pdf']
validates :name, presence: true, uniqueness: true, length: { minimum: 10, maximum: 50 }
validates :description, length: { minimum: 20, maximum: 500 }
validates :documenttype, presence: true
validates :file, presence: true
end
routes
Rails.application.routes.draw do
root 'dashboard#index'
devise_for :users
resources :users,:documenttypes, :documents
resources :annotations do
resources :comments
end
get "annotate", to: "annotations#annotate"
the controller (AnnotationsController)
class AnnotationsController < ApplicationController
before_action :annotate, only: [:edit, :update ]
def index
#annotations = Annotation.all
end
def show
#annotation = Annotation.find(params[:id])
end
def new
#annotation = Annotation.new
end
def edit
#annotation = Annotation.find(params[:id])
end
def create
#annotation = Annotation.new(annotation_params)
if #annotation.save
redirect_to #annotation
else
render 'new'
end
end
def update
#annotation = Annotation.find(params[:id])
if #annotation.update(annotation_params)
redirect_to #annotation
else
render 'edit'
end
end
def destroy
#annotation = Annotation.find(params[:id])
#annotation.destroy
redirect_to annotations_path
end
private
def annotate
#annotation = Annotation.find(params[:id])
end
def annotation_params
params.require(:annotation).permit(:name, :description, :file, :active, :documenttype_id)
end
end
views that render 1 form (using simple_form)
<div class="container-fluid">
<div class="row">
<h4>Annotation</h4>
<div class="col-md-6">
<%= simple_form_for #annotation, html: { class: 'form-horizontal', multipart: true },
wrapper: :horizontal_form,
wrapper_mappings: {
check_boxes: :horizontal_radio_and_checkboxes,
radio_buttons: :horizontal_radio_and_checkboxes,
file: :horizontal_file_input,
boolean: :horizontal_boolean
} do |f| %>
<%= f.error_notification %>
<% if #annotation.file.blank? %>
<%= f.input :file, as: :file, input_html: { accept: ('application/pdf') } %>
<% else %>
<% end -%>
<%= f.input :name, placeholder: 'Enter name' %>
<%= f.input :description, placeholder: 'Description' %>
<%= f.association :documenttype %>
<%= f.input :active, as: :boolean %>
<%= f.button :submit %>
<% unless #annotation.file.blank? %>
<%= link_to ' Annotate', annotate_path(#annotation), :class => "btn btn-default" %>
<% end -%>
<% end -%>
<p><br><%= link_to 'List' , annotations_path %></p>
</div>
<% unless #annotation.file.blank? %>
<div class="col-md-6">
<p><strong>File name: </strong><%= #annotation.file_file_name %></p>
<iframe src="<%= #annotation.file %>" width=100% height=450px class="img-rounded"></iframe>
</div>
<% end %>
</div>
<% unless #annotation.new_record? %>
<div class="row">
<hr>
<div class="col-md-6">
<%= render #annotation.comments %>
</div>
<div class="col-md-6">
<%= render 'comments/form' %>
</div>
</div>
<% end -%>
</div>
Also, I created a view call annotate.html.erb
It is called now as static page under http://localhost:3000/annotate ; while I think it should be under http://localhost:3000/annotations/annotate/:id -- so looks like a routing issue (for) now. (Routing still is a bit of a mystery to me :-) )
If you want to do it the rails way:
Your model should be singular, so: Annotation.rb:
class Annotation < ApplicationRecord
end
Your controller should be called AnnotationsController and have the standard CRUD methods: new and create (to create a new annotation). edit and update to update an annotation and destroy to destroy your annotations.
SOMETHING LIKE:
class AnnotationsController < ApplicationController
before_action :set_annotation, only: [:edit, :update, :destroy]
def index
#annotations = Annotation.all
end
def new
#annotation = Annotation.new
end
def create
#annotation = Annotation.new(annotation_params)
if #annotation.save
redirect_to annotations_path, notice: "annotations created"
else
render action: 'new'
end
end
def edit
end
def destroy
#annotation.destroy
redirect_to annotations_path
end
def update
if #annotation.update(annotation_params)
redirect_to annotations_path
else
render action: 'edit'
end
end
private
def set_annotation
#annotation = Annotation.find(params[:id])
end
def annotation_params
params.require(:annotation).permit(:name, ...)
end
end
Your views should live in views/annotations/
Since this is your first rails app, I suggest you use the scaffolding option to build your app, which according to the documetation is:
A scaffold in Rails is a full set of model, database migration for
that model, controller to manipulate it, views to view and manipulate
the data, and a test suite for each of the above.
rails g scaffold Annotate
see: http://guides.rubyonrails.org/command_line.html
Found the routing get "annotations/:id/annotate" => "annotations#annotate", as: 'annotate'
And the set_annotation method should not be private.
May be there is a better routing (always welcome), but it works now.

undefined method `loan_amount' for nil:NilClass

I am doing a jewel loan application using ruby 2.2.2p95 (2015-04-13 revision 50295) [i686-linux] and Rails 4.2.1 in that i am having two models:
1)jewelloan.rb
2)jltransaction.rb
I have added foreign key jewelloan_id to get the loan_amount in jltransactions table. Where as loan_amount is a field in jewelloans table.
My problem is foreign key relationship not working. I have searched many stackoverflow questions to solve this but that are not working.
I have attached my model,view,controller and everything. Please tell me where i did a mistake.
jewelloan.rb
class Jewelloan < ActiveRecord::Base
attr_accessor :transaction_amount
attr_accessible :transaction_amount
has_many :jltransactions, :dependent => :destroy
accepts_nested_attributes_for :jltransactions ,:allow_destroy => true
attr_accessible :account_number, :customer_name, :customer_address, :opened_on, :due_date, :amount, :jewel, :no_of_items, :gross_weight, :net_weight, :appraised_amount, :loan_amount, :transaction_mode, :transaction_type, :particulars, :comments, :close_date, :jltransactions_attributes
end
jltransaction.rb
class Jltransaction < ActiveRecord::Base
attr_accessor :loan_amount
attr_accessible :loan_amount
belongs_to :jewelloan
attr_accessible :transaction_date, :transaction_amount, :transaction_mode, :transaction_type, :particulars, :comments, :jewelloan_id
end
jewelloans_controller.rb
class JewelloansController < ApplicationController
def new
#jewelloan = Jewelloan.new
end
def create
#jewelloan = Jewelloan.create(jewelloan_params)
if #jewelloan.save
flash[:success] = "Special JL was successfully created"
redirect_to #jewelloan
else
flash[:danger] = "Special Jewel Loan was not created"
render 'new'
end
end
private
def jewelloan_params
params.require(:jewelloan).permit(:account_number, :customer_name, :customer_address, :amount, :interest_rate, :opened_on, :due_date, :amount_due, :jewel, :no_of_items, :gross_weight, :net_weight, :appraised_amount, :loan_amount, :transaction_mode, :transaction_type, :particulars, :comments, :close_date, :no_of_days, :jltransactions_attributes)
end
end
jltransactions_controller.rb
class JltransactionsController < ApplicationController
def new
#jltransaction = Jltransaction.new
#jewelloan = Jewelloan.new
end
def create
#jltransaction = Jltransaction.create(jltransaction_params)
#jewelloan = #jltransaction.jewelloans(jewelloan_params)
if #jltransaction.save
flash[:success] = "Transaction created"
redirect_to #jltransaction
else
flash[:danger] = "Transaction was not created"
render 'new'
end
end
private
def jltransaction_params
params.require(:jltransaction).permit(:transaction_date, :transaction_amount, :transaction_mode, :transaction_type, :particulars, :comments, :jewelloan_id)
end
end
app/views/jltransactions/_form.html.erb
<%= form_for #jltransaction do |jlt| %>
<%= jlt.hidden_field :jewelloan_id, :value => #jewelloan.id %>
<% if #jltransaction.errors.any? %>
<h2>Transaction Failed</h2>
<ul>
<% #jltransaction.errors.full_messages.each do |error| %>
<li><%= error %></li>
<% end %>
</ul>
<% end %>
<%= jlt.label :loan_amount %>
for<%= jlt.text_field :loan_amount, :disabled => true, :value => #jltransaction.jewelloan.try(:loan_amount) %>
<%= jlt.label :transaction_amount %>
<%= jlt.text_field :transaction_amount %>
<%= jlt.submit "Submit Transaction", class: "btn btn-success" %>
<% end %>
rails console
Completed 500 Internal Server Error in 34ms (ActiveRecord: 0.0ms)
ActionView::Template::Error (undefined method `loan_amount' for nil:NilClass):
Please give some ideas to work the foreign key.
Thanks...
Your relationship might be returning nil result.
Try
#jltransaction.jewelloan.try(:loan_amount)
You may try this:
<%= jlt.text_field :loan_amount, :disabled => true, :value => #jltransaction.jewelloan.try(:loan_amount) if #jltransaction && #jltransaction.jewelloan %>

Rails Link_to is linking to undesired view

My link_to in my view is going to a completely different "show.html.erb" than I'd like it to. I'm basically trying to understand why the "link_to #exhibit is linking to an "Investigation" profile. I think it may have to do with my routes file or the fact that its a "belong to" relationship...but can't seem to get it workin...what should that link_to be?
UPDATE: (AS PER BROIS QUESTION)
The missing misbehaving link_to is in the <%= link_to #exhibit do %> in show.html.erb
MY EXHIBIT.RB (MODEL)
class Exhibit < ActiveRecord::Base
attr_accessible :content, :investigation_id, :name, :user_id, :media, :media_html
belongs_to :investigation
has_many :categorizations
has_many :categories, :through => :categorizations
validates :name, presence: true, length: { maximum: 140 }
validates :content, presence: true
default_scope -> { order('created_at DESC') }
auto_html_for :media do
html_escape
image
youtube(:width => 400, :height => 250)
link :target => "_blank", :rel => "nofollow"
simple_format
end
MY EXHIBIT CONTROLLER:
class ExhibitsController < ApplicationController
include AutoHtml
def new
#exhibit = Exhibit.new
end
def show
#exhibit = Exhibit.find(params[:id])
end
def index
#exhibits = Exhibit.paginate(page: params[:page])
end
def create
#investigation = Investigation.find(params[:investigation_id])
#exhibit = #investigation.exhibits.create(params[:exhibit])
if #exhibit.save
flash[:success] = "You've successfully added etc etc..."
redirect_to investigation_path(#investigation)
else
render 'new'
end
end
end
MY ROUTES.RB
resources :sessions, only: [:new, :create, :destroy]
resources :investigations do
resources :players
end
resources :investigations do
resources :exhibits
end
LASTLY MY SHOW.HTML.ERB (INVESTIGATION PROFILE)
<% #investigation.exhibits.each do |exhibit| %>
<div class="row-fluid services_circles">
<%= link_to #exhibit do %>
<div class="media">
<div class="pull-left">
<%= exhibit.media_html %>
</div>
<div class="media-body">
<h4 class="media-heading"><%= exhibit.name %></h4>
<p><%= exhibit.content %></p>
</div>
</div>
<% end %>
<% end %>
ADDED THE INVESTIGATIONS CONTROLLER
class InvestigationsController < ApplicationController
def new
#investigation = Investigation.new
end
def show
#investigation = Investigation.find(params[:id])
end
def index
#investigations = Investigation.paginate(page: params[:page])
end
def create
#investigation = Investigation.new(params[:investigation])
if #investigation.save
flash[:success] = "You've successfully created..."
redirect_to #investigation
else
render 'new'
end
end
end
ADDED THE INVESTIGATION MODEL
class Investigation < ActiveRecord::Base
# belongs_to :user
has_many :players, dependent: :destroy
has_many :exhibits, dependent: :destroy
default_scope -> { order('created_at DESC') }
end
I appreciate the help...if i need to post any more info just let me know
IN YOUR : app/contorollers/exhibits_controller.rb
def show
#investigation = Investigation.find(params[:investigation_id])
#exhibit = Exhibit.find(params[:id])
end
IN YOUR : app/views/exhibits/show.html.erb
<%= link_to investigation_exhibit_path(#investigation, #exhibit) do %>
Maybe, I think.

Ruby on Rails Tutorial by M. Hartl. Chapter 10.2.1 - undefined local variable or method `micropost'

I keep getting the following error in chapter 10.2.1 - undefined local variable or method `micropost'. I'm trying to get all the posts to render on the show page.
Here is my users controller:
class UsersController < ApplicationController
before_filter :signed_in_user, only: [:index, :edit, :update, :show, :destroy]
before_filter :correct_user, only: [:edit, :update]
before_filter :admin_user, [:destroy]
def index
##users = User.all
#users = User.paginate(page: params[:page])
end
def new
#user = User.new
end
def destroy
#user = User.find(params[:id]).destroy
flash[:success] = "User has been deleted!"
redirect_to users_path
end
def show
#user = User.find(params[:id])
#microposts = #user.microposts.paginate(page: params[:page])
end
def edit
#user = User.find(params[:id])
end
def update
#user = User.find(params[:id])
if #user.update_attributes(params[:user])
#good!
sign_in #user
flash[:success] = "Profile"
redirect_to user_path(#user)
else
render 'edit'
end
end
def create
#user = User.new(params[:user])
if #user.save
sign_in #user
flash[:success] = "Welcome to the Ruby app!"
redirect_to user_path(#user)
else
render 'new'
end
end
private
def signed_in_user
if !signed_in?
store_location
flash[:error] = "Please sign in!"
redirect_to signin_url
end
end
def correct_user
#user = User.find(params[:id])
if !current_user?(#user)
redirect_to '/'
end
end
def admin_user
if current_user.admin?
#nothing
else
redirect_to '/'
end
end
end
Here is my view
<% provide(:title, #user.name) %>
<div class="row">
<aside class="span4">
<section>
<h1>
<%= gravatar_for #user %>
<%= #user.name %>
</h1>
</section>
</aside>
<div class="span8">
<% if #user.microposts.any? %>
<h3>Microposts (<%= #user.microposts.count %>)</h3>
<ol class="microposts">
<li>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
</li>
</ol>
<%= will_paginate #microposts %>
<% end %>
</div>
</div>
Here is the micropost model:
# == Schema Information
#
# Table name: microposts
#
# id :integer not null, primary key
# content :string(255)
# user_id :integer
# created_at :datetime not null
# updated_at :datetime not null
#
class Micropost < ActiveRecord::Base
attr_accessible :content
belongs_to :user
validates :content, presence: true, length: { maximum: 140 }
validates :user_id, presence: true
default_scope order: 'microposts.created_at DESC'
end
Lastly, here is the users model:
# == Schema Information
#
# Table name: users
#
# id :integer not null, primary key
# name :string(255)
# email :string(255)
# created_at :datetime not null
# updated_at :datetime not null
# password_digest :string(255)
# remember_token :string(255)
# admin :boolean default(FALSE)
#
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_secure_password
has_many :microposts, dependent: :destroy
before_save { |user| user.email = email.downcase }
before_save :create_remember_token
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true,
format: { with: VALID_EMAIL_REGEX },
uniqueness: { case_sensitive: false }
validates :password, presence: true, length: { minimum: 6 }
validates :password_confirmation, presence: true
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
Please let me know if you'd like me to post anymore code. Thanks!
In your view you are not looping the #user.microposts, try something like:
<% if #user.microposts.any? %>
<h3>Microposts (<%= #user.microposts.count %>)</h3>
<ol class="microposts">
<% #user.microposts.each do |micropost| %>
<li>
<span class="content"><%= micropost.content %></span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
</span>
</li>
<% end %>
</ol>
<%= will_paginate #microposts %>
<% end %>
In your view I'm seeing that sometimes you are refering to micropost as:
micropost.content
and in other ones as a class attribute:
#micropost
You should check on that.
It looks like your view is missing a line that loops through the users microposts to display them. Add the line:
<% #microposts.each do |micropost| %>
Now the local variable micropost in your line <%= micropost.content %> will exist.
if you want to use the local variable (micropost) directly like micropost.content, then you have to make a partial (html.erb) file and call that file in the view with render method

Resources