In my Rails 3.2 app, I want to populate some fields based on calculations where the field values users enter are the variables. However, with my current code, the calculation seems to only work based on the values already in the database - it doesn't calculate correctly on the initial save, but it will calculate correctly if I go back in the record and save it a second time.
I have these four fields in my model (Trade):
The user creates a trade with an entry price, and then later edits the trade with the exit_price. When the exit_price is entered, the app should calculate percent_result and dollar_result. However, right now, these result fields are not populating correctly on the first update - it seems to be because it doesn't read the exit_price from the field (when a user enters it in the form), only once it is saved in the DB.
What is going wrong in my controller?
my controller:
def update
@trade = Trade.find(params[:id])
exit_price = params[:trade][:exit_price]
if !exit_price.blank?
@trade.percent_result = ((exit_price.to_f - @trade.entry_price)/@trade.entry_price) * 100
@trade.dollar_result = exit_price.to_f - @trade.entry_price
end
params[:trade][:exit_date] = Date.strptime(params[:trade][:exit_date], '%m/%d/%Y') unless params[:trade][:exit_date].blank?
params[:trade][:entry_date] = Date.strptime(params[:trade][:entry_date], '%m/%d/%Y') unless params[:trade][:entry_date].blank?
respond_to do |format|
if @trade.update_attributes(params[:trade])
format.html { redirect_to @trade, :flash => {:share =>"Your trade was successfully updated. Don't forget to share it with your friends, so you can profit together!"} }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: @trade.errors, status: :unprocessable_entity }
end
end
end
the view
<%= simple_form_for(@trade, :html=>{:class=> "form-horizontal well"}) do |f| %>
<%= f.text_field :entry_price, :class=>"input-small" %>
<%= f.text_field :exit_price, :class=>"input-small" %>
<%= submit_tag "Edit Trade" %>
<% end %>
This would probably be better accomplished with a before_save filter in your model.
Add
before_save :calculate_results
to the top of your model and then define
def calculate_results
unless self.exit_price.blank? || self.entry_price.blank?
self.percent_result = ((self.exit_price - self.entry_price)/self.entry_price) * 100
self.dollar_result = self.exit_price - self.entry_price
end
end
in your model as well. Taking this approach ensures that your results will always be consistent with your values for entry and exit price. Enforcing this in the controller violates the Rails principle of "thick model and thin controller" and may also lead to data consistency issues.
An even more consistent way of doing this would be to define dollar_result and percent_result as methods in your model. As your model is now, you have dollar_result stored in the database even though it is a derived value. As a general rule, you should only have one representation of each piece of data whereas here you have two. A helper method might look something like
def dollar_result
self.exit_price - self.entry_price unless self.exit_price.blank? || self.entry_price.blank?
end
You would define a similar method for percent_result. Using this method, you can guarantee that all of your data is consistent because it only has one, canonical representation in the system.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With