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
Related
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.
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
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
Okay so in the index portion of my controller I set
#patients = Patient.all
then in patients_helper.rb
def race_abrev
return self.caucasian
end
where caucasian is an integer datatype column in the patients table
then in the view index.html.erb
<% #patients.each do |p| %>
<td><%= p.gender %></td>
<td><%= p.ethnicity %></td>
<td><%= p.race_abrev %></td>
<% end %>
I get a
undefined method `race_abrev' for #<Patient:0xb4d95cd8>
I've checked the table and I'm expecting patient.caucasian to return the integer 1, what am I missing..any insight to a fundamental misunderstanding I seem to have?
race_abrev is a helper, not a method on Patient:
<%= race_abrev(p) %>
And the helper itself would return p.caucasian, although it'd seem like you'd actually want to do something with the value of caucasian, like a compare or something.
All this said, I'm not sure why you're not defining it (or what "it" actually is) on the model, since so far it doesn't seem to have anything to do with the view, which is what view helpers are for.
If you're storing something in the DB you want to transform it may or may not belong in a view helper; if it's to turn it into something human-readable I'd be more likely to put it in the model.
I think I'm overlooking something very simple here, but would really appreciate some help to work out what it is.
In the project show view, I'm displaying associated (has_many) tasks in a partial. I only want to display those records where a particular field is not empty. My view code looks like this.
<% for task in #tasks %>
<% unless task.user.notes.empty? %>
<tr>
<td><%= task.user.name %></td>
<td><%= task.user.notes %></td>
</tr>
<% end %>
<% end %>
This is returning undefined method 'notes' for nil:NilClass. This is strange as :notes is definitely in the User model.
The Project controller handling this is contains:
def show
#tasks = #project.tasks.paginate(:page => params[:page])
end
My models look as follows
Project
has_many :tasks
end
Task
belongs_to :project
belongs_to :user
end
User
has_many :tasks
end
What have I missed here? Am I using empty? correctly? Or should I be handling this in the controller? I currently have three partials on the Project show, all using the same Task query. Performance and/or best practice -wise, does it make more sense to have all three partials sourcing data from the same controller query, or to have a sperate query just for this case?
Thanks for any pointers.
The problem was that your User model was undefined when you called task.user.notes.
You can solve this problem as well as improve your overall design by making use of the #delegate macro provided by ActiveSupport in Rails. For example, inside of the Task model, try adding
delegate :notes, :to => :user, :prefix => true, :allow_nil => true
This adds a task.user_notes method to the Task model which will allow you to fetch the User's notes straight from the Task itself. Additionally, because we specified the allow_nil option, if there is no User associated with the Task, the result of the method will be nil instead of an exception.
You can also add this for the name attribute of the User allowing you to call task.user_name from within the view.
Resources:
delegate - http://apidock.com/rails/v3.0.9/Module/delegate
"Law of Demeter" - http://devblog.avdi.org/2011/07/05/demeter-its-not-just-a-good-idea-its-the-law/
In the controller
def show
#tasks = #project.tasks.paginate(
:page => params[:page],
:conditions=>["notes is not ? and notes !=?",nil,'']
)
end
OR, not in the controller
Write a helper method to abstract this.
Add a new helper method
def filter_tasks(tasks)
tasks.find(
:all,
:conditions=>["notes is not ? and notes !=?",nil,'']
)
end
And use helper in view
<% for task in filter_tasks(#tasks) %>
<% unless task.user.notes.empty? %>
<tr>
<td><%= task.user.name %></td>
<td><%= task.user.notes %></td>
</tr>
<% end %>
<% end %>