Search code examples
ruby-on-railsrubyruby-on-rails-4nomethoderror

NoMethodError undefined method `alq' for 1:Fixnum


Im prety new on rails, and this is my first solo app. I use the Prawn-rails to generate PDFs.

Right now, im trying add lines depending on the number of lines defined at l_answer

Im getting this error: NoMethodError in Exams#show - undefined method `alq' for 1:Fixnum

My Prawn code:

@exam.line_questions.each do |q| 

pdf.text "#{q.question.question}"
pdf.move_down(5)
pdf.text "#{q.question.l_answer.alq}"
pdf.move_down(30)

end

Im getting the error at: pdf.text "#{q.question.l_answer.alq}"

This is my model class for question:

class Question < ActiveRecord::Base

DISCIPLINA_TYPE = ["Portugues", "Matematica", "Estudo do Meio"]
ANO_TYPE = ["1º Ano", "2º Ano", "3º Ano", "4º Ano"]

has_reputation :votes, source: :user, aggregated_by: :sum

has_many :line_questions

validates :title , presence:true , uniqueness: true
validates :question, presence:true
validates :disciplina, inclusion: DISCIPLINA_TYPE
validates :ano, inclusion: ANO_TYPE
def alq
  linhas = question.l_answer
  for i in linhas do
    until i <= linhas
      if i = 1 
        "R:________________________________________"
        i +=1
      else
        "__________________________________________"
        i += 1
      end
    end
  end
end

Well maybe im not explaining my self very well...

l_answer is an argument of the class Question.

So i defined the a new Question.

So that question will have 4 lines to answer so l_answer = 4

I change my code a litle to this:

def alq(l_answer)
  l = l_answer
  for i in l do
    until i = 0
      if i = 1 
        "R:________________________________________"
        i -=1
      else
        "__________________________________________"
        i -= 1
      end
    end
  end
end

And the Prawn pdf file to this:

@exam.line_questions.each do |q| 
pdf.text "#{q.question.question}"
pdf.move_down(5)
pdf.text "#{q.question.alq(q.question.l_answer)}"
pdf.move_down(30)

end

But now i get this: undefined method `each' for 1:Fixnum!

Is this closer?

@VitalyKushner As a side note, alq and l_answer are not that good names for methods. You are not saving trees by using less symbols, you are making it harder to read ;) - haahahah thats what being a noob means :). Thanks for the input


Solution

  • You have several problems in here.

    • for...in only works with collections, and l_answer is not.

    In fact, IIRC, for loops are transformed by the language to each method call, this way:

    for item in collection do
     # work with item
    end
    

    gets transformed into

    collection.each do |item|
     # work with item
    end
    

    hence your last error message since each method is defined on Array and not on Fixnum

    • Your first example uses for and until, which are the same things (or quite), this is going to crash.

    Try experimenting with puts to see what really happens

    def alq(l_answer)
      l = l_answer
      for i in l do
        puts "#{i} in for loop"
        until i = 0
          puts "#{i} in until loop"
          if i = 1 
            "R:________________________________________"
            i -=1
          else
            "__________________________________________"
            i -= 1
          end
        end
      end
    end
    

    There are probably others, which seems normal as I understand you're a beginner, but i'm not here to point at mistakes

    For loops are very seldom used in ruby, there are looping methods which use is much more idiomatic(aka the ruby way). Integer#times, Array#each are two of those. Please also have a look at Enumerable Module which define several other ways to loop through a collection and act on individual items.

    From what I understand, this is what you want

    def alq(l_answer)
      response = []
      l_answer.times do |index|
          if index = 0
            response += "R:________________________________________"
          else
            response += "__________________________________________"
          end
        end
        response.join('\n')
      end
    end
    

    or even better (I think):

    def alq(l_answer)
    
      (0...l_answer).map do |index|
          (index == 0 ? "R:" : '__') + "______________________________________"
      end.join('\n')
    end
    
    • (0...l_answer)is an exclusive range, you can see it as [0, 1, 2, ..., l_answer-1]
    • map is method from Enumerable module