Method in rails model not working - ruby-on-rails

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

Related

Rails: filter association based on current_user class

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

Iterate over STI list in rails 4

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.

Using sunspot to search down model hierarchy

Example:
I have the following:
class Person < ActiveRecord::Base
has_many :educations
end
class Education < ActiveRecord::Base
belongs_to :school
belongs_to :degree
belongs_to :major
end
class School < ActiveRecord::Base
has_many :educations
# has a :name
end
I want to be able to return all people who went to a specific school so in my PeopleController#index I have
#search = Person.search do
keywords params[:query]
end
#people = #search.results
How do I create the searchable method on the Person model to reach down into school? Do I do something like this:
searchable do
text :school_names do
educations.map { |e| e.school.name }
end
end
which I would eventually have to do with each attribute on education (degree etc) or can I make a searchable method on Education and somehow "call" that from Person.searchable?
Thanks
It would be best if you keep the declaration of all the indexed fields for an specific model in the same place.
Also, you were doing a good job indexing :school_names, just do the same thing for the rest of the associations fields' that you want to index.

Active Relation: Retrieving records through an association?

I have the following models:
class User < ActiveRecord::Base
has_many :survey_takings
end
class SurveyTaking < ActiveRecord::Base
belongs_to :survey
def self.surveys_taken # must return surveys, not survey_takings
where(:state => 'completed').map(&:survey)
end
def self.last_survey_taken
surveys_taken.maximum(:position) # that's Survey#position
end
end
The goal is to be able to call #user.survey_takings.last_survey_taken from a controller. (That's contrived, but go with it; the general goal is to be able to call class methods on #user.survey_takings that can use relations on the associated surveys.)
In its current form, this code won't work; surveys_taken collapses the ActiveRelation into an array when I call .map(&:survey). Is there some way to instead return a relation for all the joined surveys? I can't just do this:
def self.surveys_taken
Survey.join(:survey_takings).where("survey_takings.state = 'completed'")
end
because #user.survey_takings.surveys_taken would join all the completed survey_takings, not just the completed survey_takings for #user.
I guess what I want is the equivalent of
class User < ActiveRecord::Base
has_many :survey_takings
has_many :surveys_taken, :through => :survey_takings, :source => :surveys
end
but I can't access that surveys_taken association from SurveyTaking.last_survey_taken.
If I'm understanding correctly you want to find completed surveys by a certain user? If so you can do:
Survey.join(:survey_takings).where("survey_takings.state = 'completed'", :user => #user)
Also it looks like instead of:
def self.surveys_taken
where(:state => 'completed').map(&:survey)
end
You may want to use scopes:
scope :surveys_taken, where(:state => 'completed')
I think what I'm looking for is this:
class SurveyTaking < ActiveRecord::Base
def self.surveys_taken
Survey.joins(:survey_takings).where("survey_takings.state = 'completed'").merge(self.scoped)
end
end
This way, SurveyTaking.surveys_taken returns surveys taken by anyone, but #user.survey_takings.surveys_taken returns surveys taken by #user. The key is merge(self.scoped).
Waiting for further comments before I accept..

Best Practice to abstract ActiveRecord model queries in Rails?

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)

Resources