asalgan
asalgan

Reputation: 336

Passing hashes into create action in Ruby on Rails

I'm trying to create a workout routine that gets created when a workout gets created by passing this in via ajax:

Parameters: {"utf8"=>"✓", "workout"=>{"name"=>"cool workout", "desc"=>"super cool"}, "exerciseorder"=>["4", "2"], "repssets"=>{"0"=>["40", "4"], "1"=>["60", "6"]}}

Here is what my Create action looks like in my Workout Controller:

exercise_order = params[:exerciseorder]
repssets = params[:repssets]
@workout = Workout.new(workout_params)

if @workout.save
  WorkoutRoutine.create(
    [
      exercise_order.each_with_index.map { |x,i| 
        {
          :exercise_id => x,
          :position => i,
          :workout_id => @workout.id
        }
      },
      repssets.map { |x| x.last }.each { |y| 
        {
          :reps => y.first,
          :sets => y.last
        }
      }
    ]
  )
  render :nothing => true    
else
  render json: @workout.errors.full_messages, status: :unprocessable_entity
end

If I use an opening and closing '[]' within the WorkoutRoutine.create, it tells me:

ArgumentError: When assigning attributes, you must pass a hash as an argument.

And when I change them to '{}' it tells me:

syntax error, unexpected ',', expecting =>

I've tried a myriad of different combinations and work-arounds but can't seem to figure out why it won't correctly parse the data and save it to the database, any help is very appreciated.

EDIT:

When I remove the initial {} and [] from the WorkoutRoutine.create:

WorkoutRoutine.create(
    exercise_order.each_with_index.map { |x,i| 
      {
        :exercise_id => x,
        :position => i,
        :workout_id => 20
      }
    },
    repssets.map { |x| x.last }.each { |y| 
      {
        :reps => y.first,
        :sets => y.last
      }
    }
  )

I get this error message:

ArgumentError: wrong number of arguments (2 for 0..1)

Edit2:

This is the jQuery code that sents to the data field via ajax:

var getId = $(".exercise-list").sortable('toArray');
      ary = []

    $(getId).each(function () {
      id = $(this[0]).selector;

     var reps = $("#reps" + id).val();
     var sets = $("#sets" + id).val();

     ary.push([reps, sets])
    });

    var orderRepsSets = { exerciseorder: getId, repssets: ary }       
    var formData = $('#new_workout').serialize();
    var data = formData + '&' + $.param(orderRepsSets);

 $.ajax({
     url: $("#new_workout").attr('action'),
     method: 'POST',
     data: data,
     success: (function() {
     ....
  });

Upvotes: 0

Views: 1520

Answers (2)

irruputuncu
irruputuncu

Reputation: 1490

Did I get it correctly that you want to create multiple WorkloadRoutine objects, one for each exercise with the corresponding repetitions, the position, etc. If yes, then in order to do that you will have to pass an array of hashes (one hash for each object) to the WorkoutRoutine.create() function. As engineersmnky correctly stated in his comment, the data structure you are currently passing is more like [[{...},{...},{...}],[{...},{...},{...}]], but instead it should be just [{...},{...},...]. Do achieve that, something like this should do the trick:

WorkoutRoutine.create(
  exercise_order.map.with_index { |x,i| 
    {
      :exercise_id => x,
      :position => i,
      :workout_id => @workout.id,
      :reps => repssets[i.to_s].first,
      :sets => repssets[i.to_s].last
    }
  }
)

If you could change repssets to an array like exercise_order you could even remove the string cast for getting the reps and sets, which would simplify the whole think even more.

Upvotes: 1

Leszek Zalewski
Leszek Zalewski

Reputation: 426

If it comes for errors they are quite self explanatory. But let's start from beginning..

I assume that WorkoutRoutine is an ActiveRecord::Base model. The WorkoutRoutine.create method gets 0 or 1 argument which should be a Hash or a block.

In the first iteration you were passing an Array instead of Hash, so it looked like:

WorkoutRoutine.create([a, b]) # => ArgumentError: When assigning attributes, you must pass a hash as an argument.

On the second iteration you stripped away the square brackets, what gave you 2 arguments instead of one Hash:

WorkoutRoutine.create(a, b) # => ArgumentError: wrong number of arguments (2 for 0..1)

If you read errors carefully you will start getting the idea what's happening.

About the workout routine itself

From what you specified I would assume that you want something like:

  1. Workout has many Routines
  2. Routine belongs to Workout and Exercise
  3. Routine is composed of fields like
    • position/order,
    • number of repetitions,
    • number of sets

If my assumption is correct, then you want to use nested_attributes and then have parameters and controller like

# given parameters as workout_params
{
  workout: {
    name: "workout name",
    desc: "workout description",
    routines_attributes: [
      { position: 1, exercise_id: 4, repetitions_number: 40, sets_number: 4 },
      { position: 2, exercise_id: 2, repetitions_number: 60, sets_number: 6 }
    ]
  }
}

# Controller
def create
  @workout = Workout.new workout_params

  if @workout.save
    redirect_to # ...
  else
    render :new
  end
end

private

def workout_params
  params.require(:workout).permit(:name, :desc, routines_attributes: [:position, :exercise_id, :repetitions_number, :sets_number])
end

It should be strait forward how to then create a view with fields_for and update models with proper associations

Upvotes: 0

Related Questions