squeel 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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