こんなモデルがあるとして
user.rb
class User < ActiveRecord::Base
scope :male, ->{ where(sex: :male) } # 男性
scope :female, ->{ where(sex: :female) } # 女性
scope :adult, ->{ where(arel_table[:age].gteq 20) } # 成人
scope :minor, ->{ where(arel_table[:age].lt 20) } # 未成年
scope :men, ->{ adult.male } # 成人,男性
scope :women, ->{ adult.female } # 成人,女性
scope :boys, ->{ minor.male } # 未成年,男性
scope :girls, ->{ minor.female } # 未成年,女性
end
単純なwhereのscopeをorでつなげる場合。
ex) 男性 or 未成年
User.where(User.male.minor.where_values.reduce(:or)).to_sql
# => "SELECT `users`.* FROM `users` WHERE ((`users`.`sex` = 'male' OR `users`.`age` < 20))"
# もちろん male.minor と boys は同じなのでこれでもいい
User.where(User.boys.where_values.reduce(:or)).to_sql
# => "SELECT `users`.* FROM `users` WHERE ((`users`.`sex` = 'male' OR `users`.`age` < 20))"
ex) 男性 or 未成年 or :role => 'admin'
User.where(User.male.minor.where(role: 'admin').where_values.reduce(:or)).to_sql
# => "SELECT `users`.* FROM `users` WHERE ((`users`.`sex` = 'male' OR `users`.`age` < 20 OR `users`.`role` = 'admin'))"
and条件も含むちょっと複雑なscopeをorでつなげる
ex) 成人女性(成人 and 女性) or 未成年男子(未成年 and 男子)
women = User.women.where_values.reduce(:and)
boys = User.boys.where_values.reduce(:and)
User.where(women.or boys).to_sql
# => "SELECT `users`.* FROM `users` WHERE ((`users`.`age` >= 20 AND `users`.`sex` = 'female' OR `users`.`age` < 20 AND `users`.`sex` = 'male'))"
ざっくり説明
ActiveRecord::Relation#where_values
でArel::Nodes::Node
の配列が取得できる。
このNodeにはarelの.and()
やら.or()
やらが使えるので、そのあたりを組み合わせてからARの.where()
だったり.merge()
だったりに渡してあげるとOK。