Sequencing control with edit method in CRUD examples

I have been playing with the cookbook type of tutorials building a fish
tournament scoring piece and am having a bit of a problem with a DB
field called “points” in my “catch” table that is not input by the user
but is calulated based on a “size” field that is input by the user.

I have the following in my catch_controller.db

def new
@catch = Catch.new
@fish = Fish.find_all
@bait = Bait.find_all
end

def create
@catch = Catch.new(@params[‘catch’])
@catch.datetime = DateTime.now
@catch.points = @catch.size
if @catch.bait_id == 2
@catch.points = @catch.size + (@catch.size/10)
end
if @catch.bait_id == 3
@catch.points = @catch.size + (@catch.size/5)
end
if @catch.save
redirect_to :action => ‘list’
else
render_action ‘new’
end
end

Granted it’s not particularly elegant but when a new catch is entered
the “points” are correctly set in the create method. The problem I am
having is in “Edit”.

def edit
@catch = Catch.find(@params[“id”])
@fish = Fish.find_all
@bait = Bait.find_all

@catch.points = @catch.size
	if @catch.bait_id == 2
		@catch.points = @catch.size + (@catch.size/10)
	end
	if @catch.bait_id == 3
		@catch.points = @catch.size + (@catch.size/5)
	end
if @catch.save
    redirect_to :action => 'list'
else
    render_action 'new'
end

end

Where or in what method would I put similar code so that when someone
edits the “size” field of a record the “points” are properly tabulated?
The code above does not correctly work as it does for the new/create
function. Should this code be in an update method? What runs after the
edit view?

With default edit/update scaffolding I am seeing the “size” properly
updated but no effect on “points”. But if I then go back into the record
with the edit view and “submit” with no further changes the “points” are
magically updated. WTF is going on and how do I get my code to set the
points in the proper place? Clearly it does not belong in the edit view

Nevermind, solved. But a real POS solution. Clearly I am missing
something here.

def update
@catch = Catch.find(params[:id])
if @catch.update_attributes(params[:catch])
@catch.points = @catch.size
if @catch.bait_id == 2
@catch.points = @catch.size + (@catch.size/10)
end
if @catch.bait_id == 3
@catch.points = @catch.size + (@catch.size/5)
end
@catch.update_attributes(params[:catch])
redirect_to :action => ‘show’, :id => @catch
else
render :action => ‘edit’
end
end

By doing my points mod after the initial catch.update, then doing
another catch.update it works OK. Not sure why I couldn’t just do my
points processing before the first catch.update though – that’s how it
should work. This method is invoked after the edit view (form) is
submitted and the new score is input. I’m thinking that perhaps the new
score was input by the form, but the @catch instance is getting the old
score value to assign to points (from the db model) not the new one that
was freshly input inside the params. I bet there’s a way to extract it
from the params and insert the updated score into the params[:catch] so
that a single update can be done. Any ideas?

Hey,
I see you solved your problem, but you really should put that
calculation in the model, not in the controller, then call it from the
controller. This will keep your code clean and DRY, and makes it more
MVCish/Railsish. You may want to just put it in a before_save filter in
your model, would be simple, assuming on all saves you want to calculate
points. Look up callbacks in the api. Good Luck!

Chris C.
concentrationstudios.com

Mike K. wrote:

I have been playing with the cookbook type of tutorials building a fish
tournament scoring piece and am having a bit of a problem with a DB
field called “points” in my “catch” table that is not input by the user
but is calulated based on a “size” field that is input by the user.

I have the following in my catch_controller.db

Where or in what method would I put similar code so that when someone
edits the “size” field of a record the “points” are properly tabulated?
The code above does not correctly work as it does for the new/create
function. Should this code be in an update method? What runs after the
edit view?

With default edit/update scaffolding I am seeing the “size” properly
updated but no effect on “points”. But if I then go back into the record
with the edit view and “submit” with no further changes the “points” are
magically updated. WTF is going on and how do I get my code to set the
points in the proper place? Clearly it does not belong in the edit view

Mike K. wrote:

Also I am wondering about my “solution” – there really should not have

I see you solved your problem, but you really should put that
calculation in the model, not in the controller, then call it from the
controller. This will keep your code clean and DRY, and makes it more
MVCish/Railsish. You may want to just put it in a before_save filter in
your model, would be simple, assuming on all saves you want to calculate
points. Look up callbacks in the api. Good Luck!

If the scoring rules are fixed, you can just compute the points on
demand, rather than saving them in the database. E.g. (in Catch)

BAIT_SCALE_FACTORS = [1.0, 1.0, 1.1, 1.2]

def points
size * BAIT_SCALE_FACTORS[bait_id]
end

(untested!) You might need to add some protection against size or
bait_id being undefined (but the same would be true of your original
code)

Justin

Thanks Chris – so move the points tabulation code (which I do want on
all catch saves whether a new catch is entered or an existing one is
edited/updated) to the model but still invokve it from the controller? I
was under the impression actions not related to displaying data should
be in the controller MVC-wise etc.

Will a before-save filter be invoked on both new and edit operations
automatically or do I need to enable this? I’ll research the callbacks
and before_save later.

Also I am wondering about my “solution” – there really should not have
to be 2 calls to update_attributes should there? Does the before_save
get invoked through a callback when the update_attributes or save
methods are invoked? That would be a lot cleaner.

Thanks again,
Mike

Chris C. wrote:

Hey,
I see you solved your problem, but you really should put that
calculation in the model, not in the controller, then call it from the
controller. This will keep your code clean and DRY, and makes it more
MVCish/Railsish. You may want to just put it in a before_save filter in
your model, would be simple, assuming on all saves you want to calculate
points. Look up callbacks in the api. Good Luck!

Chris C.
concentrationstudios.com

Totally cool guys, I really appreciate all the pointers. The before-save
callback works like a charm once the moronic programmer realizes the
method goes in the model not the controller :wink: Justin I took your advice
a step further, and put the bait scale factors into the bait table so it
wouldn’t be hard-coded anywhere and would always be data-driven for
other tournaments, bait types, and potential point scaling factors
related to bait type.

The only thing that bites is my lack of understanding why the original
piece of code in the 2nd post was required to have 2 updates. When I
figure that out I’ll be much further down the road to understanding more
about data passing and flow control in RoR.

Thanks Again, even an old OS programmer can find a little joy in this
MVC stuff!

Mike K. wrote:

Totally cool guys, I really appreciate all the pointers. The before-save
callback works like a charm once the moronic programmer realizes the
method goes in the model not the controller :wink: Justin I took your advice
a step further, and put the bait scale factors into the bait table so it
wouldn’t be hard-coded anywhere and would always be data-driven for
other tournaments, bait types, and potential point scaling factors
related to bait type.

The only thing that bites is my lack of understanding why the original
piece of code in the 2nd post was required to have 2 updates. When I
figure that out I’ll be much further down the road to understanding more
about data passing and flow control in RoR.

Thanks Again, even an old OS programmer can find a little joy in this
MVC stuff!

Hi Mike!
Glad to see it all worked out. It needed two updates because you
updated them from the edit, then made the points, then updated again.
If you did the points logic outside of that if statement you would have
only needed the one.


Chris

Justin thanks for that idea. However I think I am going to store them
because there will be multiple fish per angler and ultimately I will be
computing the angler’s total points from all catches etc. So for
simplicity I really want to store the points although I like your
approach better – being a 25+ year C programmer I don’t have my teeth
around the Ruby syntax yet to do the obvious as you did with the points
method and array. I think I’ll be doing that anyway to better isolate
the common points logic.

Thanks!