Question

I have the models Dad, Mom and Kid. I have a boolean attribute on my Mom called is_online. Now I'm doing a pagination chain like this:

def show
  @dad = Dad.find(params[:dad_id])
  @kids =  @dad.kids.online.paginate(:page => params[:page])
end

Allowing me to see a list of the Dad's kids who's moms are online only.

My models are setup like this:

class Kid < ActiveRecord::Base
  belongs_to :dad
  belongs_to :mom

  scope :online, joins(:mom).where("moms.is_online = ?", true)
end

class Dad < ActiveRecord::Base
  has_many :kids
end

class Mom < ActiveRecord::Base
  has_many :kids
end

The problem I'm having is this doesn't return just this Dad's kids with online moms but instead returns ALL Dad's. How can I get the scope to be correct?

EDIT

SELECT DISTINCT "kids"."dad_id" FROM "kids"
 => [14, 25, 27, 8, 12, 17, 28, 1, 15, 10, 26, 11, 4, 18, 30, 16, 6, 19, 29, 2, 21, 3, 23, 31, 20, 5, 13, 22, 9, 7, 24]

Kid Load (1.5ms)  SELECT "kids".* FROM "kids" INNER JOIN "moms" ON "moms"."id" = "kids"."mom_id" WHERE (moms.is_online = 't')
 => #<ActiveRecord::Relation [
 #<Kid id: 7, dad_id: 1, mom_id: 5, created_at: "2014-04-28 16:10:48", updated_at: "2014-04-28 16:10:48">, 
 #<Kid id: 21, dad_id: 2, mom_id: 5, created_at: "2014-04-28 16:10:48", updated_at: "2014-04-28 16:10:48">, 
 #<Kid id: 35, dad_id: 3, mom_id: 5, created_at: "2014-04-28 16:10:48", updated_at: "2014-04-28 16:10:48">, 
 #<Kid id: 49, dad_id: 4, mom_id: 5, created_at: "2014-04-28 16:10:48", updated_at: "2014-04-28 16:10:48">, 
 #<Kid id: 63, dad_id: 5, mom_id: 5, created_at: "2014-04-28 16:10:48", updated_at: "2014-04-28 16:10:48">, 
 #<Kid id: 77, dad_id: 6, mom_id: 5, created_at: "2014-04-28 16:10:48", updated_at: "2014-04-28 16:10:48">, 
 #<Kid id: 91, dad_id: 7, mom_id: 5, created_at: "2014-04-28 16:10:48", updated_at: "2014-04-28 16:10:48">, 
 #<Kid id: 105, dad_id: 8, mom_id: 5, created_at: "2014-04-28 16:10:48", updated_at: "2014-04-28 16:10:48">, 
 #<Kid id: 119, dad_id: 9, mom_id: 5, created_at: "2014-04-28 16:10:48", updated_at: "2014-04-28 16:10:48">, 
 #<Kid id: 133, dad_id: 10, mom_id: 5, created_at: "2014-04-28 16:10:48", updated_at: "2014-04-28 16:10:48">, ...]> 
Was it helpful?

Solution

Change your scope as below:

scope :online, -> { joins(:mom).where("moms.is_online = ?", true) }

Somehow, in Rails 4 your scope online is not getting chained to the query formed with @dad.kids. Above is the correct way to define a Scope in Rails 4

OTHER TIPS

EDIT

As mentioned by @KirtiThorat, the real reason that the OP's scope method wasn't working is due to a change in ActiveRecord scope definitions with the release of Rails 4. Specifically, according the upgrading Rails guide:

Rails 4.0 requires that scopes use a callable object such as a Proc or lambda

With that in mind, simply redefining the :online scope definition within a lambda should fix the problem:

scope :online, -> {joins(:mom).where("mom.is_online = ?", true)}

ORIGINAL ANSWER:

Your best bet is to use the merge method:

def show
 @dad = Dad.find(params[:dad_id])
 @kids = @dad.kids.merge(Kid.online)
end

As the ActiveRecord docs define merge:

merge(other) public
Merges in the conditions from other, if other is an ActiveRecord::Relation. Returns an array representing the intersection of the resulting records with other, if other is an array.

As such, since merge finds the intersection of the two arrays, in this case, the array of @dad's kids and Kids with online Moms, this will yield the array containing all Kids with @dad as their Dad and a Mom who is_online.

It is also nice to know that the array intersection shorthand method & can be used too:

def show
 @dad = Dad.find(params[:dad_id])
 @kids = @dad.kids & Kid.online
end

I am not sure whether it will work on not, but you can try following approach::

class Dad < ActiveRecord::Base
  has_many :kids
  has_many :kids_with_online_mom, class_name: `Kid`, through: :mom,  joins(:mom).where("moms.is_online = ?", true)
end

if it works, just by @dad.kids_with_online_mom, you will get desired output.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top