NathanTempelman
NathanTempelman

Reputation: 1387

What's the proper way to do this in Rails?

So I've got two records from Model, a and b. I want to do this:

def do_codependant_stuff(a,b)
    a.attribute += b.attribute2
    b.attribute = b.stuff+a.stuff
end

I keep hearing fat model, skinny controller, and no business logic in views, so I've put this in my Model model. Based on what a user clicks in one of my views, I want to call do_codependant_stuff(a, b) or do_codependant_stuff(b,a).

I've been just using the basic crud controller actions up to this point, and I'm not quite sure how to make this happen. Do I add this logic to my ModelController update action? Because it's technically updating them, just in a more specific way. Or make another action in the controller? How do I call it/set it up? Most of the default new, update etc are somehow called behind the scenes based on their respective views.

And is updating two things at a time bad practice? Should I split the do_codependant_stuff method into two instance methods and call them on each record with the other as a parameter?

Thanks for reading.

Edit: Alright, real world code. I'm displaying on the home page of my app two pictures. The user selects the one they like most. The ratings of these pictures change based on a chess ranking algorithm. This is the relevant section of my picture class.

 class Picture < ActiveRecord::Base
   ...
   ...
        def expected_first_beats_second(picture first, picture second)
            1/(1+10**((second.rating-first.rating)/400))
        end
        def first_beat_second(picA,picB)
            Ea = expected_first_beats_second(picA,picB)
            picA.rating += 50*(1-Ea)
            picB.rating += 50*(-(1-Ea))

        end
    end

The partial I'm using for the view just displays two pictures at random so far with

<% picA = Picture.offset(rand(Picture.count)).first %>
<% picB = Picture.offset(rand(Picture.count)).first %>

<div class = "row">
    <div class = "col-md-5">
        <%= image_tag picA.url, :class => "thumbnail" %>
    </div>
    <div class = "col-md-5">
        <%= image_tag picB.url, :class => "thumbnail" %>
    </div>
</div>

I need to somehow link the onclick of those images to the method in the model.

Upvotes: 0

Views: 58

Answers (1)

agmin
agmin

Reputation: 9348

Here's some code to get you started. Note the comments, they're ruby and rails best practices

Controller:

class PC < AC
  ...
  def compare
    # count query once, save the number
    count = Picture.count
    @pic_a = Picture.offset(rand(count)).first
    @pic_b = Picture.offset(rand(count)).first
  end

  def compare_submit
    # Note variables are snake cased, not camel cased
    pic_a = Picture.find(params[:winner_id])
    pic_b = Picture.find(params[:loser_id])
    pic_a.beats(pic_b)
    redirect to compare_pictures_path # Or wherever
  end
  ...
end

Any sort of querying should be done in the controller or model. You essentially should never directly access a model in your view. At this point your view has access to the instance variables set in the controller: @pic_a and @pic_b

View:

<%= link_to compare_submit_pictures_path(winner_id: @pic_a.id, loser_id: @pic_b.id) do %>
  <%= image_tag @pic_a.url, :class => "thumbnail" %>
<% end %>

<%= link_to compare_submit_pictures_path(winner_id: @pic_b.id, loser_id: @pic_a.id) do %>
  <%= image_tag @pic_b.url, :class => "thumbnail" %>
<% end %>

So we just linked to a new path (see routes below) that will pass two parameters: winner_id and loser_id so whichever picture the user clicks on you'll know which one they chose and which one they didn't choose.

Model:

class Picture < AR::Base
  # Method arguments don't need a type declaration
  def beats(loser)
    # Always lowercase variables
    ea = 1/(1+10**((loser.rating-self.rating)/400))
    self.rating += 50*(1-ea)
    loser.rating += 50*(-(1-ea))
    self.save
    loser.save       
  end
end

This explicitly uses self for clarity, which isn't necessary. Calling save or rating = ... implicitly calls it on self since we're in the context of an instance method on the picture Model.

Routes:

resource :pictures do
  collection do
    get :compare
    get :compare_submit
  end
end

Upvotes: 1

Related Questions