Setting an attribute to 0 if a checkbox is checked in Rails - ruby

I have a db column called starting_pt that is a decimal.
I want a checkbox, that if checked, will set starting_pt = 0.0
I have set-up the a virt attr in my Model and added it to the accesible's
attr_acessor :reset
attr_accessible :reset
I've set up
before_create :reset_starting_pt, :if => :reset?
def reset?
#reset == "1"
end
def reset_starting_pt
#starting_pt = 0.0
end
This is assuming that a checked checkbox is equal to "1" and an unchecked is equal to "0".
This solution is not working. Any guidance anyone can provide on how to do do this would be very helpful, thanks!

You can create a setter method for the reset which when supplied with "0" will reset your #starting_pt instance variable.
def reset= val
#starting_pt = 0.0 if 0 == val.to_i
end

Related

complex validation on object update?

I have a model "WorkDetail" and db-attrs related to problem are 'name', 'status' and 'approved_status' with datatype all integer and the model class definition is as:-
class WorkDetail < ActiveRecord::Base
enum name: [:smartcheck, :install, :verify]
enum status: [:pending, :inprogress, :complete]
belongs_to :work_order
has_many :cabinets
after_save :work_order_status_update
private
def work_order_status_update
work_detail_count = self.work_order.work_details.count
status_array = self.work_order.work_details.where(status: 2).count
if status_array == work_detail_count
if work_detail_count == 0
self.work_order.update({status: "pending"})
else
self.work_order.update({status: "complete"})
end
else
self.work_order.update({status: "inprogress"})
end
end
end
Now, I would like to add custom validation to be applied for below problem :-
validation should only apply for update process.
If object's name == "smartcheck" and status == "complete" then only the approved_status boolean attribute should be updated to true (the
default is false on migration), else should give error if not
smartchecked and the status is not complete on trying to update
approved_status attr.
Hoping the question makes sense and Thanks !!! in advance guys, Happy Coding.
1) Validation should only apply for update process
after_save :work_order_status_update, :on => :update
2) If object's name == "smartcheck" and status == "complete" then only
the approved_status boolean attribute should be updated to true (the
default is false on migration), else should give error if not
smartchecked and the status is not complete on trying to update
approved_status attr.
def test_status_and_name
if self.name == "smartcheck" and self.status == "complete"
self.update_attributes(approved_status: true)
else
errors[:base] << "smartchecked and the status is not complete on trying to update approved_status attr."
end
end

Refactor the Ruby CLI program

I'm new to programming in Ruby.
How do I make the output show Revenue and Profit or Loss?
How can I refactor the following code to look neater? I know it's wrong but I have no idea how to take my if profit out of the initialize method.
class Theater
attr_accessor :ticket_price, :number_of_attendees, :revenue, :cost
def initialize
puts "What is your selling price of the ticket?"
#ticket_price = gets.chomp.to_i
puts "How many audience are there?"
#number_of_attendees = gets.chomp.to_i
#revenue = (#number_of_attendees * #ticket_price)
#cost = (#number_of_attendees * 3) + 180
#profit = (#revenue - #cost)
if #profit > 0
puts "Profit made: $#{#profit}"
else
puts "Loss incurred: $#{#profit.abs}"
end
end
end
theater = Theater.new
# theater.profit
# puts "Revenue for the theater is RM#{theater.revenue}."
# I hope to put my Profit/Loss here
#
# puts theater.revenue
Thanks guys.
Do not initialize the object with input from the user, make your object accept the needed values. Make a method to read the needed input and return you new Theater. Last of all put the if in separate method like #report_profit.
Remember constructors are for setting up the initial state of the object, making sure it is in a valid state. The constructor should not have side effects(in your case system input/output). This is something to be aware for all programming languages, not just ruby.
Try this:
class Theatre
COST = { running: 3, fixed: 180 }
attr_accessor :number_of_audience, :ticket_price
def revenue
#number_of_audience * #ticket_price
end
def total_cost
COST[:fixed] + (#number_of_audience * COST[:running])
end
def net
revenue - total_cost
end
def profit?
net > 0
end
end
class TheatreCLI
def initialize
#theatre = Theatre.new
end
def seek_number_of_attendes
print 'Number of audience: '
#theatre.number_of_audience = gets.chomp.to_i
end
def seek_ticket_price
print 'Ticket price: '
#theatre.ticket_price = gets.chomp.to_i
end
def print_revenue
puts "Revenue for the theatre is RM #{#theatre.revenue}."
end
def print_profit
message_prefix = #theatre.profit? ? 'Profit made' : 'Loss incurred'
puts "#{message_prefix} #{#theatre.net.abs}."
end
def self.run
TheatreCLI.new.instance_eval do
seek_ticket_price
seek_number_of_attendes
print_revenue
print_profit
end
end
end
TheatreCLI.run
Notes:
Never use your constructor (initialize method) for anything other than initial setup.
Try to keep all methods under 5 lines.
Always try to keep each class handle a single responsibility; for instance, printing and formatting output is not something the Theatre class needs to care.
Try extracting all hard coded values; eg see the COST hash.
Use apt variables consistent to the domain. Eg: net instead of profit makes the intent clear.

Create empty object and set its attribute to something

I have an action in my controller called update_models where I set an instance variable called #selected_model based on the value of params[:model_id]:
def update_models
if params[:model_id].blank?
// create object with id attribute equals '0'.
else
#selected_model = Model.find(params[:model_id]
end
end
In the first part of the if statement, how can I assign #selected_model an empty object and set its id attribute to '0', so that I can access that attribute in my view using #selected_model.id?
Assuming that params is not nil
def update_models
#selected_model = Model.find_by_id( params[:model_id] ) || Model.new( id: 0 )
end
Can't you do this?
def update_models
if params[:model_id].blank?
#selected_model = Model.new
#selected_model.id = 0
else
#selected_model = Model.find(params[:model_id]
end
end
Maybe you can try something like (with no if condition):
def update_models
#selected_model = Model.find(params[:model_id])
rescue
#selected_model = Model.new
#selected_model.id = 0
end

Store calculation in database using variables submitted in a form

I'm submitting a simple form with variables that are named in the database.
I am trying to:
Store the submitted variables in the database (which works fine)
Run a calculation, then store that value into the database
No matter what I try I either get an error or 'nil' (upon #kcmil.inspect) as my result for #kcmil. I'm assuming in my current code i'm not passing the variables to the model, but it doesn't work even when it's in the controller.
I'm at a loss here. My variables that are submitted in the form store just fine as expected. I just want to be able to use submitted variables from a form (that are also database items that get stored upon submission) and before saving to the database (or after, should it matter?) run a calculation and store the result in a database item (that is not previously called in or saved from the form). Does that make sense? Any help or hints are GREATLY APPRECIATED!!
Here are my current calculators_controller create and edit actions:
def create
#calculator = Calculator.new(calc_params)
if #calculator.save
flash[:notice] = "Calculation created successfully."
redirect_to(:action => 'index')
else
render('new')
end
end
def update
#calculator = Calculator.find(params[:id])
if #calculator.update_attributes(calc_params)
flash[:notice] = "Calculation updated successfully."
redirect_to(:action => 'index', :id => #calculator.id)
else
render('edit')
end
end
private
def calc_params
params.require(:calculator).permit(:subsection, :amps, :volts, :distance, :vdrop, :phase, :kcmil, :section_id)
end
Here's my model
class Calculator < ActiveRecord::Base
before_create :kcmil_calc
def kcmil_calc
if #phase == 1
self.kcmil = ((13 * #distance.to_i * #amps.to_i ) / #vdrop.to_i).round(2)
else
self.kcmil = ((12.9 * #distance.to_i * #amps.to_i ) / #vdrop.to_i).round(2)
end
end
end
I HAVE IT! I HAVE IT!
before_update :defaults
def defaults
if self.phase == 1
self.kcmil = ((12.9 * distance * amps) / vdrop).round(2)
else
self.kcmil = ((13 * distance * amps) / vdrop).round(2)
end
end
solved it! I had to call self.phase instead of #phase and change before_create to before_update to get it to work . No change in the controller required. Dang - one simple #! I also removed the to_i because it's not needed since my views prevent me from submitting anything other than integers.

Object's associated collection always sums to 0 during validation (ActiveRecord)

I am recording financial Interactions, modelled as an even number of LedgerEntries and each overall Interaction must sum out to zero.
class Interaction < ActiveRecord::Base
has_many :entries, class_name: 'LedgerEntry'
validate :entries_must_sum_to_zero
def balance
credit = self.entries.sum(:credit)
debit = self.entries.sum(:debit)
return credit - debit
end
protected
def entries_must_sum_to_zero
if self.entries.count.odd?
errors.add(:entries, "There must be an even number of entries.")
end
if self.balance != 0
errors.add(:entries, "Entries must sum to zero.")
end
end
end
and
class LedgerEntry < ActiveRecord::Base
validates_numericality_of :credit, greater_than_or_equal_to: 0
validates_numericality_of :debit, greater_than_or_equal_to: 0
belongs_to :interaction
validate :one_of_credit_or_debit
protected
def one_of_credit_or_debit
if self.credit != 0 && self.debit != 0
errors.add(:credit, "You can't assign both a credit and debit to the one entry.")
end
end
end
The problem I have is that this test never fails.
it "should complain if an Interaction's balance is non-zero" do
d = LedgerEntry.create!(credit: 50.0)
expect {Interaction.create!(entries: [d])}.to raise_error ActiveRecord::RecordInvalid
end
during the execution of entries_must_sum_to_zero self.entries.count is always 0 and self.balance always returns 0.
How do I force the entries to be taken up before the validation method runs?
In your validation, you're using database operations to do the validation (i.e. count and sum), but the entries haven't been stored to the database yet and won't be until after the Interaction is saved and they can be stored with their foreign key.
However, the entries attribute can still be accessed and operated on due to the magic of ActiveRecord proxies. You just need to use operations that don't depend on going to the database, such as length instead of count and to_a.map(&:<attribute>).inject(&:+) instead of sum as in:
def balance
credit = self.entries.to_a.map(&:credit).inject(&:+)
debit = self.entries.to_a.map(&:debit).inject(&:+)
return credit - debit
end
Try testing it like this instead:
it "should complain if an Interaction's balance is non-zero" do
entry = LedgerEntry.new(:credit => 50.0)
interaction = Interaction.new(:entries => [entry])
expect {interaction.valid?}.to raise_error ActiveRecord::RecordInvalid
end

Resources