Rails 5 associated table data - ruby-on-rails

Inside the console I can access a related table by doing something like this.
#eec = ExpenseExpenseCategory.last
puts #eec.expense.payee.first_name
-> Charles
but inside a template if i have something to the extent of
<% #expense_expense_categories.each do |eec| %>
<tr>
<td><%= eec.amount %></td>
<td><%= eec.expense.payee.first_name %></td>
<\tr>
<% end %>
which is set in my reports controller
def expense_expense_category_report
#expense_expense_categories = ExpenseExpenseCategory.all
respond_to do |format|
format.html {}
format.js {}
end
end
I get this error: ActionView::Template::Error (undefined method `payee' for nil:NilClass):
but, If I call expense such as
<td><%= eec.expense %></td>
I get a relation Expense:0x007f9c8ad91b28> but I get the same error when trying to access it's attributes
<td><%= eec.expense.date %></td>
I get the error ActionView::Template::Error (undefined method `date' for nil:NilClass):
How can I make certain I can access the methods like I do in the console?

It is likely that one or more of your categories does not have an expense associated with it. This sometimes happens to me if I have entered data and then added an association to a model.
This means that it is nil,and you are calling the date on a nil object, which causes your error.
You could check this in the console by running some test code like below
ExpenseExpenseCategory.all.each do |eec|
if eec.expense.nil?
puts "Expense category #{eec.id} has no expense"
end
end

Related

Saving to database in Ruby on Rails without printing?

I'm new to Ruby on Rails, and am working on a finance app to aggregate all of my spending. I am collecting all of my data through a form, then showing it all in a different file called show.html.erb. I want to add up all of my spending in show.html.erb, and have so far been able to do it like so:
<strong>Total:</strong>
<td><%= #article.total = (#article.checking_accounts + #article.savings_accounts - #article.debt) %></td>
<td><%= #article.save %></td>
However, because the .save command returns a boolean, I am getting my result on my webpage followed by "true" i.e. "544 true" rather than just 544. How do I save to the database without printing to my webpage?
As already said, you should try to have the least amount of logic in your views.
Normally saving data is done by the Create action in a Controller. So, you should have something like this in your controller:
# articles_controller.rb
...
def create
#article = Article.find(params[:id])
#article.total = #article.checking_accounts + #article.savings_accounts - #article.debt
#article.save
end
def show
#article = Article.find(params[:id])
end
...
And this in your view:
# show.html.erb
...
<strong>Total:</strong>
<td><%= #article.total %></td>
...
You should not put #article.save inside your view, it should be in your controller, or a callback in your model, but since you just starting, I think, for a quick temporary solution, you can do this:
<% #article.total = (#article.checking_accounts + #article.savings_accounts - #article.debt) %>
<% #article.save %>
<strong>Total:</strong>
<td><%= #article.total %></td>
You can use <% ...%> if you want the process to run but do not want it to print. You can use <%= ... %> if you want to print the value.

Defining a method

I am new to coding & I am taking ruby on rails online class. I have followed the lecture and documented everything but I am getting "NonMethod" error. Here what I have in my file
Controller
class CoursesController < ApplicationController
def index
#search_term = 'jhu'
#courses = Coursera.for(#search_term)
end
end
Model
class Coursera
include HTTParty
base_uri 'https://api.coursera.org/api/catalog.v1/courses'
default_params fields: "smallIcon,shortDescription", q: "search"
format :[enter image description here][1]json
def self.for term
get("", query: { query: term})["elements"]
end
end
Views
<h1>Searching for - <%= #search_term %></h1>
<table border="1">
<tr>
<th>Image</th>
<th>Name</th>
<th>Description</th>
</tr>
<% #courses.each do |course| %>
<tr class=<%= cycle('even', 'odd') %>>
<td><%= image_tag(course["smallIcon"])%></td>
<td><%= course["name"] %></td>
<td><%= course["shortDescription"] %></td>
</tr>
<% end %>
</table>
These are the messages I am getting
NoMethodError in Courses#index
Showing /Users/Dohe/my_app/app/views/courses/index.html.erb where line #11 raised:
undefined method `each' for nil:NilClass
Can any help me with what I am doing wrong
Ruby 2.2.9 and Rails 4.2.3
Check what #courses contains in the controller, and change <% end %> to <% end if #courses.present? %> in your view, Its just making sure to only try to iterate through the #courses and populate it in the view if it actually contains any data, if it is nil then nil does not have a each method defined for it so you're getting the
undefined method `each' for nil:NilClass
As Subash stated in the comment, #courses is nil, which means that:
get("", query: { query: term})["elements"]
is returning nil. So, when you try #courses.each, you're getting the NoMethod error.
If you expect:
get("", query: { query: term})["elements"]
not to be nil, then you'll have to debug that. You could show us your console logs and we might be able to help with that.
Also, to protect from the NoMethod error, you could do:
def self.for term
get("", query: { query: term})["elements"] || []
end
This says, essentially, "return an empty array if get("", query: { query: term})["elements"] is nil". This will resolve your error, but could mask other problems you might be having. So, proceed with caution.

Implementing Decorators with Rails app

I'm trying to do something very simple with Decorators. I've never personally setup a decorator and I'm trying to spend a bit of time learning about it. Basically I'm trying to take an attribute of a table and build some view logic around it. Here is my code:
Pages Controller:
def dashboard
#assignments = current_account.assignments.all.decorate
#invitation = Invitation.new
end
dashboard decorator:
class AssignmentDecorator < Draper::Decorator
def status
if finished
"Finished"
else
"Waiting"
end
end
end
View table:
<tr class="assignment-rows">
<td><%= link_to assignment.name, account_assignment_path(assignment) %></td>
<td><%= assignment.assigned_workers %></td>
<td><%= assignment.status %></td>
</tr>
As you can see I'm trying to call my assignment method in the td there. but I'm getting this error when I try to render the dashboard:
Use:
#dashboard = Dashboard.find(dasboard_id).decorate
EDIT:
It looks like you want an assignment decorator instead of a dashboard decorator.
Generate a new one with Draper, and then:
#assignments = current_account.assignments.all.decorate

Conflicting View Logic

I have a show page where I need to both show the student's units and create a unit for them. However an error is being incurred when trying to do both.
In my controller
def show
#student = Student.find(params[:id])
#unit = #student.units.build
#units = #student.units
end
In my view
<%= simple_form_for #unit, url: student_units_path(#student) %>
# form...
<% end %>
<% #units.each do |unit| %>
<tr>
<td><%= unit.course %></td>
<td><%= unit.mailing_date.strftime('%m/%d/%y') %></td>
</tr>
<% end %>
The unit.course call works and any call that is only the first child of unit, however when I call a second method on unit I get this error:
undefined method `strftime' for nil:NilClass
despite knowing that the unit exists, hence the first call working
It seems your issue is that unit.mailing_date is nil, for newly-built records.
One solution would be to define a default value for mailing_date, either at the database level or in your application. For example, you could do something like:
class Unit < ActiveRecord::Base
# ....
after_initialize :set_default_mailing_date
private
def set_default_mailing_date
self.mailing_date ||= Date.today
end
end
Or alternatively, you could leave the mailing_date as nil and handle this gracefully in the view:
<td><%= unit.mailing_date.try!(:strftime, '%m/%d/%y') %></td>
If you are using ruby version 2.3+, then I would advise using the built-in safe navigation operator, rather than ActiveSupport's try! method:
<td><%= unit.mailing_date&.strftime('%m/%d/%y') %></td>
Finally, if you went with the above choice to leave the mailing_date as nil, then perhaps you'd like to display some default value in its place - for example:
<td><%= unit.mailing_date&.strftime('%m/%d/%y') || 'Not set' %></td>
As an alternative, I assume you don't want the new unit bound to the form being rendered which is what is causing the error.
You could do
#units = #student.units.reject(&:new_record?)
To remove the newly built unit from the collection

undefined method `order' for "1":String

On my landing_pages "show" page, I'm trying to show the leads that came in via that page and sort them via their "score".
My landing_page model has:
has_many :page_leads
My page_lead model has:
belongs_to :landing_page
In my index method within my page_leads controller, I have this, which works:
def index
#page_leads = PageLead.order(score: :desc)
end
I try to duplicate this in the landing_pages controller for the show method. Here is what I have:
def show
#landingpage = LandingPage.find(params[:id]).order(score: :desc)
end
When I try to go to the "Show" page, I get an error "undefined method `order' for "1":String". How can I get this to work?
My show page has the following code:
<tbody>
<% #landingpage.page_leads.each do |page_lead| %>
<tr>
<td><%= page_lead.fname %></td>
<td><%= page_lead.lname %></td>
<td><%= page_lead.score %></td>
</tr>
<% end %>
</tbody>
It is just:
def show
#landingpage = LandingPage.find(params[:id])
end
Cause find returns the LandingPage with id == params[:id]. No need to sort a single item.
find returns an array or records if the params[:id] is an array.. the order can't be applied to the array, because order isn't part of the array class.
If params[:id] is a single element then the find returns the record. Again order doesn't apply, because it's just one record not a container of records..
If you want an sorted list then use
LandingPage.where(id: params[:id]).order
But since this is in the show I suspect you want the single item... As spickermann said drop the order
LandingPage.find(params[:id])
If you want to sort Page Leads, you would do something like
def show
#landingpage = LandingPage.find(params[:id])
#page_leads = #landingpage.page_leads.order(score: :desc)
end
As #spickermann mentioned, .find() only returns one model instance. You can't order this using ActiveRecord query methods.
If you want to order the page leads, you have to call .order on the set of page_leads that belong to the landing page

Resources