Reputation: 1387
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
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