ruby-on-railssimple-form-for

How to maintain information in a simple_form_for instance until a correct answer is passed


I am trying to create a simple rails website that I can use to practice my mental math. The idea is that I create a random question using a randomly selected ruby question_creator function I defined. This function returns two numbers, along with their answer.

If a user types in an answer that is incorrect, I want to display a quick error message and display the same question again. While redisplaying the question I would like to keep track of the number of attempts required to find the correct answer, as well as the total time taken to do so.

Where I am stuck now is that I can not figure out how to maintain the randomly generated question, along with the number of try's while reloading the simple_form_for after an incorrect input.

In order to do this I have created a model named Questions which holds the number of trys and time taken for each question. Ideally I would like to create an instance of this object whenever a question is answered correctly. The idea of having a single Question instance for every question answered is that I can then later use this to track my progression and improvements.

Right now the Questions Schema looks like this:

 create_table "questions", force: :cascade do |t|
    t.integer "trys"
    t.integer "time"
    t.bigint "user_id"
    t.bigint "question_type_id"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.index ["user_id"], name: "index_questions_on_user_id"
  end

My Questions class method to generate random questions looks like this. I am not sure if this is even the correct place this method, so please feel free to feedback on this as well. My idea is that in the end I have several different questions e.g. add_two_numbers(), multiply_two_numbers(), subtract_three_numbers() etc... from which I can randomly pick one each time.

 def random_two_digit_number()
    return rand(10..99)
  end

  def add_two_numbers()
    num1 = random_two_digit_number()
    num2 = random_two_digit_number()
    answ = num1 + num2
    return num1, num2, answ
  end

In order to pass the users answer, along with the actual answer answ to the create method I have added them as hidden fields in my simple_form_for.My Question/New page looks like this:

<% @question = Question.new() %>

<% num1, num2, answ = @question.add_two_numbers %>
<h3>  <%= num1 %> + <%= num2 %></h3>

<p>Trys: <%= @question.trys %></p>

<%= simple_form_for @question do |f| %>
  <%= f.hidden_field :trys, :value => @question.trys || 1 %>
  <%= f.hidden_field :answ, :value => answ %>
  <%= f.input :user_answ, input_html: {value: ''}  %>
  <%= f.button :submit %>
<% end %>

The create method in the questions controller:

def create
    @question = Question.new(trys: question_params["trys"], time: question_params["time"])
    @question.user_id = current_user.id
    respond_to do |format|
      if @question.save && (question_params["answ"] == question_params["user_answ"])
        format.html { render :new, notice: 'Correct!' }
        format.json { render :show, status: :created, location: @question }
      else
        format.html { render :new, notice: 'Thats wrong' }
        format.json { render json: @question.errors, status: :unprocessable_entity }
      end
    end
  end

Whenever I hit the point where question_params["answ"] == question_params["user_answ"], meaning that the user gave a wrong answer, I would like to increment the trys count by one and redirect him to the same question. As of right now, I can not get this to work however.

I have already build a quick prototype of this in python, if that is of any help: https://codeshare.io/arlrX7

I would be super glad I somebody could point me in the right direction, as I would really love to get this to work :)


Solution

  • There are many things wrong with this code, but let's tackle the ones preventing you from making it work.

    1. In your new.html.erb you have two problematic lines -
    <% @question = Question.new() %>
    <% num1, num2, answ = @question.add_two_numbers %>
    

    Having logic in your view is a bad idea, and it prevents you from reaching your goal, too. Define them inside new action in your controller.

    1. Next stop - submitting the form. You now assign @question and @num1 and its friends in the new action, and your form still works. But alas, that didn't fixed the problem yet. What you need now if to have those same variables inside your create action, because when you render :new, notice: 'Thats wrong', it's being done inside the context of create, with local variables available in that context.

    So, add num1 and fiends as hidden fields in to your form, and assign those values inside the create action.

    @num1 = question_params[:num1]
    

    This way, when you render :new, you still have those @question and @num1 and whatever else you passed along

    1. Lastly, this line in your controller:
     if @question.save && (question_params["answ"] == question_params["user_answ"])
    

    What it does is saving the question and then checking the answer. Instead, try:

     @question.save if (question_params["answ"] == question_params["user_answ"])
    
    if @question.persisted? # rest of your code
    

    This way you only save the question if the answer is correct, and then check if it was saved to determine what to do next.