How to retrieve related records in Ruby on Rails? - ruby

I am trying to find a user's overdue invoices:
class User < ActiveRecord::Base
def overdue_invoices
invoices.where("overdue = ?", true)
end
end
class Invoice < ActiveRecord::Base
def overdue
balance > 0 && Date.today > due_date
end
end
The problem seems to be that overdue is a method on the Invoice model, not a database column.
How can I retrieve those records anyway? And does that even make sense or would it be better to store those true and false values in the database?

You should create an equivalent class method or scope for overdue on Invoice:
class Invoice < ActiveRecord::Base
def self.overdue
where('balance > 0').where('? > due_date', Date.today)
end
end
Then you can call overdue on an Invoice relation:
class User < ActiveRecord::Base
def overdue_invoices
invoices.overdue
end
end
Note that I’m assuming due_date is a database column, if it’s not, you cannot use this method—however, you may be able to replace it with SQL to calculate the due date from data that is in columns.

Related

Comparing time for nested associations

I'm in the process of building an application that'll accept a specific date from a user and which will then provide a list of events that have occurred prior to that date. While my code works if I'm looking for a specific value, I'm unable to perform a search when a particular date is passed in. Essentially i'd like to view all of the children elements that occurred previous to that particular date.
My Investments models is this:
class Investment < ApplicationRecord
has_many :transactions
def count
final_quanity = 0
self.transactions.each do |transaction|
final_quanity+= transaction.quantity
end
final_quanity
end
def average_price
total_spent = 0
self.transactions.each do |transaction|
total_spent += transaction.price
end
total_spent
end
end
My transactions model is this:
class Transaction < ApplicationRecord
belongs_to :investment
end
Investment controller
class InvestmentsController < ApplicationController
def index
if params[:date]
# #investments = Investment.includes(:transactions).where(transactions: {quantity:10})
#investments = Investment.includes(:transactions).where("date > ?", params[:date])
else
#investments = Investment.all
end
render json: #investments
end
end
I'd like to only return the specific transactions that occurred before the date entered, by I'm having difficulty returning with a conditional. As you can see from the blocked out code, I'm able to successfully return entries that have a specific value. What's the appropriate way to complete the active record query with a conditional?
What's strange is that the following works:
#transactions = Transaction.where("date < ?", date)
While this doesn't:
#investments = Investment.includes(:transactions).where("date > ?", date)
Instead I get an error message that these's no such column as "date".
Essentially what's the best way to perform this action:
#investments = Investment.includes(:transactions).where( :transactions => {:price => 100})
except determining if a value is greater than or less than instead of just matching.

how to get single latest record from a view and test it in console

I kinda have 2 questions. I have following model and method to get the latest record from view. but when i try to test in console i get error undefined method or variable vCustomerDetails why i am getting the error?
Also, how do i select only one column from view?
SELECT TOP 1 HasConditionFlag FROM vCustomerDetails
WHERE vCustomerDetails.UserID = #user_id
ORDER BY EntryDate DESC
Model
module Customer
class CustomerUsage < ActiveRecord::Base
self.table_name = 'vCustomerDetails'
def self.has_condition_flag(user_id)
vCustomerDetails
.where("vCustomerDetails.UserID = #{user_id}")
.order('vCustomerDetails.EntryDate DESC')
.last
end
end
end
this one worked
def self.has_condition_flag(user_id)
CustomerUsage
.select("vCustomerDetails.HasConditionFlag")
.where("vCustomerDetails.UserID = #{user_id}")
.order('vCustomerDetails.EntryDate DESC')
.first
Remove vCustomerDetails
module Customer
class CustomerUsage < ActiveRecord::Base
self.table_name = 'vCustomerDetails'
def self.has_condition_flag(user_id)
where("vCustomerDetails.UserID = #{user_id}")
.order('vCustomerDetails.EntryDate DESC')
.last
end
end
end
to select a limited number of columns use
.select('HasConditionFlag')

get dynamically updated stats monthly rails

I have Rails4 application.
class Developer
has_many :client_applications
class ClientApplication
belongs_to :developer
Among ClientApplication's attributes there is cost.
What I can do is to get developer's total earning by:
def total_earning
self.client_applications.where(status: "Closed").map{|c| c.cost}.inject(&:+).to_i
end
What I want is to have a table of statistics. Table should have month column and total_earning column with corresponding to this month value.
I only want to get columns where total_earning is nonzero value (so that if developer earned $0 in February, this month is not present in table).
I can hardcode the month into total_earning method's query (which is dumb and one-off way), I would like to know if there (I am sure there is) a way to do it nice and smooth.
Would appreciated for any hints!
You could do a callback each time a ClientApplication record is saved to update your stats table like this:
class Developer
has_many :client_applications
Add this:
class ClientApplication
belongs_to :developer
after_save :update_statistics
def update_statistics
if self.status == "Closed"
stats = Statistic.find_or_create_by(developer: self.developer, month: self.created_at)
stats.total_earning += self.cost
stats.save
end
end
class Statisitc
has_many :developers
has_many :client_applications
So the solution I've come up with is following(ActiveAdmin table):
table do
Hash[*developer.client_applications.load.where(status: "Closed").group_by{|a| [a.updated_at.month, a.updated_at.year]}.map{|a,b| ["#{Date::MONTHNAMES[a[0]]} #{a[1]}", b.map(&:cost).map(&:to_i).inject(&:+)]}.reject{|entry| entry[1] == [0, nil]}.flatten].each_pair do |k, v|
tr do
td k
td number_to_currency(v, unit: "$")
end
end
end

Transactions and validation in Rails4

I have a problem with Rails 4 and validation. Let's consider this model:
# has integer attribute 'order'
class Item < ActiveRecord::Base
belongs_to :parent,
validate :orders_must_be_sequence
def orders_must_be_sequence
orders = []
parent.items.each do |i|
orders << i.order
end
orders.sort!
errors.add :order, "is not in a decent order" if orders != (0..orders.length - 1).to_a
end
end
There is a parent model Parent which has_many :items. The idea is, that the items for every parent are ordered; thus for every parent, the n associated items have to have the order attributes 0 to n-1. This is checked with the orders_must_be_in_sequence-validation.
Now for the problem:
In order to reorder the items, the item have to be saved. My idea was, to do that in a transaction like:
ActiveRecord::Base.transaction do
item1.order = 2
item2.order = 3
item3.order = 1
item1.save
item2.save
item3.save
end
But then all save fails due to the failing validation. Also validate: false seems not to be the answer, since I still had to invoke save with validating at the last time in the transaction.
So I want to do a couple of saves in a transaction and I want that all saves are validated at the commit-time. How to do this?
You could shift the validation logic for being in order into the parent itself, for example:
ActiveRecord::Base.transaction do
parent.item1.order = 2
parent.item2.order = 3
parent.item3.order = 1
parent.save
end
class Item < ActiveRecord::Base
belongs_to :parent
end
class Parent < ActiveRecord::Base
has_many :item
validate :orders_must_be_sequence
def orders_must_be_sequence
orders = []
parent.items.each do |i|
orders << i.order
end
orders.sort!
errors.add :order, "is not in a decent order" if orders != (0..orders.length - 1).to_a
end
end
Other than this - I'm curious why you don't just use "sort!" or "order" (to retrieve using ORDER BY SQL), which will guarantee the order, obviating the need to order validation.

activerecord single table inheritance on ruby 3

I have the following classes mapped with STI:
class Employee < ActiveRecord::Base
end
class StudentEmployee < Employee
# I'd like to keep university only to StudentEmployee...
end
#Just to make this example easier to understand, not using migrations
ActiveRecord::Schema.define do
create_table :employees do |table|
table.column :name, :string
table.column :salary, :integer
table.column :university, :string # Only Students
end
end
emp = Employee.create(:name=>"Joe",:salary=>20000,:university=>"UCLA")
I'd like to prevent the setting of the university field for Employees, but allow it for StudentEmployees. I tried to use attr_protected, but it will only prevent mass setting:
class Employee < ActiveRecord::Base
attr_protected :university
end
class StudentEmployee < Employee
attr_accessible :university
end
#This time, UCLA will not be assigned here
emp = Employee.create(:name=>"Joe",:salary=>20000,:university=>"UCLA")
emp.university = "UCLA" # but this will assign university to any student...
emp.save
puts "only Students should have univesities, but this guy has one..."+emp.university.to_s
The problem here is that it will insert in the database a university for simple employees.
Another problem is that I think it would be better to say in the StudentEmployee class that university is an attribute, and not to say in the Employee that university "is not" a a visible attribute... it just goes in the inverse direction of natural abstraction.
Thanks.
I would try something like this:
class Employee < ActiveRecord::Base
validate :no_university, unless: lambda { |e| e.type === "StudentEmployee" }
def no_university
errors.add :university, "must be empty" unless university.nil?
end
end
It isn't the prettiest, but it should to work.

Resources