1

Here is my code in the view: show.html.erb

<ul>
<% @bullets.each do |r| %>
    <li><%= r.content %></li>
<% end %>
</ul>

Here is my code in the controller: users_controller.rb

if cookies[:bullets].nil?
  @bullets = Bullet.all.shuffle.first(4)
  cookies[:bullets] = @bullets.collect(&:id)
else
 @bullets = []
 cookies[:bullets].each do |id|
   @bullets << Bullet.find(id)
 end
end

This returns undefined method 'each' for nil:NilClass on

<% @bullets.each do |r| %>

I would like to know why it does this, and how can I fix it to post four random fixed bullet contents from a database (sqlite3) table named "bullets" (column is content).

EDIT: This is the ENTIRE controller:

class StudentsController < ApplicationController
    #GET /
    def index
      @students = Student.all 

      respond_to do |format|
        format.html # index.html.erb
        format.json { render json: @students }
        end  
    end 

    #GET /new
    def new 
      @student = Student.new
    end 

    #POST
    def create
      @student = Student.new(params[:student])
          if @student.save
        render :file => 'app/views/success'
      else 
        render :file => 'app/views/students/fail'
      end  
        end

    #GET /students/{:id}
    def show 
       @student = Student.find_by_url(params[:id])

           respond_to do |format|
         format.html # show.html.erb
         format.json { render json: @student } 
           end  
    end

    #BULLETS Randomizing /students/new.html.erb
    if cookies[:bullets].nil?
      @bullets = Bullet.all.shuffle.first(4)
      cookies[:bullets] = @bullets.collect(&:id)
    else
     @bullets = []
     cookies[:bullets].each do |id|
       @bullets << Bullet.find(id)
     end
    end

    #GET /students/1/edit
    def edit 
        @student = Student.find_by_url(params[:id])
    end 

    def update
      @student = Student.find_by_url(params[:id])
      respond_to do |format|
        if @student.update_attributes(params[:student])
          format.html { redirect_to @student, notice: 'Student was successfully updated.'}  
        else 
          format.html { render action: "edit" } 
          format.json { render json: @student.errors, status: :unprocessable_entity } 
        end 
        end 
    end 

    #DELETE
    def destroy 
    @student = Student.find_by_url(params[:id])
    @student.destroy

        respond_to do |format|
         format.html { redirect_to students_url }
         format.json { head :no_content }
        end
    end
end 

EDIT #2: Like so?

#GET /students/{:id}
    def show 
       @student = Student.find_by_url(params[:id])

        #BULLETS Randomizing /students/show.html.erb
    if cookies[:bullets].nil?
      @bullets = Bullet.all.shuffle.first(4)
      cookies[:bullets] = @bullets.collect(&:id)
    else
     @bullets = []
     cookies[:bullets].each do |id|
       @bullets << Bullet.find(id)
     end
    end

           respond_to do |format|
         format.html # show.html.erb
         format.json { render json: @student } 
           end  
    end
Evan
  • 345
  • 1
  • 5
  • 14
  • 2
    You're certain that the `bullets` table is not empty? – Kevin Bedell Jul 06 '12 at 03:05
  • 150% positive it's not empty. EDIT: It contains 8 bullets which have both an ID, Name, Content, and time stamps. – Evan Jul 06 '12 at 03:09
  • 1
    Even if the table were empty, `Array#first` returns an empty array (not nil) when it receives an integer argument, and he explicitly initializes `@bullets` to an array in the other branch of the `if` statement. Is there anything else happening in your controller action or view that you could post? – Brandan Jul 06 '12 at 03:12
  • 1
    is that code to get bullets inside your show action? or is it in another method? if so, make sure it's getting called.. either explicitly from the show action or with a before_filter – SMathew Jul 06 '12 at 03:14
  • 3
    yeah see, it's outside the show action – SMathew Jul 06 '12 at 03:15
  • Yes it is to retrieve bullets from the database and place four random ones onto the view (page). EDIT: What can I do to fix it? I'm sorry but I'm not the most experienced. – Evan Jul 06 '12 at 03:16
  • 2
    move that block of code into the show action – SMathew Jul 06 '12 at 03:16
  • 1
    yeah, but you have to actually do the +1 part, not just talk about it :D – SMathew Jul 06 '12 at 03:23

3 Answers3

2

Looks like it should be:

#GET /students/{:id}
def show 

   @student = Student.find_by_url(params[:id])

   #BULLETS Randomizing /students/new.html.erb
   if cookies[:bullets].nil?
     @bullets = Bullet.all.shuffle.first(4)
     cookies[:bullets] = @bullets.collect(&:id)
   else
     # simpler to use an 'in list' for only 4 id's
     Bullet.where("id in (?)", cookies[:bullets])
   end

   respond_to do |format|
     format.html # show.html.erb
     format.json { render json: @student } 
   end  
end

Notice I converted your loop over the cookies array into a single statement with an 'in list' -- that should simplify the sql generated for the lookups.

Although, it's arguable that this code should be pushed into the model:

class Bullet < ActiveRecord::Base

  NUM_USER_BULLETS = 4
  # fetch a random set of 
  def self.user_bullets
    Bullet.all.shuffle.first(NUM_USER_BULLETS)
  end
end

Or something similar. Then your controller is simpler:

#GET /students/{:id}
def show 

   @student = Student.find_by_url(params[:id])

   #BULLETS Randomizing /students/new.html.erb
   if cookies[:bullets].nil?
     @bullets = Bullet.user_bullets
     cookies[:bullets] = @bullets.collect(&:id)
   else
     # simpler to use an 'in list' for only 4 id's
     Bullet.where("id in (?)", cookies[:bullets])
   end

   respond_to do |format|
     format.html # show.html.erb
     format.json { render json: @student } 
   end  
end

With the code migrated into your model, your controller is simpler.

Kevin Bedell
  • 13,254
  • 10
  • 78
  • 114
  • And the "id in (?)". Does it need something in place of '?' – Evan Jul 06 '12 at 03:45
  • 1
    The syntax `where("id in (?)", cookies[:bullets])` basically means *substitute the variable `cookies[:bullets]` for the `?` in the actual query*. – Kevin Bedell Jul 06 '12 at 03:53
  • Ohhh okay, thank you. May I ask why my second edit on the question worked the first time, but not the second (after a refresh)? – Evan Jul 06 '12 at 04:00
  • I get an "undefined method 'where' for nil:NilClass" now. – Evan Jul 06 '12 at 04:12
  • 1
    Sorry - there was an error in my code above. I've fixed it. It should've read `Bullet.where("id in (?)", cookies[:bullets])`. – Kevin Bedell Jul 06 '12 at 14:07
  • Thank you for your help, but between the time that I modified the code, something got corrupted, and I never saved on GitHub (which was a terrible idea), so I have to restart completely... I will be reusing this, and thank you again so much for your help. – Evan Jul 06 '12 at 19:15
  • 1
    Ooops! Well, it happens. I'm glad I could help. If you felt it was helpful I'd appreciate a check mark / acceptance! – Kevin Bedell Jul 06 '12 at 19:18
  • I hate to be a bother, but I am having trouble with your code. After refreshing the page or going back to the list to click on the link to the page - the error "undefined method `each' for nil:NilClass" pops up from the code above in my view (show.html.erb). Any suggestions? – Evan Jul 08 '12 at 03:37
1
class StudentsController < ApplicationController
   before_filter :get_bullets, :only => [:show]


  def show

  end

  ... 


  protected

  def get_bullets
  #BULLETS Randomizing /students/new.html.erb
    if cookies[:bullets].nil?
      @bullets = Bullet.all.shuffle.first(4)
      cookies[:bullets] = @bullets.collect(&:id)
    else
     @bullets = []
     cookies[:bullets].each do |id|
       @bullets << Bullet.find(id)
     end
    end

  end



end
SMathew
  • 3,993
  • 1
  • 18
  • 10
1

I would suggest refactoring your "random bullets" code into a before_filter for the show action and performing the randomization at the database, which will be faster than loading the entire table and performing the randomization in Ruby:

class UsersController < ApplicationController
  before_filter :assign_bullets, :only => :show

  # ...

private

  def assign_bullets
    if cookies[:bullets]
      @bullets = cookies[:bullets].map { |id| Bullet.find(id) }
    else
      @bullets = Bullet.order('RANDOM()').limit(4)
      cookies[:bullets] = @bullets.map(&:id)
    end
  end
end
Brandan
  • 14,735
  • 3
  • 56
  • 71
  • 1
    Be careful using `RANDOM()` -- it's format can be database-specific. Also, if the table gets large it can slow things. See here for more info: http://stackoverflow.com/questions/2752231/random-record-in-activerecord – Kevin Bedell Jul 06 '12 at 03:36
  • 1
    Thanks @KevinBedell. That thread is definitely worth reading. The OP should benchmark these different approaches on his data before settling on a solution. – Brandan Jul 06 '12 at 13:19
  • Definitely doing so. Thank you Brandan. – Evan Jul 06 '12 at 19:28