squeel 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (72) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +8 -0
  3. data/LICENSE +20 -0
  4. data/README.rdoc +41 -0
  5. data/Rakefile +19 -0
  6. data/lib/core_ext/hash.rb +13 -0
  7. data/lib/core_ext/symbol.rb +36 -0
  8. data/lib/squeel.rb +26 -0
  9. data/lib/squeel/adapters/active_record.rb +6 -0
  10. data/lib/squeel/adapters/active_record/join_association.rb +90 -0
  11. data/lib/squeel/adapters/active_record/join_dependency.rb +68 -0
  12. data/lib/squeel/adapters/active_record/relation.rb +292 -0
  13. data/lib/squeel/configuration.rb +25 -0
  14. data/lib/squeel/constants.rb +23 -0
  15. data/lib/squeel/contexts/join_dependency_context.rb +74 -0
  16. data/lib/squeel/dsl.rb +31 -0
  17. data/lib/squeel/nodes.rb +10 -0
  18. data/lib/squeel/nodes/and.rb +8 -0
  19. data/lib/squeel/nodes/binary.rb +23 -0
  20. data/lib/squeel/nodes/function.rb +84 -0
  21. data/lib/squeel/nodes/join.rb +51 -0
  22. data/lib/squeel/nodes/key_path.rb +127 -0
  23. data/lib/squeel/nodes/nary.rb +35 -0
  24. data/lib/squeel/nodes/not.rb +8 -0
  25. data/lib/squeel/nodes/operation.rb +23 -0
  26. data/lib/squeel/nodes/operators.rb +27 -0
  27. data/lib/squeel/nodes/or.rb +8 -0
  28. data/lib/squeel/nodes/order.rb +35 -0
  29. data/lib/squeel/nodes/predicate.rb +49 -0
  30. data/lib/squeel/nodes/predicate_operators.rb +17 -0
  31. data/lib/squeel/nodes/stub.rb +113 -0
  32. data/lib/squeel/nodes/unary.rb +22 -0
  33. data/lib/squeel/predicate_methods.rb +22 -0
  34. data/lib/squeel/predicate_methods/function.rb +9 -0
  35. data/lib/squeel/predicate_methods/predicate.rb +11 -0
  36. data/lib/squeel/predicate_methods/stub.rb +9 -0
  37. data/lib/squeel/predicate_methods/symbol.rb +9 -0
  38. data/lib/squeel/version.rb +3 -0
  39. data/lib/squeel/visitors.rb +3 -0
  40. data/lib/squeel/visitors/base.rb +46 -0
  41. data/lib/squeel/visitors/order_visitor.rb +107 -0
  42. data/lib/squeel/visitors/predicate_visitor.rb +179 -0
  43. data/lib/squeel/visitors/select_visitor.rb +103 -0
  44. data/spec/blueprints/articles.rb +5 -0
  45. data/spec/blueprints/comments.rb +5 -0
  46. data/spec/blueprints/notes.rb +3 -0
  47. data/spec/blueprints/people.rb +4 -0
  48. data/spec/blueprints/tags.rb +3 -0
  49. data/spec/console.rb +22 -0
  50. data/spec/core_ext/symbol_spec.rb +68 -0
  51. data/spec/helpers/squeel_helper.rb +5 -0
  52. data/spec/spec_helper.rb +30 -0
  53. data/spec/squeel/adapters/active_record/join_association_spec.rb +18 -0
  54. data/spec/squeel/adapters/active_record/join_depdendency_spec.rb +60 -0
  55. data/spec/squeel/adapters/active_record/relation_spec.rb +437 -0
  56. data/spec/squeel/contexts/join_dependency_context_spec.rb +43 -0
  57. data/spec/squeel/dsl_spec.rb +73 -0
  58. data/spec/squeel/nodes/function_spec.rb +149 -0
  59. data/spec/squeel/nodes/join_spec.rb +27 -0
  60. data/spec/squeel/nodes/key_path_spec.rb +92 -0
  61. data/spec/squeel/nodes/operation_spec.rb +149 -0
  62. data/spec/squeel/nodes/operators_spec.rb +87 -0
  63. data/spec/squeel/nodes/order_spec.rb +30 -0
  64. data/spec/squeel/nodes/predicate_operators_spec.rb +88 -0
  65. data/spec/squeel/nodes/predicate_spec.rb +92 -0
  66. data/spec/squeel/nodes/stub_spec.rb +178 -0
  67. data/spec/squeel/visitors/order_visitor_spec.rb +128 -0
  68. data/spec/squeel/visitors/predicate_visitor_spec.rb +267 -0
  69. data/spec/squeel/visitors/select_visitor_spec.rb +115 -0
  70. data/spec/support/schema.rb +101 -0
  71. data/squeel.gemspec +44 -0
  72. metadata +221 -0
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ module Squeel
4
+ module Contexts
5
+ describe JoinDependencyContext do
6
+ before do
7
+ @jd = ActiveRecord::Associations::JoinDependency.
8
+ new(Person, {
9
+ :children => {
10
+ :children => {
11
+ :parent => :parent
12
+ }
13
+ }
14
+ }, [])
15
+ @c = JoinDependencyContext.new(@jd)
16
+ end
17
+
18
+ it 'finds associations' do
19
+ last_association = @jd.join_parts.last
20
+ next_to_last_association = @jd.join_parts[-2]
21
+
22
+ @c.find(:parent, next_to_last_association).should eq last_association
23
+ end
24
+
25
+ it 'contextualizes join parts with the proper alias' do
26
+ table = @c.contextualize @jd.join_parts.last
27
+ table.table_alias.should eq 'parents_people_2'
28
+ end
29
+
30
+ it 'contextualizes symbols as a generic table' do
31
+ table = @c.contextualize :table
32
+ table.name.should eq 'table'
33
+ table.table_alias.should be_nil
34
+ end
35
+
36
+ it 'contextualizes non-JoinPart/Symbols to the default join base table' do
37
+ table = @c.contextualize nil
38
+ table.name.should eq 'people'
39
+ table.table_alias.should be_nil
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+
3
+ module Squeel
4
+ describe DSL do
5
+
6
+ it 'evaluates code' do
7
+ result = DSL.evaluate { {id => 1} }
8
+ result.should be_a Hash
9
+ result.keys.first.should be_a Nodes::Stub
10
+ end
11
+
12
+ it 'creates function nodes when a method has arguments' do
13
+ result = DSL.evaluate { max(id) }
14
+ result.should be_a Nodes::Function
15
+ result.args.should eq [Nodes::Stub.new(:id)]
16
+ end
17
+
18
+ it 'creates polymorphic join nodes when a method has a single class argument' do
19
+ result = DSL.evaluate { association(Person) }
20
+ result.should be_a Nodes::Join
21
+ result.klass.should eq Person
22
+ end
23
+
24
+ it 'handles OR between predicates' do
25
+ result = DSL.evaluate {(name =~ 'Joe%') | (articles.title =~ 'Hello%')}
26
+ result.should be_a Nodes::Or
27
+ result.left.should be_a Nodes::Predicate
28
+ result.right.should be_a Nodes::KeyPath
29
+ result.right.endpoint.should be_a Nodes::Predicate
30
+ end
31
+
32
+ it 'is not a full closure (instance_evals) when the block supplied has no arity' do
33
+ my_class = Class.new do
34
+ def a_method
35
+ 'test'
36
+ end
37
+
38
+ def dsl_test
39
+ DSL.evaluate {name =~ a_method}
40
+ end
41
+ end
42
+
43
+ obj = my_class.new
44
+ result = obj.dsl_test
45
+ result.should be_a Nodes::Predicate
46
+ result.expr.should eq :name
47
+ result.method_name.should eq :matches
48
+ result.value.should be_a Nodes::Stub
49
+ result.value.symbol.should eq :a_method
50
+ end
51
+
52
+ it 'is a full closure (yields self) when the block supplied has an arity' do
53
+ my_class = Class.new do
54
+ def a_method
55
+ 'test'
56
+ end
57
+
58
+ def dsl_test
59
+ DSL.evaluate {|q| q.name =~ a_method}
60
+ end
61
+ end
62
+
63
+ obj = my_class.new
64
+ result = obj.dsl_test
65
+ result.should be_a Nodes::Predicate
66
+ result.expr.should eq :name
67
+ result.method_name.should eq :matches
68
+ result.value.should be_a String
69
+ result.value.should eq 'test'
70
+ end
71
+
72
+ end
73
+ end
@@ -0,0 +1,149 @@
1
+ module Squeel
2
+ module Nodes
3
+ describe Function do
4
+ before do
5
+ @f = :function.func(1,2,3)
6
+ end
7
+
8
+ Squeel::Constants::PREDICATES.each do |method_name|
9
+ it "creates #{method_name} predicates with no value" do
10
+ predicate = @f.send(method_name)
11
+ predicate.expr.should eq @f
12
+ predicate.method_name.should eq method_name
13
+ predicate.value?.should be_false
14
+ end
15
+
16
+ it "creates #{method_name} predicates with a value" do
17
+ predicate = @f.send(method_name, 'value')
18
+ predicate.expr.should eq @f
19
+ predicate.method_name.should eq method_name
20
+ predicate.value.should eq 'value'
21
+ end
22
+ end
23
+
24
+ Squeel::Constants::PREDICATE_ALIASES.each do |method_name, aliases|
25
+ aliases.each do |aliaz|
26
+ ['', '_any', '_all'].each do |suffix|
27
+ it "creates #{method_name.to_s + suffix} predicates with no value using the alias #{aliaz.to_s + suffix}" do
28
+ predicate = @f.send(aliaz.to_s + suffix)
29
+ predicate.expr.should eq @f
30
+ predicate.method_name.should eq "#{method_name}#{suffix}".to_sym
31
+ predicate.value?.should be_false
32
+ end
33
+
34
+ it "creates #{method_name.to_s + suffix} predicates with a value using the alias #{aliaz.to_s + suffix}" do
35
+ predicate = @f.send((aliaz.to_s + suffix), 'value')
36
+ predicate.expr.should eq @f
37
+ predicate.method_name.should eq "#{method_name}#{suffix}".to_sym
38
+ predicate.value.should eq 'value'
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ it 'creates ascending Order nodes with #asc' do
45
+ order = @f.asc
46
+ order.expr.should eq @f
47
+ order.should be_ascending
48
+ end
49
+
50
+ it 'creates descending Order nodes with #desc' do
51
+ order = @f.desc
52
+ order.expr.should eq @f
53
+ order.should be_descending
54
+ end
55
+
56
+ it 'creates eq predicates with ==' do
57
+ predicate = @f == 1
58
+ predicate.expr.should eq @f
59
+ predicate.method_name.should eq :eq
60
+ predicate.value.should eq 1
61
+ end
62
+
63
+ it 'creates not_eq predicates with ^' do
64
+ predicate = @f ^ 1
65
+ predicate.expr.should eq @f
66
+ predicate.method_name.should eq :not_eq
67
+ predicate.value.should eq 1
68
+ end
69
+
70
+ it 'creates not_eq predicates with !=' do
71
+ predicate = @f != 1
72
+ predicate.expr.should eq @f
73
+ predicate.method_name.should eq :not_eq
74
+ predicate.value.should eq 1
75
+ end if respond_to?('!=')
76
+
77
+ it 'creates in predicates with >>' do
78
+ predicate = @f >> [1,2,3]
79
+ predicate.expr.should eq @f
80
+ predicate.method_name.should eq :in
81
+ predicate.value.should eq [1,2,3]
82
+ end
83
+
84
+ it 'creates not_in predicates with <<' do
85
+ predicate = @f << [1,2,3]
86
+ predicate.expr.should eq @f
87
+ predicate.method_name.should eq :not_in
88
+ predicate.value.should eq [1,2,3]
89
+ end
90
+
91
+ it 'creates matches predicates with =~' do
92
+ predicate = @f =~ '%bob%'
93
+ predicate.expr.should eq @f
94
+ predicate.method_name.should eq :matches
95
+ predicate.value.should eq '%bob%'
96
+ end
97
+
98
+ it 'creates does_not_match predicates with !~' do
99
+ predicate = @f !~ '%bob%'
100
+ predicate.expr.should eq @f
101
+ predicate.method_name.should eq :does_not_match
102
+ predicate.value.should eq '%bob%'
103
+ end if respond_to?('!~')
104
+
105
+ it 'creates gt predicates with >' do
106
+ predicate = @f > 1
107
+ predicate.expr.should eq @f
108
+ predicate.method_name.should eq :gt
109
+ predicate.value.should eq 1
110
+ end
111
+
112
+ it 'creates gteq predicates with >=' do
113
+ predicate = @f >= 1
114
+ predicate.expr.should eq @f
115
+ predicate.method_name.should eq :gteq
116
+ predicate.value.should eq 1
117
+ end
118
+
119
+ it 'creates lt predicates with <' do
120
+ predicate = @f < 1
121
+ predicate.expr.should eq @f
122
+ predicate.method_name.should eq :lt
123
+ predicate.value.should eq 1
124
+ end
125
+
126
+ it 'creates lteq predicates with <=' do
127
+ predicate = @f <= 1
128
+ predicate.expr.should eq @f
129
+ predicate.method_name.should eq :lteq
130
+ predicate.value.should eq 1
131
+ end
132
+
133
+ describe '#as' do
134
+
135
+ it 'aliases the function' do
136
+ @f.as('the_alias')
137
+ @f.alias.should eq 'the_alias'
138
+ end
139
+
140
+ it 'casts the alias to a string' do
141
+ @f.as(:the_alias)
142
+ @f.alias.should eq 'the_alias'
143
+ end
144
+
145
+ end
146
+
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ module Squeel
4
+ module Nodes
5
+ describe Join do
6
+
7
+ it 'defaults to Arel::InnerJoin' do
8
+ @j = Join.new :name
9
+ @j.type.should eq Arel::InnerJoin
10
+ end
11
+
12
+ it 'allows setting join type' do
13
+ @j = Join.new :name
14
+ @j.outer
15
+ @j.type.should eq Arel::OuterJoin
16
+ end
17
+
18
+ it 'allows setting polymorphic class' do
19
+ @j = Join.new :name
20
+ @j.klass = Person
21
+ @j.should be_polymorphic
22
+ @j.klass.should eq Person
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,92 @@
1
+ module Squeel
2
+ module Nodes
3
+ describe KeyPath do
4
+ before do
5
+ @k = KeyPath.new(:first, :second)
6
+ end
7
+
8
+ it 'appends to its path when endpoint is a Stub' do
9
+ @k.third.fourth.fifth
10
+ @k.path.should eq [:first, :second, :third, :fourth]
11
+ @k.endpoint.should eq Stub.new(:fifth)
12
+ end
13
+
14
+ it 'becomes absolute when prefixed with ~' do
15
+ ~@k.third.fourth.fifth
16
+ @k.path.should eq [:first, :second, :third, :fourth]
17
+ @k.endpoint.should eq Stub.new(:fifth)
18
+ @k.should be_absolute
19
+ end
20
+
21
+ it 'stops appending once its endpoint is not a Stub' do
22
+ @k.third.fourth.fifth == 'cinco'
23
+ @k.endpoint.should eq Predicate.new(:fifth, :eq, 'cinco')
24
+ expect { @k.another }.to raise_error NoMethodError
25
+ end
26
+
27
+ it 'sends missing calls to its endpoint if the endpoint responds to them' do
28
+ @k.third.fourth.fifth.matches('Joe%')
29
+ @k.endpoint.should be_a Predicate
30
+ @k.endpoint.expr.should eq :fifth
31
+ @k.endpoint.method_name.should eq :matches
32
+ @k.endpoint.value.should eq 'Joe%'
33
+ end
34
+
35
+ it 'creates a polymorphic join at its endpoint' do
36
+ @k.third.fourth.fifth(Person)
37
+ @k.endpoint.should be_a Join
38
+ @k.endpoint.should be_polymorphic
39
+ end
40
+
41
+ it 'creates a named function at its endpoint' do
42
+ @k.third.fourth.fifth.max(1,2,3)
43
+ @k.endpoint.should be_a Function
44
+ @k.endpoint.name.should eq :max
45
+ @k.endpoint.args.should eq [1,2,3]
46
+ end
47
+
48
+ it 'creates AND nodes with & if the endpoint responds to &' do
49
+ node = @k.third.fourth.eq('Bob') & Stub.new(:attr).eq('Joe')
50
+ node.should be_a And
51
+ node.children.should eq [@k, Stub.new(:attr).eq('Joe')]
52
+ end
53
+
54
+ it 'raises NoMethodError with & if the endpoint does not respond to &' do
55
+ expect {@k.third.fourth & Stub.new(:attr).eq('Joe')}.to raise_error NoMethodError
56
+ end
57
+
58
+ it 'creates Or nodes with | if the endpoint responds to |' do
59
+ node = @k.third.fourth.eq('Bob') | Stub.new(:attr).eq('Joe')
60
+ node.should be_a Or
61
+ node.left.should eq @k
62
+ node.right.should eq Stub.new(:attr).eq('Joe')
63
+ end
64
+
65
+ it 'raises NoMethodError with | if the endpoint does not respond to |' do
66
+ expect {@k.third.fourth | Stub.new(:attr).eq('Joe')}.to raise_error NoMethodError
67
+ end
68
+
69
+ it 'creates Operation nodes with - if the endpoint responds to -' do
70
+ node = @k.third.fourth - 4
71
+ node.should be_an Operation
72
+ node.left.should eq @k
73
+ node.right.should eq 4
74
+ end
75
+
76
+ it 'raises NoMethodError with - if the endpoint does not respond to -' do
77
+ expect {@k.third.fourth(Person) - Stub.new(:attr).eq('Joe')}.to raise_error NoMethodError
78
+ end
79
+
80
+ it 'creates NOT nodes with -@ if the endpoint responds to -@' do
81
+ node = - @k.third.fourth.eq('Bob')
82
+ node.should be_a Not
83
+ node.expr.should eq @k
84
+ end
85
+
86
+ it 'raises NoMethodError with -@ if the endpoint does not respond to -@' do
87
+ expect {-@k.third.fourth}.to raise_error NoMethodError
88
+ end
89
+
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,149 @@
1
+ module Squeel
2
+ module Nodes
3
+ describe Operation do
4
+ before do
5
+ @o = dsl{name + 1}
6
+ end
7
+
8
+ Squeel::Constants::PREDICATES.each do |method_name|
9
+ it "creates #{method_name} predicates with no value" do
10
+ predicate = @o.send(method_name)
11
+ predicate.expr.should eq @o
12
+ predicate.method_name.should eq method_name
13
+ predicate.value?.should be_false
14
+ end
15
+
16
+ it "creates #{method_name} predicates with a value" do
17
+ predicate = @o.send(method_name, 'value')
18
+ predicate.expr.should eq @o
19
+ predicate.method_name.should eq method_name
20
+ predicate.value.should eq 'value'
21
+ end
22
+ end
23
+
24
+ Squeel::Constants::PREDICATE_ALIASES.each do |method_name, aliases|
25
+ aliases.each do |aliaz|
26
+ ['', '_any', '_all'].each do |suffix|
27
+ it "creates #{method_name.to_s + suffix} predicates with no value using the alias #{aliaz.to_s + suffix}" do
28
+ predicate = @o.send(aliaz.to_s + suffix)
29
+ predicate.expr.should eq @o
30
+ predicate.method_name.should eq "#{method_name}#{suffix}".to_sym
31
+ predicate.value?.should be_false
32
+ end
33
+
34
+ it "creates #{method_name.to_s + suffix} predicates with a value using the alias #{aliaz.to_s + suffix}" do
35
+ predicate = @o.send((aliaz.to_s + suffix), 'value')
36
+ predicate.expr.should eq @o
37
+ predicate.method_name.should eq "#{method_name}#{suffix}".to_sym
38
+ predicate.value.should eq 'value'
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ it 'creates ascending Order nodes with #asc' do
45
+ order = @o.asc
46
+ order.expr.should eq @o
47
+ order.should be_ascending
48
+ end
49
+
50
+ it 'creates descending Order nodes with #desc' do
51
+ order = @o.desc
52
+ order.expr.should eq @o
53
+ order.should be_descending
54
+ end
55
+
56
+ it 'creates eq predicates with ==' do
57
+ predicate = @o == 1
58
+ predicate.expr.should eq @o
59
+ predicate.method_name.should eq :eq
60
+ predicate.value.should eq 1
61
+ end
62
+
63
+ it 'creates not_eq predicates with ^' do
64
+ predicate = @o ^ 1
65
+ predicate.expr.should eq @o
66
+ predicate.method_name.should eq :not_eq
67
+ predicate.value.should eq 1
68
+ end
69
+
70
+ it 'creates not_eq predicates with !=' do
71
+ predicate = @o != 1
72
+ predicate.expr.should eq @o
73
+ predicate.method_name.should eq :not_eq
74
+ predicate.value.should eq 1
75
+ end if respond_to?('!=')
76
+
77
+ it 'creates in predicates with >>' do
78
+ predicate = @o >> [1,2,3]
79
+ predicate.expr.should eq @o
80
+ predicate.method_name.should eq :in
81
+ predicate.value.should eq [1,2,3]
82
+ end
83
+
84
+ it 'creates not_in predicates with <<' do
85
+ predicate = @o << [1,2,3]
86
+ predicate.expr.should eq @o
87
+ predicate.method_name.should eq :not_in
88
+ predicate.value.should eq [1,2,3]
89
+ end
90
+
91
+ it 'creates matches predicates with =~' do
92
+ predicate = @o =~ '%bob%'
93
+ predicate.expr.should eq @o
94
+ predicate.method_name.should eq :matches
95
+ predicate.value.should eq '%bob%'
96
+ end
97
+
98
+ it 'creates does_not_match predicates with !~' do
99
+ predicate = @o !~ '%bob%'
100
+ predicate.expr.should eq @o
101
+ predicate.method_name.should eq :does_not_match
102
+ predicate.value.should eq '%bob%'
103
+ end if respond_to?('!~')
104
+
105
+ it 'creates gt predicates with >' do
106
+ predicate = @o > 1
107
+ predicate.expr.should eq @o
108
+ predicate.method_name.should eq :gt
109
+ predicate.value.should eq 1
110
+ end
111
+
112
+ it 'creates gteq predicates with >=' do
113
+ predicate = @o >= 1
114
+ predicate.expr.should eq @o
115
+ predicate.method_name.should eq :gteq
116
+ predicate.value.should eq 1
117
+ end
118
+
119
+ it 'creates lt predicates with <' do
120
+ predicate = @o < 1
121
+ predicate.expr.should eq @o
122
+ predicate.method_name.should eq :lt
123
+ predicate.value.should eq 1
124
+ end
125
+
126
+ it 'creates lteq predicates with <=' do
127
+ predicate = @o <= 1
128
+ predicate.expr.should eq @o
129
+ predicate.method_name.should eq :lteq
130
+ predicate.value.should eq 1
131
+ end
132
+
133
+ describe '#as' do
134
+
135
+ it 'aliases the function' do
136
+ @o.as('the_alias')
137
+ @o.alias.should eq 'the_alias'
138
+ end
139
+
140
+ it 'casts the alias to a string' do
141
+ @o.as(:the_alias)
142
+ @o.alias.should eq 'the_alias'
143
+ end
144
+
145
+ end
146
+
147
+ end
148
+ end
149
+ end