rails assign attributes to instance - ruby-on-rails

I am trying to calculate how much the total cost of my transaction will be. And to test I am running Transaction.last.calculate_total_payment, it is returning a BigDecimal which is what I want but none of the attributes are being updated on the instance after. What am I doing wrong?
class Transaction < ActiveRecord::Base
has_one :auction
has_one :bid
belongs_to :inventory_part
TIER0 = 0
TIER1 = 5_000
TIER2 = 50_000
TIER3 = 500_000
TIER4 = 1_000_000
.
.
.
.
def calculate_total_payment
part = self.bid.part_price
self.tax = part * self.tax_rate
if self.bid.tx.shipping_account
self.final_shipping_cost = 0
else # testing purposes
self.final_shipping_cost = self.bid.est_shipping_cost
end
price_before_fees = part + self.tax + self.final_shipping_cost
if price_before_fees < TIER1 #5,000
self.bid_aero_fee = price_before_fees * 0.025
self.armor_fee = price_before_fees * 0.015
elsif price_before_fees < TIER2 #50,000
self.bid_aero_fee = (price_before_fees - TIER1) * 0.015 + 125
self.armor_fee = (price_before_fees - TIER1) * 0.01 + 75
elsif price_before_fees < TIER3 #500,000
self.bid_aero_fee = (price_before_fees - TIER2) * 0.0125 + 800
self.armor_fee = (price_before_fees - TIER2) * 0.0075 + 525
elsif price_before_fees < TIER4 #1,000,000
self.bid_aero_fee = (price_before_fees - TIER3) * 0.0075 + 6425
self.armor_fee = (price_before_fees - TIER3) * 0.005 + 3900
else # anything over a million
self.bid_aero_fee = (price_before_fees - TIER4) * 0.0075 + 10175
self.armor_fee = (price_before_fees - TIER4) * 0.0035 + 6400
end
self.total_fee = self.armor_fee + self.bid_aero_fee
self.total_amount = price_before_fees + self.total_fee
end
end

You are never calling save in the calculate_total_payment method. You need to explicitly save the object for it to be persisted in the DB.

You should call save! at the end if you want to make your changes persistent in the database. Without this, it will modify the object in place (so changes will disappear after reloading of fetching the object again).

Related

Hi, I am creating a simpleplatformer game with love2d, I got a problem in the collisions

In my player script. I called a function to check is if a tile is collided with the player.
The basic tile collisions has been made, but I got some problems.
When the player jump while colliding with the wall, the player stop.
When the player is falling while colliding with the wall, the player stop.
I think the problem is in the Y collision checking part, but I can't figure out how to solve it.
--------------------- Collision Check ----------------------
local box = Vt2:new(self.pos.x + self.vel.x * dt * self.dirXTemp,
self.pos.y + self.vel.y * dt)
for i = 1, #collTile do
local tile = collTile[i]
local isColl = BdBoxCollision(box, self.size, tile.pos, tile.size)
if
isColl[1] == true and
isColl[2] == true and
isColl[3] == true and
isColl[4] == true then
local isOnWall = false
if self.pos.y + self.size.y > tile.pos.y then -- X Collision
if box.x + self.size.x / 2 < tile.pos.x + tile.size.x / 2 then
if box.x + self.size.x > tile.pos.x then
if self.dirX == 1 then
self.vel.x = 0
isOnWall = true
end
end
end
if box.x + self.size.x / 2 > tile.pos.x + tile.size.x / 2 then
if box.x < tile.pos.x + tile.size.x then
if self.dirX == -1 then
self.vel.x = 0
isOnWall = true
end
end
end
end
if box.y + self.size.y / 2 < tile.pos.y + tile.size.y / 2 then -- Y Collision
if box.y + self.size.y > tile.pos.y and
self.pos.y + self.size.y < tile.pos.y + 8 then
if isOnWall == false then
self.pos.y = tile.pos.y - self.size.y
self.vel.y = 0
end
end
elseif box.y + self.size.y / 2 > tile.pos.y + tile.size.y / 2 then
if box.y < tile.pos.y + tile.size.y then
self.vel.y = self.gravity
end
end
end
end
::skip::
self.pos.x = self.pos.x + self.vel.x * dt
self.pos.y = self.pos.y + self.vel.y * dt

Lua Script: Convert Multiple "if" into simpler form

I am trying to make a condition where the percentage would be calculated based on the number of fan operated and the amount of airflow. This is what I come out with
function System01()
CFM_SHOP1 = addr_getword("#W_HDW1")
CFM_SHOP2 = addr_getword("#W_HDW2")
STATUS_SHOP1 = addr_getbit("#B_M1")
STATUS_SHOP2 = addr_getbit("#B_M2")
OUTPUT_SHOP1 = addr_getword("#W_HDW10")
OUTPUT_SHOP2 = addr_getword("#W_HDW11")
CFM_1 = CFM_SHOP1 + CFM_SHOP2
if STATUS_SHOP1 == 1 then
OUTPUT_SHOP1 = CFM_SHOP1 * 10000 / CFM_1
addr_setword("#W_HDW10", OUTPUT_SHOP1)
if STATUS_SHOP2 == 1 then
OUTPUT_SHOP2 = CFM_SHOP2 * 10000 / CFM_1
addr_setword("#W_HDW11", OUTPUT_SHOP2)
end
TOTAL_1 = OUTPUT_SHOP1 + OUTPUT_SHOP2
addr_setword("#W_HDW19", TOTAL_1)
end
if STATUS_SHOP2 == 1 then
OUTPUT_SHOP2 = CFM_SHOP2 * 10000 / CFM_1
addr_setword("#W_HDW11", OUTPUT_SHOP2)
if STATUS_SHOP1 == 1 then
OUTPUT_SHOP1 = CFM_SHOP1 * 10000 / CFM_1
addr_setword("#W_HDW10", OUTPUT_SHOP1)
end
TOTAL_1 = OUTPUT_SHOP1 + OUTPUT_SHOP2
addr_setword("#W_HDW19", TOTAL_1)
end
addr_setbit("#B_M1", STATUS_SHOP1)
addr_setbit("#B_M2", STATUS_SHOP2)
addr_setbit("#B_M3", STATUS_SHOP3)
end
Is there any way that I can simplified it? Please note that this is only two example I give. There is total of 9 fan so it will be really complicated if i just use 'if'. Thanks in advance
To simplify the code use for-loop
function System01()
local CFM_SHOP = {}
local CFM = 0
for j = 1, 9 do
CFM_SHOP[j] = addr_getword("#W_HDW"..tostring(j))
CFM = CFM + CFM_SHOP[j]
end
local STATUS_SHOP = {}
for j = 1, 9 do
STATUS_SHOP[j] = addr_getbit("#B_M"..tostring(j))
end
local OUTPUT_SHOP = {}
for j = 1, 9 do
OUTPUT_SHOP[j] = addr_getword("#W_HDW"..tostring(j+9))
end
local TOTAL = 0
for j = 1, 9 do
if STATUS_SHOP[j] == 1 then
OUTPUT_SHOP[j] = CFM_SHOP[j] * 10000 / CFM
addr_setword("#W_HDW"..tostring(j+9), OUTPUT_SHOP[j])
end
TOTAL = TOTAL + OUTPUT_SHOP[j]
end
addr_setword("#W_HDW19", TOTAL)
for j = 1, 9 do
addr_setbit("#B_M"..tostring(j), STATUS_SHOP[j])
end
end

RoR: Method to return a statement in a json format?

I'm looking for a method that will return this statement in a JSON format.
def statement
total = 0
bonus points = 0
result = 'Car rental for #{#name.to_s}\n'
for r in #rentals
this_amount = 0
case r.car.style
when Car::SUV
this_amount += r.days_rented * 30
when Car::HATCHBACK
this_amount += 15
if r.days_rented > 3
this_amount += (r.days_rented - 3) * 15
end
when Car::SALOON
this_amount += 20
if r.days_rented > 2
this_amount += (r.days_rented - 2) * 15
end
else
end
if this_amount < 0
bonus_points -= 10
end
bonus_points = bonus_points + 1
if r.car.style == Car::SUV && r.days_rented > 1
bonus_points = bonus_points + 1
end
result += r.car.title.to_s + "," + this_amount.to_s + "\n"
total += this_amount
end
result += "Amount owed is " + "#{total.to_s}" + "\n"
result +="Earned bonus points: " + bonus_points.to_s
result
end
What method would I need to add to my class to return this statement in a JSON format? Thank you.
Simplest way to do that
return {:result => result}
at the end of your method.
But if you are using controller to show data to user, I would prefer using .to_json in my controller method

is there a way to apply dry and solid principal

I am working on this code to make it shorter so that it could be easier to study or read and I have but advanced to apply the dry and solid principle so could you help me with how to refactor this ruby on rails code which contains some if statements to do this.so that it becomes more easy to read and shorter using those principles.
if brand.rating = high
if price_reference < 15
price_suggestion = (price_reference - product_database.shipping_cost * 0.75) * 1.1
price_original = price_suggestion
elsif price_reference < 25
price_suggestion = (price_reference - product_database.shipping_cost * 0.775) * 1.1
price_original = price_suggestion
elsif price_reference < 50
price_suggestion = (price_reference - product_database.shipping_cost * 0.8) * 1.1
price_original = price_suggestion
elsif price_reference < 75
price_suggestion = (price_reference - product_database.shipping_cost * 0.825) * 1.1
price_original = price_suggestion
elsif price_reference < 100
price_suggestion = (price_reference - product_database.shipping_cost * 0.85) * 1.1
price_original = price_suggestion
elsif price_reference < 200
price_suggestion = (price_reference - product_database.shipping_cost * 0.875) * 1.1
price_original = price_suggestion
elsif price_reference < 400
price_suggestion = (price_reference - product_database.shipping_cost * 0.9) * 1.1
price_original = price_suggestion
elsif price_reference < 600
price_suggestion = (price_reference - product_database.shipping_cost * 0.925) * 1.1
price_original = price_suggestion
else
price_suggestion = (price_reference - product_database.shipping_cost * 0.95) * 1.1
price_original = price_suggestion
elsif brand.rating = low
if price_reference < 15
price_suggestion = (price_reference - product_database.shipping_cost * 0.75) * 0.9
price_original = price_suggestion
elsif price_reference < 25
price_suggestion = (price_reference - product_database.shipping_cost * 0.775) * 0.9
price_original = price_suggestion
elsif price_reference < 50
price_suggestion = (price_reference - product_database.shipping_cost * 0.8) * 0.9
price_original = price_suggestion
elsif price_reference < 75
price_suggestion = (price_reference - product_database.shipping_cost * 0.825) * 0.9
price_original = price_suggestion
elsif price_reference < 100
price_suggestion = (price_reference - product_database.shipping_cost * 0.85) * 0.9
price_original = price_suggestion
elsif price_reference < 200
price_suggestion = (price_reference - product_database.shipping_cost * 0.875) * 0.9
price_original = price_suggestion
elsif price_reference < 400
price_suggestion = (price_reference - product_database.shipping_cost * 0.9) * 0.9
price_original = price_suggestion
elsif price_reference < 600
price_suggestion = (price_reference - product_database.shipping_cost * 0.925) * 0.9
price_original = price_suggestion
else
price_suggestion = (price_reference - product_database.shipping_cost * 0.95) * 0.9
price_original = price_suggestion
else
if price_reference < 15
price_suggestion = (price_reference - product_database.shipping_cost * 0.75) * 1
price_original = price_suggestion
elsif price_reference < 25
price_suggestion = (price_reference - product_database.shipping_cost * 0.775) * 1
price_original = price_suggestion
elsif price_reference < 50
price_suggestion = (price_reference - product_database.shipping_cost * 0.8) * 1
price_original = price_suggestion
elsif price_reference < 75
price_suggestion = (price_reference - product_database.shipping_cost * 0.825) * 1
price_original = price_suggestion
elsif price_reference < 100
price_suggestion = (price_reference - product_database.shipping_cost * 0.85) * 1
price_original = price_suggestion
elsif price_reference < 200
price_suggestion = (price_reference - product_database.shipping_cost * 0.875) * 1
price_original = price_suggestion
elsif price_reference < 400
price_suggestion = (price_reference - product_database.shipping_cost * 0.9) * 1
price_original = price_suggestion
elsif price_reference < 600
price_suggestion = (price_reference - product_database.shipping_cost * 0.925) * 1
price_original = price_suggestion
else
price_suggestion = (price_reference - product_database.shipping_cost * 0.95) * 1
price_original = price_suggestion
end
Maybe something like:
def suggest_price(price_reference, shipping_cost)
[
[15, 0.75],
[25, 0.775],
[50, 0.8],
... etc ...
].each do |price_point, shipping_modifier|
if price_reference < price_point
return price_reference - shipping_cost * shipping_modifier
end
end
return price_reference - shipping_cost * 0.95
end
def price_modifier(brand)
if brand.rating == high
1.1
elsif brand.rating == low
0.9
else
1
end
end
price_original = price_modifier(brand) * suggest_price(price_reference, product_database.shipping_cost)
I'm not suggesting that's perfect code but it's a way you could approach it. You'd probably want the price points and modifiers in a constant.
You could apply some OOP. In the Brand class you could use some .case statements to make it more readable...
def prices
case self.rating
when 'high"
case
when price_reference < 15
(price_reference - product_database.shipping_cost * 0.75) * 1.1
when price_reference < 25
(price_reference - product_database.shipping_cost * 0.775) * 1.1
when price_reference < 50
(price_reference - product_database.shipping_cost * 0.8) * 1.1
etc...
when 'low'
case
etc...
else
etc...
end
end
A nested case statement is a bit easier to read. Also you are doing a lot of unneeded variable reassignment. Remember that Ruby always returns the last statement it evaluates. So when you call the method on a member of the Brand class you can use it like this:
brand = Brand.new(rating: 'high', price_reference: '25')
#let's assume product_database.shipping_cost = 5
brand.prices
#returns the value 23.2375 which came from (25 - 5 * 0.775) * 1.1
There are many ways to do this. You could abstract out a lot more of your code by creating some hash dictionaries to contain the values of each formula and then do variable substitution to call them. Try to thing in terms of objects and methods. Everything in Ruby is an object of some sort and you can create methods to call on those objects that call other methods and use other objects.

Rails survey calculating results and storing results in DB

Having trouble with the controller of my survey (Waterusage). It has 30+ variables collected from a form and those inputs need to be saved to the waterusage db and used to calculate a final score, also saved in the database.
class Waterusage < ApplicationRecord
belongs_to :user
end
class WaterusagesController < ApplicationController
def new
#waterusage = Waterusage.new
end
def create
#user = User.find(params[:user_id])
_showerTotal = :average_shower * :shower_flow_rate * :household_size
_bathTotal = :bath_rate * :bath_multiplier * 35
_bathroomSinkTotal = :bathroom_sink_usage * :bathroom_sink_flow_rate * :household_size
_toiletTotal = :mellow * :low_flow_toilet * :household_size
_kitchenTotal = :kitchen_sink_usage * :kitchen_sink_flow_rate
_dishwashingTotal = :dishwasher_rate * :dishwasher_multiplier * :dishwasher_method
_laundryTotal = :laundry_rate * :laundry_method * :laundry_multiplier
_homeUsage = _showerTotal + _bathTotal + _bathroomSinkTotal + _toiletTotal + _kitchenTotal + _dishwashingTotal + _laundryTotal + :greywater
_lawnTotal = :lawn_rate * :lawn_multiplier * :lawn_size * :xeriscaping
_swimmingTotal = (:swimming_pool / 365) + (:swimming_months * 1000 / 365
_carwashTotal = :carwash_rate * :carwash_multiplier * :carwash_method
_outsideUsage = _lawnTotal + _swimmingTotal + _carwashTotal
_drivingTotal = 0.735 * :miles
_powerTotal = :statewater * :percent_statewater / 100
_indirectTotal = :shopping + :paper_recycling + :plastic_recycling + :can_recycling + :textile_recycling + :diet + (200 * :pet_cost / 30)
:household_total = _homeUsage + _outsideUsage + _drivingTotal + _powerTotal + _indirectTotal
:individual_total = :household_total / :household_size
#waterusage = #user.waterusage.create(waterusage_params)
redirect_to user_path(#user)
end
def destroy
#user = User.find(params[:user_id])
#waterusage = #user.waterusage.find(params[:id])
#waterusage.destroy
redirect_to user_path(#user)
end
private
def waterusage_params
params.require(:waterusage).permit(:household_size, :average_shower,
:shower_flow_rate, :bath_rate, :bath_multiplier, :bathroom_sink_usage,
:bathroom_sink_flow_rate, :mellow, :low_flow_toilet, :kitchen_sink_usage,
:kitchen_sink_flow_rate, :dishwasher_rate, :dishwasher_multiplier,
:dishwasher_method, :laundry_rate, :laundry_multiplier, :laundry_method,
:greywater, :lawn_rate, :lawn_multiplier, :lawn_size, :xeriscaping,
:swimming_pool, :swimming_months, :carwash_rate, :carwash_multiplier,
:carwash_method, :miles, :statewater, :percent_statewater, :shopping,
:paper_recycling, :plastic_recycling, :can_recycling, :textile_recycling,
:diet, :pet_cost, :individual_total, :household_total)
end
end
Is there a better way I can be doing this? Currently there are errors on the lines that are working to sum subtotals. (ie. :household_total = _homeUsage + _outsideUsage + _drivingTotal + _powerTotal + _indirectTotal
)
Also I'm not sure if I am properly connecting the user info to the survey schema
You don't want to do that math in the controller. Skinny controllers, fat models. Aside from that, one reason it's failing is that the syntax is incorrect. A symbol (:hello_world) can't be assigned a value nor does it contain one. Less importantly, while it's not illegal to have an underscore prefixed local variable, that is not the convention in Ruby. Neither is camelcase. You want hello_world rather than helloWorld. Anyway...
Assumption: You have a requirement that the totals must be persisted. They cannot be calculated values.
You want to move those calculations to the model. And instead of assigning a ton of variables, use methods. That way you can easily unit test them.
What's missing here: Validations in the model that ensure that all expected attribute values are present. The controller should handle an invalid Waterusage instance on create, too. This code is untested and is just for illustrative purposes.
class Waterusage < ApplicationRecord
belongs_to user
before_validation :calculate_totals
def calculate_totals
self.household_total = get_household_total
self.individual_total = get_individual_total
end
def get_household_total
home_usage + outside_usage + driving_total + power_total + indirect_total
end
def get_individual_total
household_total / household_size
end
def home_usage
shower_total + bath_total + bathroom_sink_total + toilet_total + kitchen_total + dishwashing_total + laundry_total + greywater
end
def outside_usage
lawn_total + swimming_total + carwash_total
end
def driving_total
0.735 * miles
end
def power_total
statewater * percent_statewater / 100
end
def indirect_total
shopping + paper_recycling + plastic_recycling + can_recycling + textile_recycling + diet + (200 * pet_cost / 30)
end
def shower_total
average_shower * shower_flow_rate * household_size
end
def bath_total
bath_rate * bath_multiplier * 35
end
def bathroom_sink_total
bathroom_sink_usage * bathroom_sink_flow_rate * household_size
end
def toilet_total
mellow * low_flow_toilet * household_size
end
def kitchen_total
kitchen_sink_usage * kitchen_sink_flow_rate
end
def dishwashing_total
dishwasher_rate * dishwasher_multiplier * dishwasher_method
end
def laundry_total
laundry_rate * laundry_method * laundry_multiplier
end
def lawn_total
lawn_rate * lawn_multiplier * lawn_size * xeriscaping
end
def swimming_total
(swimming_pool / 365) + (swimming_months * 1000 / 365)
end
def carwash_total
carwash_rate * carwash_multiplier * carwash_method
end
end
class WaterusagesController < ApplicationController
...
def create
#user = User.find(params[:user_id])
#waterusage = #user.waterusage.create(waterusage_params)
redirect_to user_path(#user)
end
...
end
First of all prefix every ':' inside of create with a 'params[' and suffix ']', then change every '_' with a '#'.
It would be like this:
_powerTotal = :statewater * :percent_statewater / 100
turns into
#powerTotal = params[:statewater].to_i * params[:percent_statewater].to_i /100
Like that,
:individual_total = :household_total / :household_size
turns into
#individual_total = params[:household_total].to_i / params[:household_size].to_i
Also you're doing nothing with your calculations, they are just floating around, as it is, you can't even invoke them from your view.
If you want it to save on your waterusage object that relates to an user the individual_total attributes it would be;
#waterusage = #user.waterusage.create(waterusage_params, individual_total: #individual_total).

Resources