Scope evaluate time in Rails 3 - ruby-on-rails-3

I'm writing a scope that should look for calls with a status of open and any call with a datetime greater than current time. I'm a bit fuzzy on the syntax, can someone help point me in the right direction.
Example that fails:
scope :scheduled_calls, where(:call_status => "open", :transfer_date > Time.now)

You need to use a lambda to evaluate the scope when it's called, as opposed to when the class is loaded.
scope :scheduled_calls, lambda { where(["call_status = ? and transfer_date > ?", "open", Time.now]) }

Try this:
scope :scheduled_calls, where("call_status = 'open' and transfer_date > ?", Time.now)

Related

Argument error in model scope

I'm trying to refactor the Companies_Controller#index method to encompass less logic by moving most of the query into a scope, company_search_params.
What is the best way to pass the param to the model scope? I'm getting an error thrown back, wrong number of arguments (given 0, expected 1). I'm relatively new to writing scopes and couldn't find much on passing arguments/conditions that was applicable in the Rails Guide.
Companies Controller
def index
params[:name_contains] ||= ''
Company.company_search_params(params[:name_contains])
#search = Company.company_search_params
#companies = #search.page(params[:page])
end
Company Model
scope :company_search_params, ->(name_contains){
where(
<<-SQL
"name LIKE :match OR subdomain LIKE :match", { match: "%#{name_contains}%" }
SQL
).where(is_archived: false).order(name: :asc)
}
Thanks for your help.
using named_scope sample and info
scope :named_scope, lambda {
|variable1, variable2|
where...
order...
}
#when you call it from your controller
Model.named_scope("value 1","value 2")
for your problem
in your company.rb
scope :company_search_params, lambda {
|name_contains|
where(
<<-SQL
"name LIKE :match OR subdomain LIKE :match", { match: "%#{name_contains}%" }
SQL
).where(is_archived: false).order(name: :asc)
}
company_controller.rb
def index
#search = Company.company_search_params(params[:name_contains])
#companies = #search.page(params[:page])
end

How to concatonate conditions with Active Record scope, Rails 4

I am trying to create a scope for an Active Record model using rails 4. The scope deals with 2 conditions which I am able to execute individually:
1) includes(:categories).for_category("proposal")
2) where(:is_published => true)
I need a scope that returns all records where either condition is met (not necessarily both). So something like:
scope :proposal, -> { includes(:categories).for_category("proposal") || where(:is_published => true)}
Thanks for any help.
**EDIT**
Per comment here is the definition of the for_category scope taken from https://github.com/comfy/comfortable-mexican-sofa/blob/b16bb14bec7bffee61949d72a5f2c9eca3c95bf8/lib/comfortable_mexican_sofa/extensions/is_categorized.rb
scope :for_category, lambda { |*categories|
if (categories = [categories].flatten.compact).present?
self.distinct.
joins(:categorizations => :category).
where('comfy_cms_categories.label' => categories)
end
}
You'll need to define a new scope which includes an sql "OR". I don't know what your "for_category" scope is doing but let's say it's something like
scope :for_category, ->(category_name) { where("categories.name" => category_name)}
Then you could make a new scope like this: note that the where arguments are a list of values rather than a hash (the hash form can only produce sql for = tests joined with AND)
scope :published_or_for_category ->(category_name){ includes(:categories).where("categories.name = ? OR proposals.is_published = ?", category_name, true) }
Based on Max's suggestions I got the following to work:
scope :published_or_for_category, ->(category_name){
includes(:categories).where(
"comfy_cms_categories.label = ? OR is_published = ?",
category_name, true
)
}
I might mention that the following seems to work as well:
scope :proposal, -> { includes(:categories).for_category("proposal") | where(:is_published => true) }
Is one better that the other?

Rails - Simplify WHERE condition IS NOT NULL

Working on a webpage I used the next line:
Model.select(:column).where("column IS NOT NULL")
I was wondering if there was a more Rails-ish way to do this, like using a hash for example
Model.select(:column).where(column: !nil)
The Squeel gem will allow you to use != nil type syntax, but natively rails will not.
Example: Model.where{column != nil}
I would prefer to use a scope as its more readable as well as its more manageable later (like merging with other scopes)
class Model < ActiveRecord::Base
scope :not_null, lambda { |column|
{:select => column,
:conditions => "#{column} NOT NULL"
}
}
end
then use
Model.not_null("column_name")
In the Rails 4 you can do this:
Model.select(:column).where.not(column: nil)

Rails 3 Time.now without the year?

I have a Rails 3 application that is a basic intranet for our company. I have an announcement controller which checks for any announcements that have been created with a scheduled date that matches the current date.
class Announcement < ActiveRecord::Base
scope :active, lambda {
where("starts_at <= ? AND ends_at >= ?", Time.now.utc, Time.now.utc)
}
scope :since, lambda { |hide_time|
where("updated_at > ? OR starts_at > ?", hide_time.utc, hide_time.utc) if hide_time
}
def self.display(hide_time)
active.since(hide_time)
end
end
However, most announcement will be deleted within a week or so of the scheduled end date. All other announcements are simple Happy Birthday messages to staff members. Due to the way we are using the announcement system, it seems sensible to only check against the day/month and not the year, as birthday messages etc. will be annual.
What would the simplest way to 'ignore' the year in my controller code?
UPDATE
I have updated the controller code to the below code, however, announcements no longer hide after the end datetime.
class Announcement < ActiveRecord::Base
scope :active, lambda {
where("day(starts_at) <= ?
AND month(starts_at) <= ?
AND day(ends_at) >= ?
AND month(ends_at) >= ?",
Time.now.utc.day,
Time.now.utc.month,
Time.now.utc.day,
Time.now.utc.month)
}
scope :since, lambda { |hide_time|
where("day(starts_at) > ?
AND month(starts_at) > ?",
hide_time.utc.day, hide_time.utc.month) if hide_time
}
def self.display(hide_time)
active.since(hide_time)
end
end
An example record:
22, 'Test Announcement', 'This is a test announcement, please ignore it. Seriously - stop reading.', '2012-06-25 13:40:00', '2012-06-25 13:55:00', '2012-06-25 13:47:23', '2012-06-25 13:52:15');
Even though I have set :exclude_year on the input select boxes for the datetime fields it still puts the current year in.
If you are using MySQL database you could use its day() and month() methods. I don't know if these methods can be found for other databases as well. So doing it this way may make your code become dependent on the database server.
scope :active, lambda {
where("day(starts_at) <= ?
AND month(starts_at) <= ?
AND day(ends_at) >= ?
AND month(ends_at) >= ?",
Time.now.utc.day,
Time.now.utc.month,
Time.now.utc.day,
Time.now.utc.month)
}
Couple things. You should be using AND not OR if I understand you correctly, you want the time to fall between those to items. 2nd thing is you need to specify that it is at the beginning of the day and the end of the day. Right now it's just being set to the now which doesn't really say the expanse of 24 hours very clearly :
class Announcement < ActiveRecord::Base
scope :active, lambda {
where('starts_at < ? AND ends_at > ?', Time.zone.now.end_of_day, Time.zone.now.beginning_of_day)
}
scope :since, lambda { |hide_time|
where('updated_at > ? AND starts_at > ?', Time.zone.now.end_of_day, Time.zone.now.beginning_of_day)
}

SQL OR in scopes in Rails 3 / Arel

I'm trying to create a Rails 3 model scope, which is, essentially:
scope :published, where(:published => true)
scope :viewable_by, lambda { |user| where(:user_id => user.id).or(published) }
The trouble is, I can't find a way to make this work.
I had hoped arel_table would work, but it complains it "Cannot visit ActiveRecord::Relation":
where(arel_table[:user_id].eq(user.id)).or(published)
This could be done by replicating the "published" scope as Arel but then I would be repeating code (and in my project the "published" scope is quite a bit more in depth).
The best I can come up with is this:
scope :viewable_by, lambda { |user| where("(user_id = ?) OR (#{published.where_values.collect(&:to_sql).join(" AND ")})", user.id) }
This is the shortest I could come up with after a bit of digging around:
scope :published, where(:published => true)
scope :viewable_by, lambda { |user| where("user_id = ? OR #{published.where_clauses.first}", user.id) }
(Of course it only works with a single where clause; use a join if there's more than one.)
I would recommend using a gem like MetaWhere that more clearly exposes some of the Arel underpinnings. Here's an example from the main page linked above that creates an OR query:
Article.where(:title.matches % 'Hello%' | :title.matches % 'Goodbye%').to_sql
=> SELECT "articles".* FROM "articles" WHERE (("articles"."title" LIKE 'Hello%'
OR "articles"."title" LIKE 'Goodbye%'))