2

I have defined the following two scopes and both working fine:

scope :regular_friends, lambda { |user| joins{friendships}.where{friendships.user_id.not_in(user)}}
scope :inverse_friends, lambda { |user| joins{inverse_friendships}.where{inverse_friendships.friend_id.not_in(user)}}

I using squeel (https://github.com/ernie/squeel) here and building a self referential association, which is inspired by (http://railscasts.com/episodes/163-self-referential-association).

Based on that, I want to define a third scope which unions the results of the other two:

scope :friends, lambda { |user| User.regular_friends(user).union(User.inverse_friends(user)) }

Unfortunately, it doesn't work. I am getting a:

NoMethodError (undefined method `default_scoped?' for #<Arel::Nodes::Union:0x8249534>):

I am on Rails 3.1.3. I thought the default_scope? method has been removed in Rails 3.*, but it's complaining about that...

Can anyone help?

Veger
  • 37,240
  • 11
  • 105
  • 116
Scholle
  • 1,521
  • 2
  • 23
  • 44

2 Answers2

3

There is no such method as union in Rails (I don't know about Arel). Just construct the scope as you would normally.

EDIT: You can always use raw sql in the scope definition. I don't know the details of your application (especially how the tables are interconnected), so I can't construct the query in its full beauty. Nevertheless, the query will look similar to this:

scope :friends, find_by_sql(%(
  SELECT users.* FROM users INNER JOIN friendships f on f.user_id = users.#{id} WHERE ...
  UNION
  SELECT users.* FROM users INNER JOIN inverse_friendships i on i.friend_id = users.#{id} WHERE ...
)
Marek Příhoda
  • 11,108
  • 3
  • 39
  • 53
  • 1
    Arel provides an Arel::Nodes:Union and found some examples using union() method, so I guess there is one. Anyway, I am not sure how to design the friends scope to return union of the other scopes in the normal way. – Scholle Dec 12 '11 at 14:39
  • you can always use raw sql in the scope definition (and I guess that would be the way to if you want the union) – Marek Příhoda Dec 12 '11 at 14:58
3

default_scope? indeed is removed from Rails 3 and later. I do not know why this error pops up (I have the same problem), but using union() in scopes seems not to work. Using them directly (outside scope) seems to work:

User.regular_friends(user).union(User.inverse_friends(user))

Personally, I do not put the union in a scope and directly use it (luckily I only us it on one place, so for me it is not a big deal).

I can also not use union() directly with find(), first(), all(), etc. I guess arel is not yet complete... To circumvent this problem I use:

sql = User.regular_friends(user).union(User.inverse_friends(user)).to_sql
result = find_by_sql(sql)

But maybe/probably there are nicer ways to prevent this...? (like waiting for an arel update :P )

Community
  • 1
  • 1
Veger
  • 37,240
  • 11
  • 105
  • 116
  • It should be noted that this issue has been open in Arel since 2012 and is still open. I wouldn't expect a fix anytime soon. –  Mar 10 '15 at 17:11
  • 1
    might the https://github.com/brianhempel/active_record_union gem help, which defines `union` on `ActiveRecord::Query` to return `ActiveRecord::Query`? – max pleaner Dec 05 '15 at 00:47