Comparing time for nested associations - ruby

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.

Related

Refactor with Strategy Pattern. In Ruby

Heads up! In the below example, using a pattern is probably overkill... however, if I were extending this to count genres, count the members in a given band, count the number of fans, count the number of venues played, count the number of records sold, count the number of downloads for a specific song etc... it seems like there could be a ton of stuff to count.
The Goal:
To create a new function that chooses the correct counting function based on the input.
The Example:
class Genre < ActiveRecord::Base
has_many :songs
has_many :artists, through: :songs
def song_count
self.songs.length
end
def artist_count
self.artists.length
end
end
P.S. If you are also a curious about this question, you may find this other question (unfortunately answered in C#) to be helpful as a supplemental context. Strategy or Command pattern? ...
In Ruby you can implement a strategy pattern quite easily using an (optional) block (assuming it's still unused).
class Genre < ActiveRecord::Base
has_many :songs
has_many :artists, through: :songs
def song_count(&strategy)
count_using_strategy(songs, &strategy)
end
def artist_count(&strategy)
count_using_strategy(artists, &strategy)
end
private
def count_using_strategy(collection, &strategy)
strategy ||= ->(collection) { collection.size }
strategy.call(collection)
end
end
The above code defaults to using the size strategy. If you ever want to use a specific strategy in a specific scenario you can simply provide the strategy alongside the call.
genre = Genre.last
genre.song_count # get the song_count using the default #size strategy
# or provide a custom stratigy
genre.song_count { |songs| songs.count } # get the song_count using #count
genre.song_count { |songs| songs.length } # get the song_count using #length
If you need to re-use some strategies more often you could save them in a constant or variable:
LENGTH_STRATEGY = ->(collection) { collection.length }
genre.artist_count(&LENGTH_STRATEGY)
Or create a specific class for them if they are more complicated (currently overkill):
class CollectionStrategy
def self.to_proc # called when providing the class as a block argument
->(collection) { new(collection).call }
end
attr_reader :collection
def initialize(collection)
#collection = collection
end
end
class LengthStrategy < CollectionStrategy
def call
collection.length
end
end
genre.artist_count(&LengthStrategy)

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')

rails3 custom validation overlapping dates error

Building a validator that has to check multiple siblings who belong to the same (option) parent.
class Optionrate < ActiveRecord::Base
belongs_to :option
attr_accessible :from, :to, :option_id
validates_presence_of :from, :to
validate :not_overlap
scope :overlaps, ->(from, to) do
where "((from <= ?) and (to >= ?))", to, from
end
def overlaps?
overlaps.exists?
end
def overlaps
siblings.overlaps from, to
end
def not_overlap
errors.add(:key, t('overlap_message')) if overlaps?
end
def siblings
Optionrate.where('option_id = ?', option_id).all
end
is generating an error: "undefined method `overlaps' for []:Array" referring to statement
siblings.overlaps from, to
The fact that siblings is plural makes me assume it is expecting an array, so that's an oddity.
[Another was that the where statement was not accepting *where('option_id = ?', params[:option_id])* whence the record has yet to be created as the validation has not completed]
Please try to run the code after removing .all from Optionrate.where('option_id = ?', option_id).all because when you are using .Where then there is no need to use .all method.
Or
Take a look on following url for reference
http://guides.rubyonrails.org/3_2_release_notes.html#active-record

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.

How to retrieve related records in Ruby on Rails?

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.

Resources