How to prepend to order clause with scope - ruby-on-rails

Is there any way to prepend to the existing order section of an active record query?
I the following associations defined on my Location model:
class Location < ActiveRecord::Base
has_many :location_parking_locations, -> { by_votes }
has_many :parking_locations, through: :location_parking_locations
end
In the LocationParkingLocation model, the by_votes scope is defined:
class LocationParkingLocation < ActiveRecord::Base
belongs_to :location
belongs_to :parking_location
scope :by_votes, -> { order("upvotes - downvotes ASC, upvotes + downvotes DESC, id ASC") }
end
I would like to add a scope to the ParkingLocation model that adds an additional scope to the query, but I want that scope to be prepended to the existing order section of the query. The scope looks like this:
class ParkingLocation < ActiveRecord::Base
has_many :location_parking_locations
has_many :locations, through: :location_parking_locations
scope :with_pending, -> { order(creation_pending: :desc) }
end
My hope is to call location.parking_locations.with_pending, and get back a collection of parking_locations, ordered by votes, but with any pending parking locations at the beginning of the collection. Is this possible?

Since a scope is a lambda, you can use arguments, for example:
scope :your_scope, -> (sort_order = :desc) { order(name: sort_order) }
Just set it up as you need and call it with a (possibly optional) argument:
your_model.your_scope(:desc)

Related

has_many through => find not matching records

I want to be able to find unpopulated hives, but don't find any solution.
Can you help me please ?
The goal is to be able to do Hive.unpopulated
The main problem is the most_recent, butins ok for me to work with a raw SQL, but I don't find the right query.
Here are my classes :
class Hive < ApplicationRecord
has_many :moves, dependent: :destroy
has_many :yards, through: :moves
has_many :populations, -> { where(:most_recent => true) }
has_many :colonies, through: :populations
validates :name, uniqueness: true
def hive_with_colony
"#{name} (colony #{if self.colonies.count > 0 then self.colonies.last.id end})"
end
def self.populated
Hive.joins(:populations)
end
def self.unpopulated
end
end
class Population < ApplicationRecord
belongs_to :hive
belongs_to :colony
after_create :mark_most_recent
before_create :mark_end
class Colony < ApplicationRecord
has_many :populations, -> { where(:most_recent => true) }
has_many :hives, through: :populations
has_many :visits
has_many :varroas
has_many :most_recents_populations, -> { where(:most_recent => true) }, :class_name => 'Population'
scope :last_population_completed, -> { joins(:populations).where('populations.most_recent=?', true)}
I think you can do a simple query to select Hives which are not in populated list, so:
def self.unpopulated
where.not(id: populated.select(:id))
end
Another option is a LEFT OUTER JOIN and picking the lines that have no population id set on the right side.
def self.unpopulated
left_outer_joins(:populations).where(populations: { id: nil })
end
It depends on your data if Thanh's version (which compares a potentially huge list of ids) or this version (which makes a sightly more complex join but doesn't need to compare against a list of ids) is more performant.

Scopes and associations not working

I'm trying to return records where the association is either present or not:
I tried these scopes:
class Booking < ActiveRecord::Base
has_one :availability
scope :with_availability, -> {where{availability.not_eq nil}}
scope :without_availability, -> {where{availability.eq nil}}
end
Try this:
class Booking < ActiveRecord::Base
has_one :availability
scope :with_availability, -> { joins{availability} }
scope :without_availability, -> { joins{availability.outer}.where{availability.id.eq nil} }
end
Use instance methods instead
def with_availability
availability.present?
end
def without_availability
availability.blank?
end
I know there is better way but this should work as well:
class Booking < ActiveRecord::Base
has_one :availability
scope :with_availability, -> {where(id: Availability.pluck(:booking_id))}
scope :without_availability, -> {where.not(id: Availability.pluck(:booking_id))}
end
Also I tried to reproduce the solution by the link below but I didn't manage to do this (but it could be helpful):
Rails - has_one relationship : scopes for associated and non-associated objects

ActiveRecord sort_by model's association

I want to use a where clause to retrieve all the correct ItemAttributes, then sort by the column order in Static.
Relationships:
class ItemAttribute < ActiveRecord::Base
belongs_to :static, primary_key:"name", foreign_key:"name"
end
class Static < ActiveRecord::Base
has_many :item_attributes, :foreign_key => 'name', :primary_key => 'name'
end
Code that doesn't quite do it...
#items = ItemAttribute.where(level:5)
#sorted = #items.sort_by(&:static.order)
I like using scopes and keeping specifics of each class in it's class, for something like this I could add a scope to Static model,
class Static < ActiveRecord::Base
scope :sort_by_order, -> { order(order: :desc) } # or asc if you want
end
Then use that scope in the query
#sorted_items = ItemAttribute.joins(:static).where(level: 5).merge(Static.sort_by_order)
#sorted_items = ItemAttribute.include(:static).where(level:5).order('statics.order')

scope and an instance method

I have a model
class User < ActiveRecord::Base
has_many :articles
def good_publisher?
self.articles.size > 50
end
scope :good_publishers, -> where { ...???? }
end
How do I express the scope through good_publisher?? Something like
scope :good_publishers, -> where { x.good_publisher? }
The scope has to be a SQL statement, your best bet here is to use a counter cache for the association.
At the Article class, include this:
belongs_to :user, counter_cache: true # you should already have this belongs_to, all that needs to happen is include the counter_cache
Then, create a migration that includes the articles_count field to user:
add_column :users, :articles_count
Once you have this, the scope can be written as:
scope :good_publishers, where("articles_count > 50")

Ruby / Rails - Can I use a joined table's scope(or class method) as part of my WHERE clause?

I want to grab all the categories that contain purchaseable products.
class Product < ActiveRecord::Base
belongs_to :category
scope :purchaseable, where(:available => true)
end
class Category < ActiveRecord::Base
has_many :products
scope :with_purchaseable_products, ?????
end
So, I'm trying to define :with_purchaseable_products. This works:
scope :with_purchaseable_products, joins(:products).where("products.available is true").group(:id).having('count(products.id) > 0')
But that's not very DRY. Is there any way to apply my :purchaseable scope to products in my :with_purchaseable_products scope?
Thanks.
You should use the merge method
class Category < ActiveRecord::Base
has_many :products
scope :with_purchaseable_products, joins(:products).merge(Product.purchaseable).group(:id).having('count(products.id) > 0')
end
Read more on http://asciicasts.com/episodes/215-advanced-queries-in-rails-3

Resources