Scopes and associations not working - ruby-on-rails

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

Related

Active Record eagerly load polymorphic association

I have the following models:
class User < ApplicationRecord
has_one :card, as: :cardable
scope :active, -> { where(active: true) }
end
class Organisation < ApplicationRecord
has_one :card, as: :cardable
scope :active, -> { where(active: true) }
end
class Card < ApplicationRecord
belongs_to :cardable, polymorphic: true
end
I want to find all the cards whose associated User or Organisation is active.
I thought the following would work:
Card.includes(:cardable).where(cardable: {active: true})
But this throws an error:
ActiveRecord::EagerLoadPolymorphicError: Cannot eagerly load the polymorphic association :cardable
Is what I'm trying to do even possible with ActiveRecord?
I've looked other questions with a similar title, but I am not sure the scenarios are similar enough to this one.
I think this should work:
cardable_ids = User.active.pluck(:id) + Organisation.active.pluck(:id)
Card.where(cardable_id: cardable_ids)

Rails 4 scope has_many

I have two models
class Portfolio < ActiveRecord::Base
has_many :project_types, dependent: :destroy
end
class ProjectType < ActiveRecord::Base
belongs_to :portfolio
end
ProjectType model has field ptype. It can be 'web' or 'mobile', etc.
How can I get all 'web' or 'mobile' portfolios using scopes?
Add an appropriate scope to your model:
# in models/project_type.rb
class ProjectType < ActiveRecord::Base
belongs_to :portfolio
scope :web, -> { where(ptype: 'web') }
end
To load portfolios with type web only use that scope with a join in the controller:
# in the controller
#web_portfolios = Portfolio.joins(:project_types).merge(ProjectType.web)
You can have explicit scopes for both, or you may have one scope that selects for you based on ptype, or you may metaprogram a scope for each unique ptype in the database. If you are adding a variety of different "ptypes" you can do any of the following which will give you a scope for handling any "ptypes":
scope :ptype, -> (ptype) { where(ptype: ptype) }
called as:
ProjectType.ptype('web')
or
class ProjectType < ActiveRecord::Base
self.pluck(:ptype).each do |ptype|
scope ptype.gsub(/\s+/,"_").downcase.to_sym, -> { where(ptype: ptype) }
end
end
I don't recommend this and I also don't recommend individual scopes for each "string" (e.g. ProjectType.web or ProjectType.mobile) in there. The best balance is to pass a string value into a scope which retrieves what you are looking for. Just my opinion, I'm sure others feel differently about it.
To be honest, I think the ptype field is ripe for an enumerator -- this will give some clarity in your code and define what that field actually expects as opposed to allowing any string to be placed in there at random. So something like:
class ProjectType < ActiveRecord::Base
enum ptype: [:web, :mobile]
scope :ptype, -> (ptype) { where(ptype: self.ptypes[ptype] }
end
and called like so:
ProjectType.ptype(:web)
I don't think any one of these solutions presented here or above is any more "right", except the meta-programming solution that's generating a scope for each string in the ptype field stored in the database.
Finally, here is a scope for your Portfolio:
class Portfolio < ActiveRecord::Base
scope :by_ptype, -> (ptype) { joins(:project_type).merge(ProjectType.ptype(ptype) }
end
And called like so:
Portfolio.by_ptype(:web)
You can do it this way
class Portfolio < ActiveRecord::Base
has_many :project_types, dependent: :destroy
scope :ptype, -> (p_type) { includes(:project_types).where(project_types: {ptype: p_type}) }
end
And you can call
Portfolio.ptype('web')
Following a Rails 4.1 approach I would consider my ptype field an enum and write this:
class ProjectType < ActiveRecord::Base
belongs_to :portfolio
enum ptype: { web: 'web', mobile: 'mobile' }
end
This will give you:
ProjectType.web
ProjectType.mobile
scopes to fetch objects based on ptype, but will give you also other useful methods like:
#projectType.web?
#projectType.web!
Please refer to docs: http://api.rubyonrails.org/v4.1/classes/ActiveRecord/Enum.html
In here you ask how to retrieve "web" Portfolio and I will suppose you mean "All Portfolio with at least a ProjectType of ptype 'Web'.
Then I would write something like:
Portfolio.joins(:project_types).merge(ProjectType.web)
see docs: http://apidock.com/rails/ActiveRecord/SpawnMethods/merge
In a final refinement you can then create a scope out of it:
class Portfolio
has_many :project_types
scope :web, -> { joins(:project_types).merge(ProjectType.web) }
end
and call just
Portfolio.web

How to prepend to order clause with scope

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)

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")

Rails 3 joining a related model with it's default scope

I'm trying to query across models with the following setup
Class Scorecard < AR::Base
default_scope where(:archived => false)
belongs_to :user
has_many :scorecard_metrics
end
Class ScorecardMetric < AR::Base
belongs_to :scorecard
end
Class User < AR::Base
has_many :scorecards
end
I am trying to query from scorecard metrics with a named scope that joins scorecard and I want it to include the default scope for scorecard, my current implementation (which works) looks like this
# on ScorecardMetric
scope :for_user, lambda {
|user| joins(:scorecard).
where("scorecards.user_id = ? and scorecards.archived = ?", user.id, false)
}
This just seems messy to me, is there any way to join and include the default scope of the joined association?
Looks like I found the answer I was looking for, I just did this
scope :for_user, lambda { |user| joins(:scorecard).where('scorecards.user_id = ?', user.id) & Scorecard.scoped }
which is much nicer without the duplicated logic

Resources