How to simplify my model code? - ruby-on-rails

I am new to Rails and I wonder if there's any way to simplify this code from my model:
class Item < ActiveRecord::Base
def subtotal
if price and quantity
price * quantity
end
end
def vat_rate
if price and quantity
0.19
end
end
def total_vat
if price and quantity
subtotal * vat_rate
end
end
end
As far as I know *before_filter* does not work within models?

I'd do:
class Item < ActiveRecord::Base
VAT_RATE = 0.19
def subtotal
(price || 0) * (quantity || 0)
end
def total_vat
subtotal * VAT_RATE
end
end

Personally I would override the getter methods for price and quantity so that they return zero when not set, which allows your other methods to return valid results when no values are set rather than checking that they are and returning nil.
Additionally, creating a method to provide the VAT rate seems a little overkill for what should be a constant. If it isn't a constant then it should probably be stored in the DB so that it can be modified.
Here's a modification of your model based on my thoughts:
class Item < ActiveRecord::Base
VAT_RATE = 0.19
def price
self.price || 0
end
def quantity
self.quantity || 0
end
def subtotal
price * quantity
end
def total_vat
subtotal * VAT_RATE
end
end

Related

How to list in order of the point

I want to list users in order of points. But I didn't migrate point column, I define the point in the model like below.
class User < ApplicationRecord
def my_point(user)
point = user.posts.count * 10 + user.followers.count * 2
end
end
How can I arrange users in the view?
Remember that you can reference self to reference the instance, removing the argument from your method.
class User < ApplicationRecord
def points
(self.posts.count * 10) + (self.followers.count * 2)
end
end
#users = User.includes(:posts, :followers).all.sort_by { |user| user.points }

Rails association scope by method with aggregate

I'm trying to retrieve association records that are dependent on their association records' attributes. Below are the (abridged) models.
class Holding
belongs_to :user
has_many :transactions
def amount
transactions.reduce(0) { |m, t| t.buy? ? m + t.amount : m - t.amount }
end
class << self
def without_empty
includes(:transactions).select { |h| h.amount.positive? }
end
end
class Transaction
belongs_to :holding
attributes :action, :amount
def buy?
action == ACTION_BUY
end
end
The problem is my without_empty method returns an array, which prevents me from using my pagination.
Is there a way to rewrite Holding#amount and Holding#without_empty to function more efficiently with ActiveRecord/SQL?
Here's what I ended up using:
def amount
transactions.sum("CASE WHEN action = '#{Transaction::ACTION_BUY}' THEN amount ELSE (amount * -1) END")END")
end
def without_empty
joins(:transactions).group(:id).having("SUM(CASE WHEN transactions.action = '#{Transaction::ACTION_BUY}' THEN transactions.amount ELSE (transactions.amount * -1) END) > 0")
end

method containing calculation - Rails 4

i just want to calculate the total sum of all active events that users have paid to attend. If you could advise me i could be grateful as i am very unsure. Many thanks
event.rb
has_many :payments
payment
belongs_to :event
in the event.rb i tried the below method but no success
def self.active_events
active_events = live_events.open_events
active_events.all.each do |event|
event.price * event.payments.count
end
end
You can do this simply in following way,
total = 0
Event.live_events.open_events.find_each { |e| total += e.price * e.payments.count }
In Event.rb place it in a method with meaningful name.
This will work for you.
def self.total_price_for_active_events
total = 0
Event.live_events.open_events.find_each { |e| total += e.price * e.payments.count }
total
end
Most optimized way
def self.total_price_for_active_events
Event.live_events.open_events.joins(:payments).sum("events.price")
end
You're off too a good start! Unfortunately, what you have there is only the beginning; you're generating an array that contains the total sum for each event. All that remains is to add them together:
def self.active_events
active_events = live_events.open_events
costs = active_events.all.each do |event|
event.price * event.payments.count
end
costs.reduce(0) do |sum,x|
sum + x
end
end
You could also get real fancy and simply use:
costs.reduce(0, :+)

Counting percentage of true elements in ruby

class Constituency < ActiveRecord::Base
has_many :votes
end
class Vote <ActiveRecord::Base
belongs_to :constituency
end
I have a database with votes and each vote has boolean attribute "valid_vote".
What I want to receive how many votes are in each constituency (in percent), but counting only valid votes. So 100% is all votes, where valid_vote == true.
Any ideas how should I write it?
To get all the percentage of valid votes to all votes run:
all_votes_count = constituency.votes.count
valid_votes_count = constituency.votes.where(valid_vote: true).count
if all_votes_count > 0
percent = valid_notes_count / all_votes_count
else
puts "no votes"
end
You can do it with a single SQL query like this:
h = votes.group(:valid_vote).count
percentage = 100.0 * h[true] / h.values.sum rescue 0

Ruby on Rails: Avoiding infinite loop when calling .save during an update

I have an order model that has_many :items. Each item has item.price for the cost of said item. I want to add up all of the item prices in the order for a order.total_price. Right now I'm doing that with
after_save :update_total_price, :if => "self.saved.nil? "
def update_total_price
self.total_price = Item.find(item_ids).inject(0){|sum,item| sum + (item.price * item.amount) } #amount is how many items there are
self.saved = 1
self.save if self.saved
end
This works just fine the first time that I put in the info, but if I try to edit the order, the total_price doesn't get updated because update_total_price doesn't get called because self.saved is not nil.
What can I do to make it so that updating the model will update it, but won't keep on doing an infinite loop of calling .save?
Why not have the update_total_price NOT save the data again.
just set the value in before_update:
before_save :update_total_price
def update_total_price
self.total_price = items.find(:all).inject(0){|sum,item| sum + (item.price * item.amount) }
end
after_save :update_total_price
def update_total_price
self.total_price = find_total_price
self.save_without_callbacks
end
def find_total_price
Item.find(item_ids).inject(0){|sum,item| sum + (item.price * item.amount)
end

Resources