4

I'm trying to build something that will randomly pick two objects from a list in Ruby (using Rails) and then perform basic operations on those objects. My current set up involves randomly picking numbers at the start - using these numbers to get objects from the database and then rendering those objects to index. However, now I'm trying to edit these objects using AJAX I'm running into a problem.

It seems that, when I click on a link that the random number in being recalculated and is causing my AJAX function not to work.

I've included (what I believe is) all relevant code below but let me know if you need to see anything else to get to the bottom of this. I'm looking to get the titles of the old animals and (ideally their previous ratings and) their new ratings to appear at the bottom of the page.

Thanks,

Matt

controllers/static_pages.rb

class StaticPagesController < ApplicationController
respond_to :html, :js
before_filter :pickanimals

def pickanimals
    @allAnimals = Animal.all
    @random_no = rand(@allAnimals.length)
    @animal = @allAnimals[@random_no]
    @allAnimals.delete_at(@random_no)
    @newRandom = rand(@allAnimals.length)
    @animal2 = @allAnimals[@newRandom]
end

  def index
    respond_to do |format|
        format.html
        format.js
    end
end

  def help
  end

  def about
  end

  def contact
  end

def league
end

def voting
    @votedAnimal = Animal.find(params[:id])
    if @votedAnimal == @animal
        @animal.rating += 1
        @animal2.rating -= 1
    else
        @animal.rating -= 1
        @animal2.rating += 1    
    end
    Animal.transaction do
        @animal.save!
        @animal2.save!
    end
    respond_to do |format|
        format.html { redirect_to root_path }
        format.js  
    end
end
end

app/views/static_pages/index.html.erb

<div class="center hero-unit">
<h1>Animal Attack</h1>
<p> Who will win when nature collides? </p>
<div class="animalcontainerright">
<h2> <%= @animal.name %> </h2> 
<%= link_to image_tag(@animal.attachment.url(:large)), voting_path(@animal.id), :remote => true, :confirm => "Rating:  "+@animal.rating.to_s  %>
</div>
<div class="animalcontainerleft">
<h2> <%= @animal2.name %> </h2>
<%= link_to image_tag(@animal2.attachment.url(:large)), voting_path(@animal2.id), :remote => true, :confirm => "Rating:  "+@animal2.rating.to_s  %>
</div>
<div id="animalfacts"></div>

routes.rb

AnimalAttack::Application.routes.draw do

resources :animals

root to: 'static_pages#index'
match '/help' => 'static_pages#help'
match '/about' => 'static_pages#about'
match '/contact' => 'static_pages#contact'
match '/league' => 'static_pages#league'
match '/voting/:id' => 'static_pages#voting'

end

rake routes

    animals GET    /animals(.:format)          animals#index
        POST   /animals(.:format)          animals#create
 new_animal GET    /animals/new(.:format)      animals#new
edit_animal GET    /animals/:id/edit(.:format) animals#edit
 animal GET    /animals/:id(.:format)      animals#show
        PUT    /animals/:id(.:format)      animals#update
        DELETE /animals/:id(.:format)      animals#destroy
   root        /                           static_pages#index
   help        /help(.:format)             static_pages#help
  about        /about(.:format)            static_pages#about
contact        /contact(.:format)          static_pages#contact
 league        /league(.:format)           static_pages#league
               /animals(.:format)          animals#new
               /voting/:id(.:format)       static_pages#voting
Kali_89
  • 617
  • 2
  • 7
  • 21

1 Answers1

8

I'm guessing @animal is being re-assigned because of your :before_filter. Unless you tell it otherwise, it'll execute before all actions (thus reassigning @animal before every controller action). You probably want to limit it to just the methods that need a newly-regenerated random number. For example:

before_filter :pickanimals, :only => :index

Also. Do you know about Array#sample? It's a built-in way of picking a random element from an array. You might find it useful here:

@animal, @animal2 = @allAnimals.sample(2)

ADDITIONALLY

Relying on instance variables in the controller like this is a pretty fragile way of passing information between functions. If your controller needs to know the ids of two models to have counters on each incremented or decremented based on user selection, why not just use a form to send those two ids to this controller action? So far, you'll only need two fields: chosen_animal_id and reject_animal_id.

pje
  • 21,801
  • 10
  • 54
  • 70
  • Hey - thanks for this. Changing the before filter doesn't seem to work, think it might be because I'm actually using the index view to render my AJAX so I require the index view to both go for a new random number and not (I know, terrible design). I'll check out the sample method, looks like a much better way! Thanks, Matt – Kali_89 Nov 03 '12 at 00:36
  • OK...more info on how it now doesn't work. When I change the `before_filter` I get the following error message on trying to access the homepage: `NoMethodError in Static_pages#index` with a bit more info given as: `undefined method `voting_path'` where `voting_path` refers to the links in the index view. Any idea? Thanks, Matt – Kali_89 Nov 03 '12 at 00:46
  • [Array#sample](http://www.ruby-doc.org/core-1.9.3/Array.html#method-i-sample) would be exactly how I cherry pick the two randoms as well. – Braden Nov 03 '12 at 01:55
  • @Kali_89: are you sure the error started after changing the filter? Not sure how that could cause this. You should paste the full output. – pje Nov 03 '12 at 03:50
  • @pje : I know what you mean - doesn't really seem to make any sense. Have now included the output of `rake routes` and my `routes.rb` file in the above. – Kali_89 Nov 03 '12 at 11:57
  • 1
    @Kali_89: That's an unrelated routing problem. Read over [this section of rails routing guide](http://guides.rubyonrails.org/routing.html#naming-routes). Once that makes sense, change your route to something like `match '/voting/:id' => 'static_pages#voting', :as => 'voting'` and see if that helps. – pje Nov 04 '12 at 02:24
  • @Kali_89: Here's an answer about [debugging routing from the console](http://stackoverflow.com/questions/433860/rails-how-do-you-access-restful-helpers), and another [really similar solution](http://stackoverflow.com/questions/5136940/undefined-method-user-path) to your `voting_path` problem. HTH. – pje Nov 04 '12 at 02:27
  • @pje Thanks for that. I'd actually just got to the bottom of the routing errors (you're right, just needed an `:as => voting`) and then distracted myself from my main problem by migrating everything to AWS S3. Now I'm using `Array#sample` (much simpler!) but still have the problem of needing to randomly generate variables (fine), keep them fixed until an action is performed (fine) and then make them available to all the controllers in a file (not fine). Any idea how to do this? A search suggested that it's not possible and the result of poorly designed code! Should I switch to forms? – Kali_89 Nov 04 '12 at 03:17