Rails query question - ruby-on-rails-3

I got a model called items, with a field called weeks. The content in weeks is as follows:
{2011=>["46", "47", "48", "49"]}
How can i do something like this:
Item.where(:week => week, :year => year)
When just passing one week example: 47 and year 2011
Thanks.

# Model
class Item < AR::Base
def self.with_week(weeek)
where("week LIKE (?)", "\"#{week}\"")
end
def self.with_year(year)
where("week LIKE (?)", "{#{year}=>")
end
end
usage
#items = Item.with_week(47).with_year(2011)

Related

Trying to define method that makes datetime entries by day of week

I have a datetime column in one of my tables (team_opps) called start_date.
I am trying to write methods in my model that allow me to classify them as Monday, Tuesday, etc... opportunities.
def self.friday_team_opps
where('WEEKDAY(start_date) = ?', 4)
end
In my view I am trying to call a .each on it.
<% TeamOpp.friday_team_opps.each do |team_opp| %>
<%= render team_opp, :team_opp => :team_opp %>
<% end %>
Error is:
SQLite3::SQLException: no such function: WEEKDAY: SELECT "team_opps".* FROM "team_opps" WHERE (WEEKDAY(start_date) = 4)
Thanks
First of all, you need to define the method on the TeamOpp class by defining the method as def self.friday_team_opps.
Moreover, you can't call methods on the column since it would require ActiveRecord to load all the data in your table and then call the Ruby method on that data. What you can do is use direct SQL functions, like for example MySQL's WEEKDAY (monday = 0, tuesday = 1, etc.):
def self.friday_team_opps
where("WEEKDAY(team_opps.created_at) = ?", 4)
end
In SQLite, you can use the strftime function (sunday = 0, monday = 1, etc.):
def self.friday_team_opps
where('strftime("%w", "created_at") = "?"', 5)
end
You define it as an instance method
def friday_team_opps
And it should be defined as a class method
def self.friday_team_opps
If you want to make it a instance function then you should use it like this
def friday_team_opps
return self.start_date.strftime("%A")
end
Subsequently you will have to modify your view like this :
<% TeamOpp.select("start_date").each do |team_opp| %>
<%= team_opp.friday_team_opps() %>
<% end %>

ActiveRecord execute raw transaction typecasting?

ActiveRecord::Base.connection.execute(sql)
The results are not typecasted so they are all strings as an example
ActiveRecord::Base.connection.execute(sql).entries
=> [{"id" => "1", "length" => "120", "content" => "something"},{"id" => "2", "length" => "200", "content" => "blahblah"}]
Is it possible to execute raw transactions in activerecord and return typecasted results?
Consider manifesting your SQL statement as a view, and creating a new record to interface with the view.
Here's a project where I'm backing AR with a view:
https://github.com/michaelkirk/household-account-mgmt/blob/develop/app/models/monthly_report.rb
class CreateMonthlyReports < ActiveRecord::Migration
def up
sql = <<-SQL
create view monthly_reports as
select date_part('year', created_at) as year, date_part('month', created_at) as month, sum(purchase_amount) as purchases_amount, sum(investment_amount) as investments_amount
from (
select * from transactions
left join
(select id as purchase_id, amount as purchase_amount from transactions where credit = false)
as purchases on transactions.id = purchases.purchase_id
left join
(select id as investment_id, amount as investment_amount from transactions where credit = true)
as investments on transactions.id = investments.investment_id)
as classified_transactions
group by year, month
order by year, month
SQL
execute(sql)
end
def down
sql = <<-SQL
drop view monthly_reports
SQL
execute(sql)
end
Then, since you've abstracted your complexity into a database view, which for all AR's intents/purposes works like a table your model and controller look completely vanilla.
class MonthlyReport < ActiveRecord::Base
MONTHS = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]
def time_period
"#{month} #{year}"
end
def month
MONTHS[self[:month] - 1]
end
def year
self[:year].to_i
end
end
Then you can do things like
class MonthlyReportsController < ApplicationController
def index
#monthly_reports = MonthlyReport.all
end
end
Note that because this is a DB view, you won't be able to do inserts. I'm not sure what would happen if you tried.
I think you are referring to ORM (Object relational mapping)
First of all, connection.execute will return a Mysql adapter where you can just iterate over the rows
You cant convert array of strings(the result you have) to ActiveRecord objects just like that ( I guess this is what you referred as typecasting)
What you can do is use find_by_sql.
Ex:
Blog.find_by_sql("select * from blog")
# => [#<Blog id: 1, name: "first blog", description: nil, record_status_id: 1>]
Using this method you can get ActiveRecord Objects fro raw SQL
Actually, you can get results cast either as ActiveRecord objects (see the find_by_sql method here) or as native Ruby types (see this StackOverflow answer).

How to list all times from 20:00 untill 24:00 in a collection and only list the times > Time.now

Im trying to populate a collection with Times like 20.00, 20.10, 20.20 ... 24:00. So in intervals of 10.minutes. But how to do this smartly and take into account the Time.now?
Only times that are > Time.now should be listed.
So if its 20.30 It should not show 20.10, 20.20,20.30
Example code
= f.input :order, :collection => ["20:00","20:10","20:20"... etc ["24:00"],
:default => 2,
:label => "orders,
:hint => "Select the time you want this order to be processed"
Some of the things Ive tried so far:
:collection => [(Time.now + 10.minutes).strftime("%I:%M%p").to_s]
and
#hours=(Time.now.minus_with_coercion(Time.now.midnight)/3600/2)
Any thoughts how to cleanly code this ? Thank you
Not sure to understand your problem but this could help :
Time.parse('20:00').to_datetime.step(Time.parse('23:59'), 10.minutes).to_a.map {|date| date.strftime("%I:%M%p")}
=> ["08:00PM", "08:10PM", "08:20PM", "08:30PM", "08:40PM", "08:50PM", "09:00PM", "09:10PM", "09:20PM", "09:30PM", "09:40PM", "09:50PM", "10:00PM", "10:10PM", "10:20PM", "10:30PM", "10:40PM", "10:50PM", "11:00PM", "11:10PM", "11:20PM", "11:30PM", "11:40PM", "11:50PM"]
After that, you could call the delete_if method to remove unwanted time.
Something like that :
Time.parse('20:00').to_datetime.step(Time.parse('23:59'), 10.minutes).to_a.delete_if {|date| date < DateTime.now.to_time}.map {|date| date.strftime("%I:%M%p")}

How to convert postgresql column into a float for a scoped search in rails

I am trying to search my postgresql db in rails. I followed the Railscasts #111 Advanced Search tutorial and it is working for the name and category of my items column in plain text. However, I want to set a min/max price on my search as well which is where I come into my problem. In my db my price is stored as a string in the format "AU $49.95". Can I convert this into a float on the fly in my scoped search? If so how? If not, what should I do?
Here is the code:
search.rb
class Search < ActiveRecord::Base
attr_accessible :keywords, :catagory, :minimum_price, :maximum_price
def items
#items ||= find_items
end
private
def find_items
scope = Item.scoped({})
scope = scope.scoped :conditions => ["to_tsvector('english', items.name) ## plainto_tsquery(?)", "%#{keywords}%"] unless keywords.blank?
scope = scope.scoped :conditions => ["items.price >= ?", "AU \$#{minimum_price.to_s}"] unless minimum_price.blank?
# scope = scope.scoped :conditions => ["items.price <= ?", "AU \$#{maximum_price.to_s}"] unless maximum_price.blank?
scope = scope.scoped :conditions => ["to_tsvector('english', items.catagory) ## ?", catagory] unless catagory.blank?
scope
end
end
searches_controller.rb
class SearchesController < ApplicationController
def new
#search = Search.new
end
def create
#search = Search.new(params[:search])
if #search.save
redirect_to #search, :notice => "Successfully created search."
else
render :action => 'new'
end
end
def show
#search = Search.find(params[:id])
end
end
Thanks for reading this far!
Use the data type numeric or money for exact numerical calculation without rounding errors - and sorting as a number (not as text).
Converting string literal to numeric should not be a performance problem at all.

Rails 3 Custom Validation

I'm attempting to created a custom validation that verifies a schedule's start_date and end_date do not overlap with another schedule
class Schedule < ActiveRecord::Base
#has many scheduleTimes (fk in scheduleTime)
has_many :scheduletimes, :inverse_of => :schedule
validate :dateOverlaps?
scope :active, lambda {
where('? between start_date and end_date', Date.today)
}
def dateOverlaps?
results = ActiveRecord::Base.connection.execute("Select (start_date::DATE, end_date::DATE) OVERLAPS ('#{self.start_date}'::DATE, '#{self.end_date}'::DATE) from schedules;")
errors.add_to_base("Date ranges cannot overlap with another schedule") if results.first["overlaps"] == 't'
end
however, this causes
NoMethodError: undefined method `add_to_base'
I have tried creating a custom validator and using the private validate method to no avail. Could someone shine some light on this for me?
Try replacing this:
errors.add_to_base("Date ranges cannot overlap with another schedule")
with this:
errors.add(:base, "Date ranges cannot overlap with another schedule")
Instead of:
errors.add_to_base
try using:
errors.add