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,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 |