grouped_scope 0.6.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/.travis.yml +5 -0
  2. data/CHANGELOG +13 -0
  3. data/Gemfile +4 -6
  4. data/README.md +173 -0
  5. data/lib/grouped_scope.rb +7 -7
  6. data/lib/grouped_scope/arish/associations/association_scope.rb +90 -0
  7. data/lib/grouped_scope/arish/associations/builder/grouped_association.rb +50 -0
  8. data/lib/grouped_scope/arish/associations/builder/grouped_collection_association.rb +32 -0
  9. data/lib/grouped_scope/arish/associations/collection_association.rb +25 -0
  10. data/lib/grouped_scope/arish/base.rb +24 -0
  11. data/lib/grouped_scope/arish/reflection.rb +18 -0
  12. data/lib/grouped_scope/arish/relation/predicate_builer.rb +27 -0
  13. data/lib/grouped_scope/errors.rb +2 -3
  14. data/lib/grouped_scope/self_grouping.rb +59 -23
  15. data/lib/grouped_scope/version.rb +2 -4
  16. data/test/cases/has_many_test.rb +155 -0
  17. data/test/cases/has_many_through_test.rb +51 -0
  18. data/test/cases/reflection_test.rb +62 -0
  19. data/test/cases/self_grouping_test.rb +201 -0
  20. data/test/helper.rb +48 -35
  21. metadata +27 -30
  22. data/README.rdoc +0 -98
  23. data/lib/grouped_scope/association_reflection.rb +0 -54
  24. data/lib/grouped_scope/class_methods.rb +0 -32
  25. data/lib/grouped_scope/core_ext.rb +0 -29
  26. data/lib/grouped_scope/grouping.rb +0 -9
  27. data/lib/grouped_scope/has_many_association.rb +0 -28
  28. data/lib/grouped_scope/has_many_through_association.rb +0 -28
  29. data/lib/grouped_scope/instance_methods.rb +0 -10
  30. data/test/grouped_scope/association_reflection_test.rb +0 -73
  31. data/test/grouped_scope/class_methods_test.rb +0 -51
  32. data/test/grouped_scope/has_many_association_test.rb +0 -156
  33. data/test/grouped_scope/has_many_through_association_test.rb +0 -51
  34. data/test/grouped_scope/self_grouping_test.rb +0 -146
@@ -0,0 +1,18 @@
1
+ module GroupedScope
2
+ module Arish
3
+ module Reflection
4
+ module AssociationReflection
5
+
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ attr_accessor :grouped_scope
10
+ alias :grouped_scope? :grouped_scope
11
+ end
12
+
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ ActiveRecord::Reflection::AssociationReflection.send :include, GroupedScope::Arish::Reflection::AssociationReflection
@@ -0,0 +1,27 @@
1
+ module GroupedScope
2
+ module Arish
3
+ module PredicateBuilder
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ singleton_class.alias_method_chain :build_from_hash, :grouped_scope
9
+ end
10
+
11
+ module ClassMethods
12
+
13
+ def build_from_hash_with_grouped_scope(engine, attributes, default_table)
14
+ attributes.select{ |column, value| GroupedScope::SelfGroupping === value }.each do |column_value|
15
+ column, value = column_value
16
+ attributes[column] = value.arel_table[column.to_s].in(value.ids_sql)
17
+ end
18
+ build_from_hash_without_grouped_scope(engine, attributes, default_table)
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+ end
25
+ end
26
+
27
+ ActiveRecord::PredicateBuilder.send :include, GroupedScope::Arish::PredicateBuilder
@@ -7,6 +7,5 @@ module GroupedScope
7
7
  def initialize(owner) ; @owner = owner ; end
8
8
  def message ; %|The #{@owner.class} class does not have a "group_id" attribute.| ; end
9
9
  end
10
-
11
-
12
- end
10
+
11
+ end
@@ -1,13 +1,13 @@
1
1
  module GroupedScope
2
2
  class SelfGroupping
3
3
 
4
- attr_reader :proxy_owner
4
+ attr_reader :proxy_owner, :reflection
5
5
 
6
- delegate :primary_key, :quote_value, :columns_hash, :to => :proxy_class
6
+ delegate :quote_value, :columns_hash, :to => :proxy_class
7
7
 
8
8
  [].methods.each do |m|
9
- unless m =~ /(^__|^nil\?|^send|^object_id$|class|extend|respond_to\?)/
10
- delegate m, :to => :group_proxy
9
+ unless m =~ /^(?:nil\?|send|object_id|to_a)$|^__|^respond_to|proxy_/
10
+ delegate m, :to => :grouped_proxy
11
11
  end
12
12
  end
13
13
 
@@ -17,45 +17,71 @@ module GroupedScope
17
17
  @proxy_owner = proxy_owner
18
18
  end
19
19
 
20
+ def blank?
21
+ proxy_owner.group_id.blank?
22
+ end
23
+
24
+ def present?
25
+ !blank?
26
+ end
27
+
20
28
  def ids
21
- @ids ||= find_selves(group_id_scope_options).map(&:id)
29
+ grouped_scoped_ids.map(&primary_key.to_sym)
30
+ end
31
+
32
+ def ids_sql
33
+ Arel.sql(grouped_scoped_ids.to_sql)
22
34
  end
23
35
 
24
36
  def quoted_ids
25
37
  ids.map { |id| quote_value(id,columns_hash[primary_key]) }.join(',')
26
38
  end
27
39
 
40
+ def with_reflection(reflection)
41
+ @reflection = reflection
42
+ yield
43
+ ensure
44
+ @reflection = nil
45
+ end
46
+
28
47
  def respond_to?(method, include_private=false)
29
- super || !proxy_class.grouped_scopes[method].blank?
48
+ super || proxy_class.grouped_reflections[method].present? || grouped_proxy.respond_to?(method, include_private)
30
49
  end
31
50
 
32
51
 
33
52
  protected
34
53
 
35
- def group_proxy
36
- @group_proxy ||= find_selves(group_scope_options)
54
+ def primary_key
55
+ reflection ? reflection.association_primary_key : proxy_class.primary_key
37
56
  end
38
57
 
39
- def all_grouped?
40
- proxy_owner.all_grouped? rescue false
58
+ def arel_group_id
59
+ arel_table['group_id']
41
60
  end
42
61
 
43
- def no_group?
44
- proxy_owner.group_id.blank?
62
+ def arel_primary_key
63
+ arel_table[primary_key]
64
+ end
65
+
66
+ def arel_table
67
+ reflection ? Arel::Table.new(reflection.table_name) : proxy_class.arel_table
45
68
  end
46
69
 
47
- def find_selves(options={})
48
- proxy_owner.class.find :all, options
70
+ def grouped_proxy
71
+ @grouped_proxy ||= grouped_scoped
72
+ end
73
+
74
+ def all_grouped?
75
+ proxy_owner.all_grouped? rescue false
49
76
  end
50
77
 
51
- def group_scope_options
52
- return {} if all_grouped?
53
- conditions = no_group? ? { primary_key => proxy_owner.id } : { :group_id => proxy_owner.group_id }
54
- { :conditions => conditions }
78
+ def grouped_scoped
79
+ return proxy_class.scoped if all_grouped?
80
+ proxy_class.where present? ? arel_group_id.eq(proxy_owner.group_id) : arel_primary_key.eq(proxy_owner.id)
55
81
  end
56
82
 
57
- def group_id_scope_options
58
- { :select => primary_key }.merge(group_scope_options)
83
+ def grouped_scoped_ids
84
+ grouped_scoped.select(arel_primary_key)
59
85
  end
60
86
 
61
87
  def proxy_class
@@ -65,9 +91,19 @@ module GroupedScope
65
91
 
66
92
  private
67
93
 
68
- def method_missing(method, *args, &block)
69
- if proxy_class.grouped_scopes[method]
70
- proxy_owner.send("grouped_scope_#{method}", *args, &block)
94
+ def method_missing(method, *args)
95
+ if proxy_class.grouped_reflections[method]
96
+ if block_given?
97
+ proxy_owner.send(:"grouped_scope_#{method}", *args) { |*block_args| yield(*block_args) }
98
+ else
99
+ proxy_owner.send(:"grouped_scope_#{method}", *args)
100
+ end
101
+ elsif grouped_proxy.respond_to?(method)
102
+ if block_given?
103
+ grouped_proxy.send(method, *args) { |*block_args| yield(*block_args) }
104
+ else
105
+ grouped_proxy.send(method, *args)
106
+ end
71
107
  else
72
108
  super
73
109
  end
@@ -1,5 +1,3 @@
1
1
  module GroupedScope
2
-
3
- VERSION = '0.6.1'
4
-
5
- end
2
+ VERSION = '3.1.0'
3
+ end
@@ -0,0 +1,155 @@
1
+ require 'helper'
2
+
3
+ class GroupedScope::HasManyTest < GroupedScope::TestCase
4
+
5
+ describe 'For an Employee' do
6
+
7
+ before do
8
+ @employee = FactoryGirl.create(:employee)
9
+ end
10
+
11
+ it 'scopes existing association to owner' do
12
+ assert_sql(/"employee_id" = #{@employee.id}/) do
13
+ @employee.reports(true)
14
+ end
15
+ end
16
+
17
+ it 'scopes group association to owner when no group present' do
18
+ assert_sql(/"employee_id" = #{@employee.id}/) do
19
+ @employee.group.reports(true)
20
+ end
21
+ end
22
+
23
+ it 'scopes group association to owner when group present' do
24
+ @employee.update_attribute :group_id, 43
25
+ assert_sql(/"employee_id" IN \(SELECT "employees"\."id" FROM "employees" WHERE "employees"\."group_id" = 43\)/) do
26
+ @employee.group.reports(true)
27
+ end
28
+ end
29
+
30
+ describe 'for counting sql' do
31
+
32
+ before do
33
+ @e1 = FactoryGirl.create(:employee_with_reports, :group_id => 1)
34
+ @e2 = FactoryGirl.create(:employee_with_reports, :group_id => 1)
35
+ end
36
+
37
+ it 'scope count sql to owner' do
38
+ assert_sql(/SELECT COUNT\(\*\)/,/"employee_id" = #{@e1.id}/) do
39
+ @e1.reports(true).count
40
+ end
41
+ end
42
+
43
+ it 'scope count sql to group' do
44
+ assert_sql(/SELECT COUNT\(\*\)/,/"employee_id" IN \(SELECT "employees"\."id" FROM "employees" WHERE "employees"\."group_id" = #{@e1.group_id}\)/) do
45
+ @e1.group.reports(true).count
46
+ end
47
+ end
48
+
49
+ it 'have a group count equal to sum of seperate owner counts' do
50
+ assert_equal @e1.reports(true).count + @e2.reports(true).count, @e2.group.reports(true).count
51
+ end
52
+
53
+ end
54
+
55
+ describe 'training association extensions' do
56
+
57
+ before do
58
+ @e1 = FactoryGirl.create(:employee_with_urgent_reports, :group_id => 1)
59
+ @e2 = FactoryGirl.create(:employee, :group_id => 1)
60
+ @urgent_reports = @e1.reports.select(&:urgent_title?)
61
+ end
62
+
63
+ it 'find urgent report via normal ungrouped association' do
64
+ assert_same_elements @urgent_reports, @e1.reports(true).urgent
65
+ end
66
+
67
+ it 'find urgent report via grouped reflection' do
68
+ assert_same_elements @urgent_reports, @e2.group.reports(true).urgent
69
+ end
70
+
71
+ it 'use association extension SQL along with group reflection' do
72
+ assert_sql(select_from_reports, where_for_groups(@e2.group_id), where_for_urgent_title) do
73
+ @e2.group.reports.urgent
74
+ end
75
+ end
76
+
77
+ end
78
+
79
+ describe 'training named scopes' do
80
+
81
+ before do
82
+ @e1 = FactoryGirl.create(:employee_with_urgent_reports, :group_id => 1)
83
+ @e2 = FactoryGirl.create(:employee, :group_id => 1)
84
+ @urgent_titles = @e1.reports.select(&:urgent_title?)
85
+ @urgent_bodys = @e1.reports.select(&:urgent_body?)
86
+ end
87
+
88
+ it 'find urgent reports via normal named scopes by normal owner' do
89
+ assert_same_elements @urgent_titles, @e1.reports(true).with_urgent_title
90
+ assert_same_elements @urgent_bodys, @e1.reports(true).with_urgent_body
91
+ end
92
+
93
+ it 'find urgent reports via group reflection by group member' do
94
+ assert_same_elements @urgent_titles, @e2.group.reports(true).with_urgent_title
95
+ assert_same_elements @urgent_bodys, @e2.group.reports(true).with_urgent_body
96
+ end
97
+
98
+ it 'use named scope SQL along with group reflection' do
99
+ assert_sql(select_from_reports, where_for_groups(@e2.group_id), where_for_urgent_body, where_for_urgent_title) do
100
+ @e2.group.reports.with_urgent_title.with_urgent_body.inspect
101
+ end
102
+ end
103
+
104
+ end
105
+
106
+ end
107
+
108
+ describe 'For a LegacyEmployee' do
109
+
110
+ before do
111
+ @employee = FactoryGirl.create(:legacy_employee)
112
+ end
113
+
114
+ it 'scope existing association to owner' do
115
+ assert_sql(/"legacy_reports"."email" = '#{@employee.id}'/) do
116
+ @employee.reports(true)
117
+ end
118
+ end
119
+
120
+ it 'scope group association to owner, since no group is present' do
121
+ assert_sql(/"legacy_reports"."email" = '#{@employee.id}'/) do
122
+ @employee.group.reports(true)
123
+ end
124
+ end
125
+
126
+ it 'scopes group association to owners group when present' do
127
+ @employee.update_attribute :group_id, 43
128
+ assert_sql(/"legacy_reports"."email" IN \(SELECT "legacy_employees"\."email" FROM "legacy_employees" WHERE "legacy_employees"\."group_id" = 43\)/) do
129
+ @employee.group.reports(true)
130
+ end
131
+ end
132
+
133
+ end
134
+
135
+
136
+ protected
137
+
138
+ def select_from_reports
139
+ /SELECT "reports"\.\* FROM "reports"/
140
+ end
141
+
142
+ def where_for_groups(id)
143
+ /WHERE "reports"."employee_id" IN \(SELECT "employees"\."id" FROM "employees" WHERE "employees"\."group_id" = #{id}\)/
144
+ end
145
+
146
+ def where_for_urgent_body
147
+ /WHERE.*body LIKE '%URGENT%'/
148
+ end
149
+
150
+ def where_for_urgent_title
151
+ /WHERE.*"?reports"?."?title"? = 'URGENT'/
152
+ end
153
+
154
+
155
+ end
@@ -0,0 +1,51 @@
1
+ require 'helper'
2
+
3
+ class GroupedScope::HasManyThroughTest < GroupedScope::TestCase
4
+
5
+ before do
6
+ @e1 = FactoryGirl.create(:employee, :group_id => 1)
7
+ @e1.departments << Department.hr << Department.finance
8
+ @e2 = FactoryGirl.create(:employee, :group_id => 1)
9
+ @e2.departments << Department.it
10
+ @all_group_departments = [Department.hr, Department.it, Department.finance]
11
+ end
12
+
13
+ describe 'For default association' do
14
+
15
+ it 'scope to owner' do
16
+ assert_sql(/"employee_id" = #{@e1.id}/) do
17
+ @e1.departments(true)
18
+ end
19
+ end
20
+
21
+ it 'scope count to owner' do
22
+ assert_sql(/"employee_id" = #{@e1.id}/) do
23
+ @e1.departments(true).count
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ describe 'For grouped association' do
30
+
31
+ it 'scope to group' do
32
+
33
+ assert_sql(/"employee_id" IN \(SELECT "employees"\."id" FROM "employees" WHERE "employees"\."group_id" = #{@e1.group_id}\)/) do
34
+ @e2.group.departments(true)
35
+ end
36
+ end
37
+
38
+ it 'scope count to group' do
39
+ assert_sql(/"employee_id" IN \(SELECT "employees"\."id" FROM "employees" WHERE "employees"\."group_id" = #{@e1.group_id}\)/) do
40
+ @e1.group.departments(true).count
41
+ end
42
+ end
43
+
44
+ it 'have a group count equal to sum of separate owner counts' do
45
+ assert_equal @e1.departments(true).count + @e2.departments(true).count, @e2.group.departments(true).count
46
+ end
47
+
48
+ end
49
+
50
+
51
+ end
@@ -0,0 +1,62 @@
1
+ require 'helper'
2
+
3
+ class GroupedScope::ReflectionTest < GroupedScope::TestCase
4
+
5
+ it 'creates a has_many association named :grouped_scope_* using existing association as a suffix' do
6
+ assert FactoryGirl.create(:employee).respond_to?(:grouped_scope_reports)
7
+ end
8
+
9
+ it 'does not add the #grouped_scope to existing reflection' do
10
+ assert_nil Employee.reflections[:reports].grouped_scope?
11
+ end
12
+
13
+ it 'should have added the #grouped_scope to new grouped reflection' do
14
+ assert Employee.reflections[:grouped_scope_reports].grouped_scope?
15
+ end
16
+
17
+ describe 'Class attribute for #grouped_reflections' do
18
+
19
+ it 'should one' do
20
+ assert_instance_of Hash, Employee.grouped_reflections
21
+ end
22
+
23
+ it 'populate with new grouped scopes' do
24
+ assert_nil Employee.grouped_reflections[:newgroupes]
25
+ Employee.class_eval { has_many(:newgroupes) ; grouped_scope(:newgroupes) }
26
+ assert Employee.grouped_reflections[:newgroupes]
27
+ end
28
+
29
+ end
30
+
31
+ describe 'Raise and exception' do
32
+
33
+ it 'when a association does not exist' do
34
+ begin
35
+ raised = false
36
+ Employee.class_eval{ grouped_scope(:doesnotexist) }
37
+ rescue ArgumentError => e
38
+ raised = true
39
+ e.message.must_match %r{Cannot create a group scope for :doesnotexist}
40
+ ensure
41
+ assert raised, 'Did not raise an ArgumentError'
42
+ end
43
+ end
44
+
45
+ it 'when the association is not a has_many or a has_and_belongs_to_many' do
46
+ begin
47
+ raised = false
48
+ Employee.class_eval { belongs_to(:belongstowillnotwork) ; grouped_scope(:belongstowillnotwork) }
49
+ rescue ArgumentError => e
50
+ raised = true
51
+ e.message.must_match %r{:belongstowillnotwork.*the reflection is blank or not supported}
52
+ ensure
53
+ assert raised, 'Did not raise an ArgumentError'
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+
60
+
61
+
62
+ end