How to find instance of an object in a collection within a collection in rails


How to find instance of an object in a collection within a collection in rails



I'm trying to write an if statement that will find an instance of an object in a collection within another collection...


House
has_many :occupants
Occupant
has_many :shirts
belongs_to :house
Shirt
belongs_to :occupant



So if I want check if any of the occupants of house own a white shirt, I want to do something like this:


<% if @house.occuptants.shirts.where(:color => 'white') %>



However when I do that I get an error:



undefined method `shirts' for #< Occupant::ActiveRecord_Associations_CollectionProxy



I believe because occupants is a collection in this case, but I'm not sure what the correct approach or syntax should be.




2 Answers
2



Something simpler and that will help you later for different use cases is to add more to the relationships:


class House
has_many :occupants
has_many :shirts, through: :occupants
end

class Occupant
has_many :shirts
belongs_to :house
scope :females, -> { where(...) } # This is homework for you: http://guides.rubyonrails.org/active_record_querying.html#scopes
end

class Shirt
belongs_to :occupant
end



If you have a House instance:then you can check for occupants with white shirts as below:


House


<% if @house.shirts.where(color: 'white').exists? %>



and to check for female occupants with white color shirts, do:


<% if @house.occupants.females.select { |o| o.shirts.where(color: 'white').exists? } %>





The condition of the if will always be true. where(color: 'white') cannot return a falsy value, it will always return an active record result wrapping 0 or more records. You need to use .exists? to return a boolean true/false.
– meagar
Jun 29 at 23:49



if


where(color: 'white')


.exists?





you are right, edited, thanks
– xploshioOn
Jun 29 at 23:51





This is helpful, but I've also got a similar scenario where I need to check just for females with white shirts, ie. I need to validate an attribute on occupant as well.... in this case would the decorator approach be necessary?
– phauwn
Jun 30 at 0:20





@phauwn i have updated the answer to include your further query. For next times, please try to include all your queries in the question itself. This answer was perfect in itself.
– Jagdeep Singh
Jun 30 at 7:08





Awesome @Jagdeep, don't worry, we are all here to help.
– xploshioOn
Jun 30 at 14:07



I would go for a presenter/decorator in this scenario.



Use draper to decorate the house object like:


# /app/decorators/house_decorator.rb
class HouseDecorator < Draper::Decorator
def count_occupants_with_white_shirts
object.occupants.joins(:shirts).where(shirts: { color: 'white' } ).count
end
end



Then in your view:


<% if @house.count_occupants_with_white_shirts > 0 %>



Hope that helps.



NOTE: If you don't want an additional dependency(Draper) you can also put that method inside you House model


House





This is not exactly decorating a model. It is more like adding a helper method to avoid calculations in the view. Should not go into the decorator. A good example of a decorator method is where you want to display a Time/Date object in pretty format.
– Jagdeep Singh
Jun 30 at 6:59


Time


Date





Also, you will have to call the method on @house.decorate.
– Jagdeep Singh
Jun 30 at 6:59


@house.decorate






By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Comments

Popular posts from this blog

paramiko-expect timeout is happening after executing the command

Export result set on Dbeaver to CSV

Opening a url is failing in Swift