I am trying to check the day and hours of a business to determine if its open or not.
I have this code in my supplier partial
%li
= link_to shopping_supplier_path(supplier) do
= content_tag :div, class: 'grid-block' do
= content_tag :div, supplier.image_available, :class => 'small-3 grid-content'
= content_tag :div, :class => 'small-9 grid-content' do
%h4= supplier.name.truncate(30).titlecase
= content_tag :div, class: 'grid-block' do
.small-3.grid-content 9/10
.small-3.grid-content $$
.small-6.grid-content.text-right
- SupplierTradingHour.open.includes(:supplier).each do |hour|
= "#{hour.supplier.name} is open! It closes at #{hour.close_time}."
TradingHour Model
class SupplierTradingHour < ActiveRecord::Base
belongs_to :supplier
scope :open, lambda { |day, time| { :conditions => ["self.weekday = ? AND self.open_time >= ? AND self.close_time < ?", day, time, time] } }
end
As an alternative implementation, you might want to consider using the following gem for business hour related functionalities:
https://github.com/Intrepidd/working_hours
You are calling the open scope without arguments: SupplierTradingHour.open but your :open scope is a lambda expression with two arguments |day, time| which one of the reasons why your code is failing.
Your SQL logical expression is also not correct: open_time <= ? AND close_time > ? (note the operators are the opposite of yours)
In your case, for a DIY implementation (without using third party gems) I would suggest doing this:
#app/models/trading_hour.rb
class TradingHour < ActiveRecord::Base
belongs_to :supplier
scope :open_now, -> (day, time) { where("weekday = ? AND open_time <= ? AND close_time > ?", day, time, time) }
end
then in your supplier model:
#app/models/supplier.rb
class Supplier < ActiveRecord::Base
has_many :trading_hours
def opening_status day, hour
today_trading_hours = trading_hours.open_now(day, hour)
if today_trading_hours.size > 0
"Opened until #{today_trading_hours.first.close_time}H"
else
"Closed"
end
end
end
Then from your view call
#if today is day 1
#and current hour is 12
supplier.opening_status(1, 12)
setting a scope is fine but I would probably just define my #suppliers with something like:
#suppliers = Supplier.where("weekday = ? AND open_time > ? AND closing_time < ?", day, time, time)
Another option, if you need both sides of it in different circumstances, would be to write an instance method is_open?
Related
I'm creating a small lesson app, with a different lesson for each day. I'm wanting to show the current days lesson only on the index, and can't figure out how. I googled and found some info that came close, but still couldn't fix the problem. I only have one controller, DaysController. No user controller.
For my model(day.rb) I've tried this
class Day < ActiveRecord::Base
validates :description, :date, :link_to, :presence => true
scope :created_on, lambda {|date| {:conditions => ['created_at >= ? AND created_at <= ?', date.beginning_of_day, date.end_of_day]}}
def self.today
self.created_on(Date.today)
end
end
And for my index I've tried these
<% #day.created_on(Date.today) %>
<% #day.today %>
any advice??
If I understand correctly and for simplicity sake is this essentially what you are trying to achieve?
Controller:
def index
#days = Day.all
end
View (index.html.erb):
<% #days.each do |day| %>
<% if day.created_at == Date.today %>
<%= day.field_name %>
<% end %>
I would change the scope to just use the Date object and I assume that you want your condition to use the newer syntax:
scope :created_on, ->(date) { where(created_at: date) }
Then the Day.today method should work without any change.
You can do the following:
class Day < ActiveRecord::Base
scope :today, lambda { where('CAST(created_at AS date) = ?', Date.today) }
And use it like this:
#days = Day.today
# returns a list of `Day` records where the `created_at`'s date is equal to today's date
In my limited knowledge I dont know how to write this without passing the volunteer as an argument to the status method. Which normally wouldnt be an issue, except when I need all irregular volunteers. Then I have to loop through Volunteers.all and evaluate each against the status method. I feel like the overhead here is too great.
Class Volunteer < ActiveRecord::Base
has_many :reports
def status(volunteer)
case
when volunteer.reports.nought.where(:report_month => Report_range.months(6)).count > 5
return 'inactive'
when volunteer.reports.nought.where(:report_month => Report_range.months(6)).count >= 3
return 'irregular'
else
return 'active'
end
end
end
class Report < ActiveRecord::Base
belongs_to :volunteer
scope :nought, -> {where("hours IS NULL") || where("hours: 0")}
end
module Report_range
def self.months(range)
Volunteer.first.reports.order("report_month desc").limit(range).pluck(:report_month)
end
end
Thanks in advance for helping a noob!
I'd do something like this:
class Volunteer < ActiveRecord::Base
has_many :reports
def status
if number_of_recent_nought_reports > 5
'inactice'
elsif number_of_recent_nought_reports >= 3
'irregular'
else
'active'
end
end
private
def number_of_recent_nought_reports
# You could move the where call to scope in your `Report` class.
#recent_nought_reports ||= reports.nought.where(:report_month => Report_range.months(6)).count
end
end
def status(volunteer)
case
when volunteer.reports.nought.where(:report_month => Report_range.months(6)).count > 5
return 'inactive'
when volunteer.reports.nought.where(:report_month => Report_range.months(6)).count >= 3
return 'irregular'
else
return 'active'
end
end
You don't need to pass the volunteer because you are calling the method status on an instance of Volunteer that already has direct access to reports. So the above becomes:
def status
case
when reports.nought.where(:report_month => Report_range.months(6)).count > 5
return 'inactive'
when reports.nought.where(:report_month => Report_range.months(6)).count >= 3
return 'irregular'
else
return 'active'
end
end
Additionally, you are running the count query two times which is not optimal. I suggest:
def status
number_of_reports = reports.nought.where(:report_month => Report_range.months(6)).count
case
when number_of_reports > 5
return 'inactive'
when number_of_reports >= 3
return 'irregular'
else
return 'active'
end
end
Finally the case returns the matching value so you don't need the returns.
def status
number_of_reports = reports.nought.where(:report_month => Report_range.months(6)).count
case
when number_of_reports > 5
'inactive'
when number_of_reports >= 3
'irregular'
else
'active'
end
end
There is more but that is a start.
You could also specify it as using #Magnuss' code as a base point
class Volunteer < ActiveRecord::Base
has_many :reports
STATUSES = {(0..2) => 'active', (3..5) => 'irregular'}
def status
STATUSES.find(->{['inactive']}){|range,_| range === number_of_recent_nought_reports}.pop
end
private
def number_of_recent_nought_reports
# You could move the where call to scope in your `Report` class.
#recent_nought_reports ||= reports.nought.last_n_months(6).count
end
end
class Report < ActiveRecord::Base
belongs_to :volunteer
scope :nought, -> {where("hours IS NULL") || where("hours: 0")}
scope :last_n_months, ->(months){ where(:report_month => Report_range.months(months)) }
end
This allows more flexibility in my opinion for adding more statuses by just redefining STATUSES as you see fit.
What this does is find the first result where the number_of_recent_nought_reports is in a range defined in STATUSES in the event that it falls outside of any of the ranges ['inactive'] is returned then pop pulls out just the status.
I also moved the report_month scope to Report as last_n_months.
You could write:
def status(volunteer)
case volunteer.reports
.nought
.where(:report_month => Report_range.months(6))
.count
when (6..Float::INFINITY) then 'inactive'
when (3..5) then 'irregular'
else 'active'
end
For example in my Car model i have such fields:
color, price, year
and in form partial i generate form with all this fields. But how to code such logic:
user could enter color and year and i must find with this conditions, user could enter just year or all fields in same time...
And how to write where condition? I could write something like:
if params[:color].present?
car = Car.where(color: params[:color])
end
if params[:color].present? && params[:year].present?
car = Car.where(color: params[:color], year: params[:year])
end
and so over....
But this is very ugly solution, i'm new to rails, and want to know: how is better to solve my problem?
Check out the has_scope gem: https://github.com/plataformatec/has_scope
It really simplifies a lot of this:
class Graduation < ActiveRecord::Base
scope :featured, -> { where(:featured => true) }
scope :by_degree, -> degree { where(:degree => degree) }
scope :by_period, -> started_at, ended_at { where("started_at = ? AND ended_at = ?", started_at, ended_at) }
end
class GraduationsController < ApplicationController
has_scope :featured, :type => :boolean
has_scope :by_degree
has_scope :by_period, :using => [:started_at, :ended_at], :type => :hash
def index
#graduations = apply_scopes(Graduation).all
end
end
Thats it from the controller side
I would turn those into scopes on your Car model:
scope :by_color, lambda { |color| where(:color => color)}
scope :by_year, lambda { |year| where(:year => year)}
and in your controller you would just conditionally chain them like this:
def index
#cars = Car.all
#cars = #cars.by_color(params[:color]) if params[:color].present?
#cars = #cars.by_year(params[:year]) if params[:year].present?
end
user_params = [:color, :year, :price]
cars = self
user_params.each do |p|
cars = cars.where(p: params[p]) if params[p].present?
end
The typical (naive, but simple) way I would do this is with a generic search method in my model, eg.
class Car < ActiveRecord::Base
# Just pass params directly in
def self.search(params)
# By default we return all cars
cars = all
if params[:color].present?
cars = cars.where(color: params[:color])
end
if params[:price1].present? && params[:price2].present?
cars = cars.where('price between ? and ?', params[:price1], params[:price2])
end
# insert more fields here
cars
end
end
You can easily keep chaining wheres onto the query like this, and Rails will just AND them all together in the SQL. Then you can just call it with Car.search(params).
I think you could use params.permit
my_where_params = params.permit(:color, :price, :year).select {|k,v| v.present?}
car = Car.where(my_where_params)
EDIT: I think this only works in rails 4, not sure what version you're using.
EDIT #2 excerpt from site I linked to:
Using permit won't mind if the permitted attribute is missing
params = ActionController::Parameters.new(username: "john", password: "secret")
params.permit(:username, :password, :foobar)
# => { "username"=>"john", "password"=>"secret"}
as you can see, foobar isn't inside the new hash.
EDIT #3 added select block to where_params as it was pointed out in the comments that empty form fields would trigger an empty element to be created in the params hash.
I have a record called Feeds that contains the field 'last_visited' and 'last_modified', both are timestamps.
I'm trying to render a list in a view of alls Feeds where last_modified > last_visited.
I currently have this:
Controller
#feeds = #user.feeds
#feeds_hot = #feeds.where(['#feeds.last_modified > ?', #feeds.last_visited])
Have a feeling I'm way off track here. Should I also being using a model class to do this?
Any helps is greatly appreciated.
Edit:
Here's my model
class Feed < ActiveRecord::Base
attr_accessible :feed_id, :feed_url, :last_modified, :title, :url, :last_visited, :user_id
belongs_to :user
scope :hottest, lambda {where('last_modified > ?', :last_visited)}
def fetch_feed!
feed = Feedzirra::Feed.fetch_and_parse(feed_url) # probably want some eror handling here
self.title = feed.title
self.url = feed.url
self.last_modified = feed.last_modified
self.last_visited = feed.last_modified
self #or nil if you like
end
def self.check_for_update(feed)
fetched_feed = Feedzirra::Feed.fetch_and_parse(feed.feed_url)
entry = fetched_feed.entries.first
feed.last_modified = entry.published
end
def update_visit_date!
date = Time.now
update_attribute(:last_visited, date)
self
end
end
Edit
Updated code
Controller
def home
#user = current_user
#feeds = #user.feeds
#feeds_hot = #feeds.hottest
end
Model
attr_accessible :feed_id, :feed_url, :last_modified, :title, :url, :last_visited, :user_id
belongs_to :user
scope :hottest, lambda {where("'last_modified' > ?", 'last_visited')}
View
%ol.feeds.feeds_hot
- #feeds_hot.each do |feed|
%li.feed.hot[feed]
= render :partial => 'feeds/feed_link', :locals => {:feed => feed}
Unfortunately it's still not rendering the hot feeds in the view when I have a feed with the following data:
Last Modified 2013-06-14 23:49:07 UTC
Last Visited 2013-06-14 23:47:55 UTC
Last Modified is a few hours > than Last Visited
If you're looking to get a list of all feeds that have been modified more recently than the most recent visit on any feed, the following will work:
last_visit = Feed.order("last_visited").last.last_visited
#feeds = #user.feeds
#hot_feeds = Feed.where("last_modified > ?", last_visited)
EDIT:
Based on your comments and a re-reading of your question, the code posted above will not accomplish what you're trying to do. Since you're trying to get a list of all invites where each has been modified since it was last visited, you'll want to create a model scope to do the lookup with ActiveRecord. The following code should work:
# app/models/feed.rb
class Feed < ActiveRecord::Base
scope :hottest, lambda {where('last_modified > ?', :last_visited)}
end
Then, you can run the following in a console, controller, or view:
#user.invites.hottest
#=> array of all the user's invites that have been modified more recently than they have been viewed
In the following example, how can the amount_below_limit instance method access the argument to the max_weight scope?
# Model
class Elephant < ActiveRecord::Base
scope :max_weight, lambda { |limit| where('weight <= ?', limit) }
def amount_below_limit
max_weight = # How can I see 1000 from here?
max_weight - weight
end
end
# Controller
#elephants = Elephant.max_weight(1000)
# View
<% #elephants.each do |elephant| %>
<%= elephant.amount_below_limit %>
<% end %>
You can do it in sql: class Elephant < ActiveRecord::Base
scope :max_weight, lambda { |limit| where('weight <= ?', limit).
select("elephants.*, #{limit}-weight as amount_below_limit") }
def amount_below_limit
read_attribute(:amount_below_limit) || -1
end
end
limit is scoped to the block. it doesn't exist outside the lambda
Thanks for your suggestions.
In the end, I refactored my code to make the parameter to max_weight an instance variable, which I then passed on to the amount_below_weight method.