Co2 Co2 - 17 days ago 3
Ruby Question

RoR: Mark an object complete, save completed_on in other model

I am creating an action whereby a user can mark homework as complete.

Homework is stored mostly in homework table but homework_student holds the completed_on and a boolean complete attribute:

homework_student.rb

id :integer not null, primary key
# school_user_id :integer not null
# homework_id :integer not null
# completed_on :datetime
# created_at :datetime not null
# updated_at :datetime not null
# complete :boolean

belongs_to :homework, :class_name => 'Homework', :foreign_key => :homework_id, dependent: :destroy


homework.rb

has_many :homework_students, :class_name => 'HomeworkStudent', dependent: :destroy


My attempt...

In my show view for homework I want the user to be able to click complete on their homework and to have the date stored in the completed_on attribute in homework_students.

Routes:

resources :homeworks do
resources :homework_students do
member do
patch :complete
end
end
end


homeworks_controller.rb:

def complete
@complete_item = Homework.homework_students.update_attribute(:completed_on, Time.now)
end


In my view:

<% @homework.homework_students.each do |homework_student| %>
<%= link_to "complete", complete_homework_path(:homework_id => @homework, :home_work_students_id => homework_student), method: :patch %>
<% end %>


I am trying a PATCH method but it is not working. I'm not sure whether it is better to use the boolean attribute for this but i'm not sure how. Homework has many users and each user updates whether they completed or not.

Error:

undefined local variable or method `homework


Much appreciate any guidance.

UPDATE: homeworks_controller.rb

before_action :find_homework, only: [:show, :complete, :edit, :update]
#before_action :set_homework, only: [:show]
#before_action :set_homework_student, except: [:create]
before_action :authenticate_user!

....
def complete
# Loop over each matching homework_student for the @homework where the id matches and update the completed_on attribute.
# You need to get the indivual homework_student record and update it. You could do it other ways but this seemed to work for me.
@homework.homework_students.where(id: params[:home_work_students_id]).each do |homework_student| homework_students.update_attributes(:completed_on => Time.now)
end
# Redirect back to the @homework view.
redirect_to @homework, notice: 'Homework was successfully marked as complete.'
end
....

private

def set_homework_student
@homework_students = HomeworkStudent.find(params[:homework_id])
end

def set_homework
@homework = @homework_students.homeworks.find(params[:id])
end


def homework_params
params.require(:homework).permit(:user_id, :id, :school_user_id, :homeworks, :significance, :significance_id, :sig_option, :feedback_request, :subject, :source, :description, :due, :completed_at, homework_students: [:completed_on, :complete], school_user: [:f_name, :s_name])
end



def find_homework
@homework = Homework.find(params[:id])
end
end


UPDATED ERROR:

Error message:

undefined method `update_attributes' for nil:NilClass

Rails.root:

Application Trace | Framework Trace | Full Trace
app/controllers/homeworks_controller.rb:39:in `block in complete'
app/controllers/homeworks_controller.rb:39:in `complete'
Request

Parameters:

{"_method"=>"patch",
"authenticity_token"=>"OL1vW0BnnJ8TM5lshZWNKisNMrTU2Gp2cY7jYf3T+NRhlx2994q0ndrpehz+vW5unY1EBtSKCHecOJjTDwLHsw==",
"home_work_students_id"=>"142",
"homework_id"=>"78",
"id"=>"78"}


VIEW Update

<% @homework.homework_students.each do |homework_student| %>
<%= link_to "complete", complete_homework_path(@homework, homework_student), method: :patch %>
<% end %>

Answer

I mocked this version up based on our comments and it works on my end although it may not be the best approach. You may want to watch this video from Mackenzie Child who uses ajax to make this type of update on his todo app (mark things as complete, etc).

In your routes.rb

resources :homeworks do 
    member do
      patch :complete
    end
    resources :homework_students
end

In your homework_controller.rb

 def complete
    # Loop over each matching homework_student for the @homework where the id matches and update the completed_on attribute.
    # You need to get the indivual homework_student record and update it. You could do it other ways but this seemed to work for me.
    @homework.homework_students.where(id: params[:home_work_students_id]).each do |homework_student|
      homework_student.update_attributes(:completed_on => Time.now)
    end
    # Redirect back to the @homework view.
    redirect_to @homework, notice: 'Homework was successfully marked as complete.'        
  end

before_action :set_homework, only: [:show, :complete]

In your homework show.html.erb

# This assumes you are looping over the @homework.homework_students in your view to create a link for each homework_student record and that will give you access to the homework_student local variable.
<%= link_to "complete", complete_homework_path(:homework_id => @homework, :home_work_students_id => homework_student), method: :patch %>