0
votes

I've been searching these forums for the passed 2 days and finally given up. I've been working on a rails app where you can upload workouts, and then have other view them and download any attached excel files. My form looks like this

  '<div class="form-group">
     <div class="control-label col-md-2">
                <%= f.label :name, "Workout name" %><br>
              </div>
              <div class="col-md-10">
                <%= f.text_field :name, class: "form-control" %>
              </div>
            </div>
            <div class="form-group">
              <div class="control-label col-md-2">
                <%= f.label :summary, "Summary", placeholder: "Who's this for? what experience level? Athletes or Bodybuilders etc"  %>
              </div>
              <div class="col-md-10">
                <%= f.text_area :summary, class: "form-control", rows: 10%>
              </div>
            </div>
          <div class="clearfix">
            <div class="col-lg-3 col-md-6">
            <%= f.label :Movement %>
              <%= f.fields_for :movements do |movement| %>
                <%= render 'movement_fields', f: movement %>
            <% end %>
                <div class="links">
                  <%= link_to_add_association 'Add movement', f, :movements, class: "pull-right d-inline-block btn btn-primary add-button" %>
                  </br>
                  </br>
                </div>
            </div>
            <div class="col-lg-3 col-md-6">
                 <%= f.label :Reps %>
                    <%= f.fields_for :reps do |rep| %>
                      <%= render 'rep_fields', f: rep %>
                    <% end %>
                <div class="links">
                  <%= link_to_add_association 'Add Reps', f, :reps, class: "pull-right d-inline-block btn btn-primary add-button" %>
                </br>
                </br>
                </div>
            </div>
            <div class="col-lg-3 col-md-6">
            <%= f.label :Sets %>
                    <%= f.fields_for :zets do |zet| %>
                      <%= render 'zet_fields', f: zet %>
                    <% end %>
                <div class="links">
                  <%= link_to_add_association 'Add Set', f, :zets, class: "pull-right d-inline-block btn btn-primary add-button" %>
                </div>
                  </br>
                  </br>
            </div>
            <div class="col-lg-3 col-md-6">
                 <%= f.label :Weights %>
                    <%= f.fields_for :weights do |weight| %>
                      <%= render 'weight_fields', f: weight %>
                    <% end %>
                <div class="links">
                  <%= link_to_add_association 'Add weight', f, :weights, class: "pull-right d-inline-block btn btn-primary add-button" %>
                </div>
                  </br>
                  </br> 
            </div>
            <strong>Upload image: </strong>
                <span class="picture">
                    <%= f.file_field :attachment %>
                </span>
          </div>

            <div class="form-group">
              <div class="col-md-offset-2 col-md-10">
                <%= f.submit class: "btn btn-success btn-lg" %>
              </div>
            </div>
          <% end %>
        </div>
      </div>
    </div>`

Which seems to upload everything i need to the database, because when i go through the entries it seems to have the data i need i.e workout_id attached to a movement etc.

Heres the schema.rb

# Deleted some other columns in this that are in the app that aren't relevant to the post to reduce the amount of text. Exercises column is from another element in the app that works that i used basically the exact same method in creating. 

ActiveRecord::Schema.define(version: 20161113011850) do


  create_table "exercises", force: :cascade do |t|
    t.integer  "duration_in_min"
    t.text     "workout"
    t.date     "workout_date"
    t.integer  "user_id"
    t.datetime "created_at",      null: false
    t.datetime "updated_at",      null: false
  end

  add_index "exercises", ["user_id"], name: "index_exercises_on_user_id"

  create_table "movements", force: :cascade do |t|
    t.string   "name"
    t.integer  "exercise_id"
    t.integer  "workout_id"
    t.datetime "created_at",  null: false
    t.datetime "updated_at",  null: false
    t.integer  "workout_id"
  end

  add_index "movements", ["exercise_id"], name: "index_movements_on_exercise_id"
  add_index "movements", ["workout_id"], name: "index_movements_on_workout_id"


  create_table "reps", force: :cascade do |t|
    t.integer  "amount"
    t.integer  "exercise_id"
    t.integer  "workout_id"
    t.datetime "created_at",  null: false
    t.datetime "updated_at",  null: false
  end

  add_index "reps", ["exercise_id"], name: "index_reps_on_exercise_id"
  add_index "reps", ["workout_id"], name: "index_reps_on_workout_id"

  create_table "reviews", force: :cascade do |t|
    t.text     "body"
    t.integer  "user_id"
    t.integer  "recipe_id"
    t.datetime "created_at"
    t.datetime "updated_at"
  end

  create_table "users", force: :cascade do |t|
    t.string   "username"
    t.string   "email",                  default: "",    null: false
    t.string   "encrypted_password",     default: "",    null: false
    t.string   "reset_password_token"
    t.datetime "reset_password_sent_at"
    t.datetime "remember_created_at"
    t.integer  "sign_in_count",          default: 0,     null: false
    t.datetime "current_sign_in_at"
    t.datetime "last_sign_in_at"
    t.string   "current_sign_in_ip"
    t.string   "last_sign_in_ip"
    t.datetime "created_at",                             null: false
    t.datetime "updated_at",                             null: false
    t.string   "first_name"
    t.string   "last_name"
    t.boolean  "admin",                  default: false
  end

  add_index "users", ["email"], name: "index_users_on_email", unique: true
  add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true

  create_table "weights", force: :cascade do |t|
    t.integer  "kilogram"
    t.integer  "workout_id"
    t.integer  "exercise_id"
    t.datetime "created_at",  null: false
    t.datetime "updated_at",  null: false
  end

  add_index "weights", ["exercise_id"], name: "index_weights_on_exercise_id"
  add_index "weights", ["workout_id"], name: "index_weights_on_workout_id"

  create_table "workouts", force: :cascade do |t|
    t.string   "name"
    t.text     "summary"
    t.integer  "user_id"
    t.string   "attachment"
    t.datetime "created_at",      null: false
    t.datetime "updated_at",      null: false
    t.integer  "duration_in_min"
  end

  create_table "zets", force: :cascade do |t|
    t.integer  "quantity"
    t.integer  "exercise_id"
    t.integer  "workout_id"
    t.datetime "created_at",  null: false
    t.datetime "updated_at",  null: false
  end

  add_index "zets", ["exercise_id"], name: "index_zets_on_exercise_id"
  add_index "zets", ["workout_id"], name: "index_zets_on_workout_id"

end

And the model workout.rb

class Workout < ActiveRecord::Base belongs_to :user mount_uploader :attachment, AttachmentUploader

  has_many :movements
  has_many :reps
  has_many :zets
  has_many :weights  

  accepts_nested_attributes_for :movements,
                                                            reject_if: proc { |attributes| attributes['name'].blank? },
                                                            allow_destroy: true
    accepts_nested_attributes_for :reps,
                                                            reject_if: proc { |attributes| attributes['amount'].blank? },
                                                            allow_destroy: true
  accepts_nested_attributes_for :zets,
                                                            reject_if: proc { |attributes| attributes['quantity'].blank? },
                                                            allow_destroy: true
    accepts_nested_attributes_for :weights,
                                                            reject_if: proc { |attributes| attributes['kilogram'].blank? },
                                                            allow_destroy: true

  validates :name, presence: true
  validates :summary, presence: true
  validates :movements, presence: true
  validates :reps, presence: true
  validates :zets, presence: true
  validates :weights, presence: true
end

and the controller workouts_controller.rb

 class WorkoutsController < ApplicationController
      before_action :set_workout, only: [:edit, :update, :show, :like, :review]
      before_action :authenticate_user!, except: [:show, :index, :like, :search]
      before_action :require_same_user, only: [:edit, :update]
      before_action :admin_user, only: :destroy

  def index
    @workouts = Workout.all
  end

  def show
    @random_workout = Workout.where.not(id: @workout).order("RANDOM()").first(3)
  end

  def new
    @workout = Workout.new
  end

  def create
    @workout = Workout.new(workout_params)
    @workout.user = current_user

    if @workout.save
      flash[:success] = "Your workout was created successfully!"
      redirect_to workouts_path
    else
      render :new
    end
  end

  def edit

  end

  def update
    if @workout.update(workout_params)
      flash[:success] = "Your workout was update success"
      redirect_to workout_path(@workout)
    else
      render :edit
    end
  end

  def destroy
    Workout.find(params[:id]).destroy
    flash[:success] = "Workout Deleted"
    redirect_to workouts_path
  end

  def review
    review = Review.create(body: params[:body], user: current_user, workout: @workout)
    if review.valid?
      flash[:success] = "Thank you for reviewing this workout"
    else
      flash[:danger] = "Review failed to post"
    end
    redirect_to :back
  end

  def deletereview
    Review.find(params[:revid]).destroy
    flash[:success] = "Review deleted"
    redirect_to :back
  end

  private

    def workout_params
        params.require(:workout).permit(:name, :summary, :attachment, :user_id, movements_attributes: [:id, :name, :_destroy], reps_attributes: [:id, :amount, :_destroy], zets_attributes: [:id, :quantity, :_destroy], weights_attributes: [:id, :kilogram, :_destroy],)
    end

    def set_workout
      @workout = Workout.find(params[:id])
    end

    def require_same_user
      if current_user != @recipe.user and !current_user.admin?
        flash[:danger] = "You can only edit your own recipes"
        redirect_to recipes_path
      end
    end


    def admin_user
      redirect_to recipes_path unless current_user.admin?
    end

end

and finally the index.html.erb

  <thead>
    <tr>
      <th>Name</th>
      <th>Summary</th>
      <th>Attachment</th>
      <th>Exercises</th>
      <th colspan="3"></th>
    </tr>
  </thead>

  <tbody>
    <% @workouts.each do |workout| %>
      <tr>
        <td><%= workout.name %></td>
        <td><%= truncate(workout.summary, length: 350) %><%= link_to 'click to see workout', workout_path(workout)%></td>
        <td><%= link_to "Download Workout", workout.attachment_url %>%></td>
        <td><%= link_to 'Show', workout %></td>
    <% end %>
    <% @workout.movements.each do |movement| %>
        <td>
          <ol>
            <li>
              <%= movement.name %>
            </li>
          </ol>
    <%end%>
    <% @workout.reps.each do |rep| %>
        <td>
          <ol>
            <li>
              <%= rep.amount %>
            </li>
          </ol>
    <%end%>

Sorry about all the text, i just wanted to include everything the first time round. And i know this question has been asked to death and i appologise, for not being able to workout the issue from the already answered questions, but, was reallly hoping to figure out why im getting this error.

NoMethodError in Workouts#index Showing /home/ubuntu/workspace/app/views/workouts/index.html.erb where line #24 raised:

undefined method `movements' for nil:NilClass

As I mentioned I've implemented almost the exact same functionality twice now, on 2 other functions within the app, which work almost exactly the same as this is 'supposed to'. So im quite confused :(

2

2 Answers

1
votes

You are getting the undefined method because you haven't declare the variable @workout - meaning @workout is nil and you are trying to invoke movements on that nil object.

You do have a variable @workouts but I'm assuming that isn't the object you want... I think you wanted to use workout as declared in the @workout.each |workout| which leads to the second issue. You are ending that block before attempting to use @workout (should be workout)

This is what you have:

<% @workouts.each do |workout| %>
   # do stuff
<% end %>
# variable workout now out of scope
<% @workout.movements.each do |movement| %>

What you should do is change the instance variable @workout to local variable workout and embed this in your each block.

<% @workouts.each do |workout| %>
   # do stuff with workout object here
  <% workout.movements.each do |movement| %>
    # do stuff with movement object here
  <% end %>
  <% workout.reps.each do |rep| %>
    # do stuff with rep object here
  <% end %>
  #etc ... etc...
<% end %>
0
votes

you are trying to call movements association on @workout variable because you have not declared any where in your controller action. So no method error exception is raised. If you want to loop in the movements of every workout you need to get inside the loop and call the association on it.

<% @workouts.each do |workout| %>
   <% workout.movements.each do |movement| %>
     <%= movement.name %>
   <% end %>
 <% end %>