I have two sub models, called: Service and Product that inherits from ProductBase. And I have another model to consume it. Acquire that have many AcquireBasket. Check out my code:
product_base.rb:
class ProductBase < ActiveRecord::Base
extend ::EnumerateIt
include Searchable
self.table_name = 'products'
end
product.rb:
class Product < ProductBase
default_scope { where(kind: ProductKind::PRODUCT) }
def initialize(attributes = {})
super(attributes)
self.kind = ProductKind::PRODUCT
self.status = ProductStatus::DRAFT
end
end
service.rb:
class Service < ProductBase
default_scope { where(kind: ProductKind::SERVICE) }
def initialize(attributes = {})
super(attributes)
self.kind = ProductKind::SERVICE
self.status = ProductStatus::DRAFT
end
end
acquire_basket.rb:
class AcquireBasket < ActiveRecord::Base
extend ::EnumerateIt
belongs_to :acquire
belongs_to :product
end
In some part of my project, I get a list (acquire baskets) of both models, Service and Product. And I need to check if I have services inside of it.
My code to check was:
def services_in?(acquire)
acquire.baskets.map(&:product).detect(&:service?)
end
The code works, ONLY if I pass services first, and products after!! Or if I have only one of them.
You should be able to utilize the descendents method to iterate over all of the subclasses
I can't find the answer in blog post around the world, so I will share with you:
class AcquireBasket < ActiveRecord::Base
extend ::EnumerateIt
belongs_to :acquire
belongs_to :product, class_name: 'ProductBase'
end
The problem was, when I try to find (lazily) in a ActiveRecord::Relation, Rails lookup (I think) to just Product model. And It can't find other type models inside of it. So using this typo I put it to work.
Related
Have an app where current_user can be a Client or and Admin. A Client can have many Accounts and an AccountStatement belongs to both the Account and the Client.
However there are some auto generated statements that we don't want to show the clients. I'd like to add a model scope of some kind that would look something like
def account_statements
if current_user.is_a?(Client)
super.where(auto_generated: false)
else
super
end
end
so if I ran .account_statements on a valid instance of Account or Client it would only return a subset of the statements if the current_user is a Client, but all of them if the current_user is an Admin. Is there any way to do this?
Just give each class its own method:
class Admin < ApplicationRecord
...
def account_statements
AccountStatement.all
end
end
class Client < ApplicationRecord
...
def account_statements
AccountStatement.where(auto_generated: false)
end
end
If you need Client to have a defined relationship to AccountStatement, things get a little more tricky:
class Client < ApplicationRecord
has_many :account_statements
# this method will supersede the relationship
def account_statements
...
end
end
In which case, you can either:
use a different name for the method
use this cool relationship-with-scope trick to do something like this:
class Client < ApplicationRecord
has_many :account_statements, -> { without_auto_generated }
end
class AccountStatement < ApplicationRecord
belongs_to :client
belongs_to :account
scope :without_auto_generated, -> { where(auto_generated: false) }
end
An application I'm working on, is trying to use the concept of polymorphism without using polymorphism.
class User
has_many :notes
end
class Customer
has_many :notes
end
class Note
belongs_to :user
belongs_to :customer
end
Inherently we have two columns on notes: user_id and customer_id, now the bad thing here is it's possible for a note to now have a customer_id and a user_id at the same time, which I don't want.
I know a simple/better approach out of this is to make the notes table polymorphic, but there are some restrictions, preventing me from doing that right now.
I'd like to know if there are some custom ways of overriding these associations to ensure that when one is assigned, the other is unassigned.
Here are the ones I've tried:
def user_id=(id)
super
write_attribute('customer_id', nil)
end
def customer_id=(id)
super
write_attribute('user_id', nil)
end
This doesn't work when using:
note.customer=customer or
note.update(customer: customer)
but works when using:
note.update(customer_id: 12)
I basically need one that would work for both cases, without having to write 4 methods:
def user_id=(id)
end
def customer_id=(id)
end
def customer=(id)
end
def user=(id)
end
I would rather use ActiveRecord callbacks to achieve such results.
class Note
belongs_to :user
belongs_to :customer
before_save :correct_assignment
# ... your code ...
private
def correct_assignment
if user_changed?
self.customer = nil
elsif customer_changed?
self.user = nil
end
end
end
I have made the models users and schools, and one of the schools has the name Harvard. I want to be able to return all Users who's school is Harvard. This section of code is in the user.rb file.
def self.harvard_students
return User.where.School(name: 'Harvard')
end
I would do that like this:
# in models/user.rb
scope :harvard_students, -> { joins(:school).where(schools: { name: 'Harvard' }) }
Or as a class method:
def self.harvard_students
joins(:school).where(schools: { name: 'Harvard' })
end
used like this:
User.harvard_students
Assuming your relationships are setup like the below then this should work. I'd advise against a hard coded harvard_students model unless you are really sure you want to treat those students differently.
class School < ActiveRecord::Base
has_many :students
end
class User < ActiveRecord::Base
belongs_to :school
def self.harvard_students
School.find_by(name: "Harvard").users
end
end
#....
User.harvard_students
I'm trying to call a class method (currently a scope) that uses an attribute from its parent (or belongs_to) model, but can't seem to get it working right.
My models:
class Venue < ActiveRecord::Base
attr_accessible :address
has_many :events, :dependent => :destroy
end
class Event < ActiveRecord::Base
belongs_to :venue
scope :is_near, lambda {|city| self(Venue.address).near(city, 20, :units => :km)}
end
I know the syntax is wrong, but I think that illustrates what I'm intending to do. I want to get the address of the venue and call another method on it. I need the scope in the Event class so I can chain other scopes together.
Appreciate any ideas.
Since #address is not a class method but an instance method, you won't be able to do what you want by using a scope.
If you want to get all the events within a 20km range of a venue, create these class methods in Venue instead:
class Venue < ActiveRecord::Base
def self.events_near_city(city)
venues_near_city(city).map(&:events).flatten
end
private
def self.venues_near_city(city)
near(city, 20, :units => :km)
end
end
Then call it by using Venue.events_near_city(session[:city]) since, as you told me in chat, you're storing the city in the session.
As you've defined it above, address is not a class method - it's an instance method. You would have to have an instance of venue (like you do in your view) to call it.
Searching a bit more I found this page that answered the question in another way. This works better for me because it's simpler to call, and I can use it on various relations. In rails how can I delegate to a class method
class Venue < ActiveRecord::Base
attr_accessible :address
def self.is_near(city)
venues_near_city(city).map(&:events).flatten
end
private
def self.venues_near_city(city)
self.near(city, 20, :units => :km)
end
end
class Event < ActiveRecord::Base
belongs_to :venue
class << self
def is_near(*args, &block)
Venue.is_near(*args, &block)
end
end
end
And I call it with event.is_near(session[:city])
I'd like to extract out logic from the controllers to somewhere that it can be more DRY. What's the best way of handling something like the following in Rails?
For instance, as opposed to having the query in the controllers, I could place it in the model:
class SmoothieBlender < ActiveRecord::Base
belongs_to :user
def self.get_blenders_for_user(user)
self.where(["user_id = ?", user.id])
end
end
Or would it be better to create a module as a service layer and include that in each model that uses it?
module BlenderUser
def get_blenders_for_user(user)
SmoothieBlender.where(["user_id = ?", user.id])
end
end
class SmoothieBlender < ActiveRecord::Base
include BlenderUser
belongs_to :user
end
class User < ActiveRecord::Base
include BlenderUser
has_many :smoothie_blenders
end
Or just make it a full blown service class that's accessible from the User and Blender controller? Where would you put this class?
class BlenderService
def self.get_blenders_for_user(user)
SmoothieBlender.where(["user_id = ?", user.id])
end
end
I'm new to Ruby and Rails, so if this is a silly question/syntax is incorrect, forgive me. Thanks in advance!
I'd create a named_scope (I think it's just scope in Rails 3)
class SmoothieBlender < ActiveRecord::Base
belongs_to :user
scope :for_user, lambda { |user_id|
where("user_id = ?", user_id)
}
end
This way you can call
SmoothieBlender.for_user(user.id)