Commit 0b31911bd64211099153a1dd30feede66d6d2a91
Committed by
Daniel
1 parent
b1bcaade
Exists in
master
and in
28 other branches
Make scope unions
closes ActionItem2830
Showing
4 changed files
with
102 additions
and
32 deletions
Show diff stats
app/models/profile.rb
| ... | ... | @@ -84,7 +84,8 @@ class Profile < ActiveRecord::Base |
| 84 | 84 | def members |
| 85 | 85 | scopes = plugins.dispatch_scopes(:organization_members, self) |
| 86 | 86 | scopes << Person.members_of(self) |
| 87 | - scopes.size == 1 ? scopes.first : Person.or_scope(scopes) | |
| 87 | + return scopes.first if scopes.size == 1 | |
| 88 | + ScopeTool.union *scopes | |
| 88 | 89 | end |
| 89 | 90 | |
| 90 | 91 | def members_count | ... | ... |
| ... | ... | @@ -0,0 +1,13 @@ |
| 1 | +module ScopeTool | |
| 2 | + | |
| 3 | + def union(*scopes) | |
| 4 | + model = scopes.first.class_name.constantize | |
| 5 | + scopes = scopes.map &:to_sql | |
| 6 | + model.from "(\n#{scopes.join("\nUNION\n")}\n) as #{model.table_name}" | |
| 7 | + end | |
| 8 | + | |
| 9 | + class << self | |
| 10 | + include ScopeTool | |
| 11 | + end | |
| 12 | + | |
| 13 | +end | ... | ... |
| ... | ... | @@ -0,0 +1,31 @@ |
| 1 | +require File.dirname(__FILE__) + '/../test_helper' | |
| 2 | + | |
| 3 | +class ScopeToolTest < ActiveSupport::TestCase | |
| 4 | + include ScopeTool | |
| 5 | + | |
| 6 | + should 'unite scopes' do | |
| 7 | + cmm = fast_create Community | |
| 8 | + ent = fast_create Enterprise | |
| 9 | + orgs = union(Profile.communities, Profile.enterprises) | |
| 10 | + assert orgs.include? cmm | |
| 11 | + assert orgs.include? ent | |
| 12 | + end | |
| 13 | + | |
| 14 | + should 'filter united scopes' do | |
| 15 | + cmm1 = fast_create Community, :visible => true | |
| 16 | + cmm2 = fast_create Community, :visible => false | |
| 17 | + ent1 = fast_create Enterprise, :visible => true | |
| 18 | + ent2 = fast_create Enterprise, :visible => false | |
| 19 | + orgs = union(Profile.communities, Profile.enterprises) | |
| 20 | + assert orgs.include? cmm1 | |
| 21 | + assert orgs.include? cmm2 | |
| 22 | + assert orgs.include? ent1 | |
| 23 | + assert orgs.include? ent2 | |
| 24 | + orgs = orgs.visible | |
| 25 | + assert orgs.include? cmm1 | |
| 26 | + assert !orgs.include?(cmm2) | |
| 27 | + assert orgs.include? ent1 | |
| 28 | + assert !orgs.include?(ent2) | |
| 29 | + end | |
| 30 | + | |
| 31 | +end | ... | ... |
vendor/plugins/monkey_patches/methods_from_fake_arel/init.rb
| ... | ... | @@ -19,14 +19,6 @@ module Rails3Finder |
| 19 | 19 | named_scope :readonly, lambda {|readonly| {:readonly => readonly }} |
| 20 | 20 | named_scope :lock, lambda {|lock| {:lock => lock }} |
| 21 | 21 | |
| 22 | - def self.select(value = Proc.new) | |
| 23 | - if block_given? | |
| 24 | - all.select {|*block_args| value.call(*block_args) } | |
| 25 | - else | |
| 26 | - self.scoped(:select => Array.wrap(value).join(',')) | |
| 27 | - end | |
| 28 | - end | |
| 29 | - | |
| 30 | 22 | __where_fn = lambda do |*where| |
| 31 | 23 | if where.is_a?(Array) and where.size == 1 |
| 32 | 24 | {:conditions => where.first} |
| ... | ... | @@ -37,33 +29,66 @@ module Rails3Finder |
| 37 | 29 | |
| 38 | 30 | named_scope :where, __where_fn |
| 39 | 31 | |
| 40 | - # Use carefully this method! It might get lost with different classes | |
| 41 | - # scopes or different types of joins. | |
| 42 | - def self.or_scope(*scopes) | |
| 43 | - where = [] | |
| 44 | - joins = [] | |
| 45 | - includes = [] | |
| 32 | + class << self | |
| 46 | 33 | |
| 47 | - # for some reason, flatten is actually executing the scope | |
| 48 | - scopes = scopes[0] if scopes.size == 1 | |
| 49 | - scopes.each do |s| | |
| 50 | - s = s.proxy_options | |
| 51 | - begin | |
| 52 | - where << merge_conditions(s[:conditions]) | |
| 53 | - rescue NoMethodError | |
| 54 | - # I am ActiveRecord::Base. Only my subclasses define merge_conditions: | |
| 55 | - where << subclasses.first.merge_conditions(s[:conditions]) | |
| 34 | + def select(value = Proc.new) | |
| 35 | + if block_given? | |
| 36 | + all.select {|*block_args| value.call(*block_args) } | |
| 37 | + else | |
| 38 | + self.scoped(:select => Array.wrap(value).join(',')) | |
| 56 | 39 | end |
| 57 | - #where << merge_conditions(s[:conditions]) | |
| 58 | - joins << s[:joins] unless s[:joins].nil? | |
| 59 | - includes << s[:include] unless s[:include].nil? | |
| 60 | 40 | end |
| 61 | - scoped = self | |
| 62 | - scoped = scoped.select("DISTINCT #{self.table_name}.*") | |
| 63 | - scoped = scoped.includes(includes.uniq.flatten) unless includes.blank? | |
| 64 | - scoped = scoped.joins(joins.uniq.flatten) unless joins.blank? | |
| 65 | - scoped.where(where.join(" OR ")) | |
| 41 | + | |
| 42 | + def to_sql | |
| 43 | + join_dependency = JoinDependency.new(self, merge_includes(scope(:find, :include), nil), nil) | |
| 44 | + scope = scope(:find) || {} | |
| 45 | + options = current_scoped_methods || {} | |
| 46 | + sql = "SELECT #{(scope && scope[:select]) || default_select(options[:joins] || (scope && scope[:joins]))} " | |
| 47 | + sql << "FROM #{(scope && scope[:from]) || quoted_table_name} " | |
| 48 | + sql << join_dependency.join_associations.collect{|join| join.association_join }.join | |
| 49 | + | |
| 50 | + add_joins!(sql, options[:joins], scope) | |
| 51 | + add_conditions!(sql, options[:conditions], scope) | |
| 52 | + add_limited_ids_condition!(sql, options, join_dependency) if !using_limitable_reflections?(join_dependency.reflections) && ((scope && scope[:limit]) || options[:limit]) | |
| 53 | + | |
| 54 | + add_group!(sql, options[:group], options[:having], scope) | |
| 55 | + add_order!(sql, options[:order], scope) | |
| 56 | + add_limit!(sql, options, scope) if using_limitable_reflections?(join_dependency.reflections) | |
| 57 | + add_lock!(sql, options, scope) | |
| 58 | + | |
| 59 | + return sanitize_sql(sql) | |
| 60 | + end | |
| 61 | + | |
| 62 | + # Use carefully this method! It might get lost with different classes | |
| 63 | + # scopes or different types of joins. | |
| 64 | + def or_scope(*scopes) | |
| 65 | + where = [] | |
| 66 | + joins = [] | |
| 67 | + includes = [] | |
| 68 | + | |
| 69 | + # for some reason, flatten is actually executing the scope | |
| 70 | + scopes = scopes[0] if scopes.size == 1 | |
| 71 | + scopes.each do |s| | |
| 72 | + s = s.proxy_options | |
| 73 | + begin | |
| 74 | + where << merge_conditions(s[:conditions]) | |
| 75 | + rescue NoMethodError | |
| 76 | + # I am ActiveRecord::Base. Only my subclasses define merge_conditions: | |
| 77 | + where << subclasses.first.merge_conditions(s[:conditions]) | |
| 78 | + end | |
| 79 | + #where << merge_conditions(s[:conditions]) | |
| 80 | + joins << s[:joins] unless s[:joins].nil? | |
| 81 | + includes << s[:include] unless s[:include].nil? | |
| 82 | + end | |
| 83 | + scoped = self | |
| 84 | + scoped = scoped.select("DISTINCT #{self.table_name}.*") | |
| 85 | + scoped = scoped.includes(includes.uniq.flatten) unless includes.blank? | |
| 86 | + scoped = scoped.joins(joins.uniq.flatten) unless joins.blank? | |
| 87 | + scoped.where(where.join(" OR ")) | |
| 88 | + end | |
| 89 | + | |
| 66 | 90 | end |
| 91 | + | |
| 67 | 92 | end |
| 68 | 93 | end |
| 69 | 94 | end | ... | ... |