Commit 0b31911bd64211099153a1dd30feede66d6d2a91
Committed by
Daniel
1 parent
b1bcaade
Exists in
master
and in
22 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,7 +84,8 @@ class Profile < ActiveRecord::Base | ||
| 84 | def members | 84 | def members |
| 85 | scopes = plugins.dispatch_scopes(:organization_members, self) | 85 | scopes = plugins.dispatch_scopes(:organization_members, self) |
| 86 | scopes << Person.members_of(self) | 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 | end | 89 | end |
| 89 | 90 | ||
| 90 | def members_count | 91 | def members_count |
| @@ -0,0 +1,13 @@ | @@ -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 @@ | @@ -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,14 +19,6 @@ module Rails3Finder | ||
| 19 | named_scope :readonly, lambda {|readonly| {:readonly => readonly }} | 19 | named_scope :readonly, lambda {|readonly| {:readonly => readonly }} |
| 20 | named_scope :lock, lambda {|lock| {:lock => lock }} | 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 | __where_fn = lambda do |*where| | 22 | __where_fn = lambda do |*where| |
| 31 | if where.is_a?(Array) and where.size == 1 | 23 | if where.is_a?(Array) and where.size == 1 |
| 32 | {:conditions => where.first} | 24 | {:conditions => where.first} |
| @@ -37,33 +29,66 @@ module Rails3Finder | @@ -37,33 +29,66 @@ module Rails3Finder | ||
| 37 | 29 | ||
| 38 | named_scope :where, __where_fn | 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 | end | 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 | end | 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 | end | 90 | end |
| 91 | + | ||
| 67 | end | 92 | end |
| 68 | end | 93 | end |
| 69 | end | 94 | end |