ActiveRecord associations and scopes oh my - ruby-on-rails

I'm trying to figure out how to pull data from multiple tables and making sense of it.
I have events, occurrences and venues tables.
Here are the models/associations so far:
models/event.rb:
class Event < ActiveRecord::Base
has_many :occurences, :dependent => :destroy
belongs_to :price
belongs_to :venue
validates_presence_of :venue
end
models/venue.rb:
class Venue < ActiveRecord::Base
has_many :events, :dependent => :destroy
end
models/occurence.rb:
class Occurence < ActiveRecord::Base
belongs_to :event
scope :today, lambda { where("occurence.datetime_start <= ? AND datetime_end >= ?", Time.now.end_of_day, Time.now) }
scope :this_week, lambda { where("occurence.datetime_start <= ? AND datetime_end <= ?", Time.now.end_of_day, Time.now.at_end_of_week) }
scope :next_week, lambda { where("occurence.datetime_start > ? AND datetime_end < ?", Time.now.at_end_of_week, Time.now.at_end_of_week + 1.weeks) }
scope :this_month, lambda { where("occurence.datetime_start <= ? AND datetime_end >= ?", Time.now.end_of_day, Time.now.at_end_of_month) }
scope :next_month, lambda { where("occurence.datetime_start >= ? AND datetime_end <= ?", Time.now.at_beginning_of_month + 1.months, Time.now.at_end_of_month + 1.months) }
end
What I'd like to show in my view/events/index.html.erb is a table of all events happening today and I like to do something like:
<% #events.today.each do |event| %>
and display all the events that match the .today scope.
I tried moving the scope to the event model to no avail. Should I move the scopes to the event model? So far my searches have lead me to
"Using scope to return results within multiple DateTime ranges in ActiveRecord"
and
"Multiple scope in rails 3.0" but I can't really make anything out of it.
Can anyone point me in the right direction? Should I create a new function in my event model?

class Event < ActiveRecord::Base
has_many :occurences, :dependent => :destroy
belongs_to :price
belongs_to :venue
validates_presence_of :venue
scope :today, lambda {
joins(:occurences).where("datetime_start <= ? AND datetime_end >= ?", Time.now.end_of_day, Time.now)
}
end

If you want your syntax to work like:
#events.today
Then the scope would have to be in the Event model.
If you're OK with:
#event.occurences.today
then you're OK now, i.e.:
#events = Event.all
#events.each do |ev|
ev.occurences.today.each do |oc|
end
end

Related

how to get unvoted post count in Ruby on Rails

I am using Thumbs_up gem for creating vote function. I have 3 tables - Post, User and Vote where Post is acts_as_voteable and User is acts_as_voter.
Model Post.rb
class Post < ActiveRecord::Base
attr_accessible :title, :content, :user_type
acts_as_voteable
validates_presence_of :title,:content
default_scope order: 'posts.created_at DESC'
end
Model Vote.rb
class Vote < ActiveRecord::Base
scope :for_voter, lambda { |*args| where(["voter_id = ? AND voter_type = ?", args.first.id, args.first.class.base_class.name]) }
scope :for_voteable, lambda { |*args| where(["voteable_id = ? AND voteable_type = ?", args.first.id, args.first.class.base_class.name]) }
scope :recent, lambda { |*args| where(["created_at > ?", (args.first || 2.weeks.ago)]) }
scope :descending, order("created_at DESC")
belongs_to :voteable, :polymorphic => true
belongs_to :voter, :polymorphic => true
attr_accessible :vote, :voter, :voteable
end
Model User.rb
class User < ActiveRecord::Base
attr_accessible :name, :email, :password, :password_confirmation
has_secure_password
acts_as_voter
has_many :posts, dependent: :destroy
end
Now I want to count posts number which are NOT VOTED. I am trying to do like this..
<%= Post.joins(:votes).where("dont_know_how_to_write_condition").count %>
Any help?? Thanks in advance
I am not sure if this will work in sqlite, it should on postgresql though.
def self.not_voted
joins("left outer join votes on votes.voteable_id = posts.id").
where("votes.id is null")
end
Then:
Post.not_voted.count
There are ways to do that condition in SQL with NOT EXISTS but they are very heavy queries. If this si going to be an action facing the public or performed multiple times, it might be too heavy to do it like that. I would recommend you to denormalize the tables and include a count of votes on the post. Just use an observer on vote creation and deletion to update the vote count for the post. The query then would be like
Post.where('votecount = 0')
This query is much lighter and scalable than the previous one

Scope Lambda wrong number of arguments

Can anyone please have a look what I'm doing wrong?
It's Rails 3:
class Ad < ActiveRecord::Base
belongs_to :postingtemplate
scope :active, (lambda do |ad|
Item.exists?(:postingtemplate => ad.postingtemplate_id)
end)
end
It's a scope inside Ad model, and is supposed to return all ads, for which Item exists where item.postingtemplate == ad.postingtemplate_id
UPDATE
Broke it into two scopes and it worked :)
class Ad < ActiveRecord::Base
belongs_to :postingtemplate
scope :active, where(:postingtemplate_id => Postingtemplate.active)
end
class Postingtemplate < ActiveRecord::Base
has_many :ads
scope :active, where(:id => Item.all.collect{|x| x.postingtemplate}.uniq)
end
if anyone knows a better way - feel free to tell
You can do it with a join
scope :active, lambda { |ad| joins(:postingtemplate => :items).where(:postingtemplate => {:postingtemplate_id => ad.postingtemplate_id}) }
Maybe this will work too:
scope :active, lambda { |ad| joins(:postingtemplate => :items).where(:postingtemplate => ad.postingtemplate) }

Can I define a query in Rails model?

Here's what I have:
module EventDependencyProperties
def start_date
shows.order('show_date ASC').first.show_date
end
def end_date
shows.order('show_date DESC').first.show_date
end
def is_future_show?
end_date >= Date.today
end
end
class Event < ActiveRecord::Base
include EventDependencyProperties
has_many :shows
has_and_belongs_to_many :users
end
class Show < ActiveRecord::Base
belongs_to :event
end
I have bits of code elsewhere using the is_future_show? method. What I would like to do is have a method in the module mixin to return "future shows" using a query that has the same criteria as the is_future_show? method. How would I go about achieving this? I'm very much a newbie to Rails but tainted by knowledge of other languages and frameworks.
Cheers,
Dany.
You can put the query into a scope:
class Show < ActiveRecord::Base
scope :future, lambda { where("show_date > ?", Date.today) }
end
Call it like this:
my_event.shows.future
Edit: Ah I see. To return all events with a show in the future:
Event.joins(:shows).where("shows.show_date > ?", Date.today)
agains this can be scoped:
class Event
scope :future, lambda { joins(:shows).where("shows.show_date > ?", Date.today) }
end
On a side note, I'm not sure about the setup of your models, especially the use of the mixin. Here's what I do:
class Show < ActiveRecord::Base
belongs_to :event
# use default_scope so shows are ordered by date by default
default_scope order("show_date ASC")
end
class Event < ActiveRecord::Base
has_many :shows
has_and_belongs_to_many :users
scope :future, lambda { joins(:shows).where("shows.show_date > ?", Date.today) }
def start_date
shows.first.show_date
end
def end_date
shows.last.show_date
end
def ends_in_future?
end_date > Date.today
end
end
also it would be cleaner if the show_date column for the Show model was just called date (so you could just write show.date rather that show.show_date).

How to apply conditions when accessing records using a has_many through relationship in Rails?

I have the following models:
class Campaign < ActiveRecord::Base
has_many :campaign_keywords
has_many :leads, :through => :campaign_keywords
end
class CampaignKeyword < ActiveRecord::Base
belongs_to :campaign
has_many :leads
end
class Lead < ActiveRecord::Base
belongs_to :campaign_keyword
end
I am trying to build a function in the "Campaign" model that will only return leads which belong to a given campaign_keyword.
My attempt is:
def leads?(campaign_keyword_id = -1)
self.leads :conditions => ['campaign_keyword_id = #{campaign_keyword_id}']
end
but this does not work, the conditions are ignored.
Can you see a solution to this?
Create a named_scope for your Lead model, like so:
class Lead < ActiveRecord::Base
belongs_to :campaign_keyword
named_scope :with_keyword, lambda { |keyword| { :conditions => { :campaign_keyword => keyword } } }
end
Now, when you want to get leads for a particular campaign keyword, you would do so like this:
def leads_for_campaign(keyword)
self.leads.with_keyword(keyword)
end
This is much nicer and more re-usable, because the Lead model itself now knows how to find leads for a specific campaign.
For more of an idea of what you can do with named_scopes, check out http://apidock.com/rails/ActiveRecord/NamedScope/ClassMethods/named_scope
Try this:
def leads?(campaign_keyword_id = -1)
self.leads.all :conditions => ['campaign_keyword_id = #{campaign_keyword_id}']
end
I would rewrite your query as follows:
def leads?(campaign_keyword_id = -1)
self.leads.all :conditions => ['campaign_keyword_id = ?', campaign_keyword_id]
end
OR
self.leads.find_all_by_compaign_keyword_id(campaign_keyword_id)

Using named_scope with counts of child models

I have a simple parent object having many children. I'm trying to figure out how to use a named scope for bringing back just parents with specific numbers of children.
Is this possible?
class Foo < ActiveRecord::Base
has_many :bars
named_scope :with_no_bars, ... # count of bars == 0
named_scope :with_one_bar, ... # count of bars == 1
named_scope :with_more_than_one_bar, ... # count of bars > 1
end
class Bar < ActiveRecord::Base
belongs_to :foo
end
I'm hoping to do something like Foo.with_one_bar
I could write methods on the parent class for something like this, but I'd rather have the power of the named scope
class Foo < ActiveRecord::Base
has_many :bars
# I don't like having the number be part of the name, but you asked for it.
named_scope :with_one_bar, :joins => :bars, :group => "bars.foo_id", :having => "count(bars.foo_id) = 1"
# More generically...
named_scope :with_n_bars, lambda {|n| {:joins => :bars, :group => "bars.foo_id", :having => ["count(bars.foo_id) = ?", n]}}
named_scope :with_gt_n_bars, lambda {|n| {:joins => :bars, :group => "bars.foo_id", :having => ["count(bars.foo_id) > ?", n]}}
end
Called like so:
Foo.with_n_bars(2)
I would use the counter cache for this. Therefore you need the following migration:
class AddBarCount < ActiveRecord::Migration
def self.up
add_column :foos, :bars_count, :integer, :default => 0
Foo.reset_column_information
Foo.all.each do |p|
p.update_attribute :bars_count, p.bars.length
end
end
def self.down
remove_column :foos, :bars_count
end
end
Than you need too change you Bar model like this:
class Bar < ActiveRecord::Base
belongs_to :foo, :counter_cache => true
end
Now the count of bars is cached in the foo model, that will speed up your queries for the count of bars.
Your named_scopes then have too look like this:
#rails 2
named_scope :with_no_bars, :conditions => { :bars_count => 0 }
named_scope :with_one_bar, :conditions => { :bars_count => 1 }
named_scope :with_more_than_one_bar, :conditions => ["bars_count > 1"]
#rails 3 & ruby 1.9+
scope :with_no_bars, where(bars_count: 0)
scope :with_one_bar, where(bars_count: 1)
scope :with_more_than_on_bar, where("bars_count > 1")
#rails 4* & ruby 1.9+
scope :with_no_bars, -> { where(bars_count: 0) }
scope :with_one_bar, -> { where(bars_count: 1) }
scope :with_more_than_one_bar, -> { where("bars_count > 1") }
That way you can save time counting bars for each foo every time you make such a request.
I got this idea watching the railscast about counter cache: http://railscasts.com/episodes/23-counter-cache-column
* What's new in Active Record [Rails 4 Countdown to 2013]

Resources