squeel 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/CHANGELOG.md +20 -0
  2. data/README.md +1 -1
  3. data/lib/squeel.rb +1 -2
  4. data/lib/squeel/adapters/active_record.rb +3 -1
  5. data/lib/squeel/adapters/active_record/3.0/compat.rb +5 -1
  6. data/lib/squeel/adapters/active_record/3.0/context.rb +2 -2
  7. data/lib/squeel/adapters/active_record/3.1/compat.rb +22 -0
  8. data/lib/squeel/adapters/active_record/3.1/context.rb +2 -2
  9. data/lib/squeel/adapters/active_record/compat.rb +22 -0
  10. data/lib/squeel/adapters/active_record/join_dependency_extensions.rb +2 -2
  11. data/lib/squeel/configuration.rb +2 -2
  12. data/lib/squeel/core_ext/symbol.rb +3 -13
  13. data/lib/squeel/dsl.rb +10 -1
  14. data/lib/squeel/nodes.rb +9 -1
  15. data/lib/squeel/nodes/aliasing.rb +2 -2
  16. data/lib/squeel/nodes/as.rb +1 -1
  17. data/lib/squeel/nodes/binary.rb +1 -4
  18. data/lib/squeel/nodes/function.rb +1 -10
  19. data/lib/squeel/nodes/grouping.rb +35 -0
  20. data/lib/squeel/nodes/join.rb +3 -3
  21. data/lib/squeel/nodes/key_path.rb +26 -24
  22. data/lib/squeel/nodes/literal.rb +2 -17
  23. data/lib/squeel/nodes/nary.rb +1 -2
  24. data/lib/squeel/nodes/ordering.rb +21 -0
  25. data/lib/squeel/nodes/predicate.rb +1 -4
  26. data/lib/squeel/nodes/predicate_methods.rb +16 -0
  27. data/lib/squeel/nodes/stub.rb +11 -21
  28. data/lib/squeel/nodes/unary.rb +1 -4
  29. data/lib/squeel/version.rb +1 -1
  30. data/lib/squeel/visitors/attribute_visitor.rb +12 -13
  31. data/lib/squeel/visitors/predicate_visitor.rb +38 -15
  32. data/lib/squeel/visitors/symbol_visitor.rb +2 -6
  33. data/lib/squeel/visitors/visitor.rb +24 -1
  34. data/spec/squeel/adapters/active_record/relation_extensions_spec.rb +6 -1
  35. data/spec/squeel/dsl_spec.rb +9 -1
  36. data/spec/squeel/nodes/grouping_spec.rb +177 -0
  37. data/spec/squeel/nodes/join_spec.rb +4 -5
  38. data/spec/squeel/nodes/key_path_spec.rb +3 -5
  39. data/spec/squeel/nodes/operators_spec.rb +3 -3
  40. data/spec/squeel/nodes/stub_spec.rb +6 -7
  41. data/spec/squeel/visitors/attribute_visitor_spec.rb +6 -1
  42. data/spec/squeel/visitors/predicate_visitor_spec.rb +28 -0
  43. metadata +62 -22
  44. data/lib/squeel/predicate_methods.rb +0 -14
data/CHANGELOG.md ADDED
@@ -0,0 +1,20 @@
1
+ # v1.0.2
2
+ ## 2012-05-30
3
+
4
+ * Add groupings to DSL. Allows control of matched sets of
5
+ parentheses in the absence of not/and/etc. Accessed via
6
+ `_()`.
7
+ * Allow As nodes in predicates. This allows casting inside
8
+ a where/having clause with PostgreSQL: `cast(value.as type)`
9
+ * Work around issue with Relation#count when where_values
10
+ contains InfixOperations. Fixes #122.
11
+
12
+ # v1.0.1
13
+ ## 2012-05-02
14
+
15
+ * Undefine `type` method on Stubs/KeyPaths for 1.8.x compat.
16
+
17
+ # v1.0.0
18
+ ## 2012-04-22
19
+
20
+ * Official 1.0.0 release.
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Squeel [![Build Status](https://secure.travis-ci.org/ernie/squeel.png)](http://travis-ci.org/ernie/squeel)
1
+ # Squeel [![Build Status](https://secure.travis-ci.org/ernie/squeel.png)](http://travis-ci.org/ernie/squeel) [![endorse](http://api.coderwall.com/ernie/endorsecount.png)](http://coderwall.com/ernie)
2
2
 
3
3
  Squeel lets you write your ActiveRecord queries with fewer strings, and more Ruby,
4
4
  by making the ARel awesomeness that lies beneath ActiveRecord more accessible.
data/lib/squeel.rb CHANGED
@@ -22,7 +22,6 @@ module Squeel
22
22
 
23
23
  end
24
24
 
25
- require 'squeel/nodes'
26
25
  require 'squeel/dsl'
27
26
  require 'squeel/visitors'
28
- require 'squeel/adapters/active_record'
27
+ require 'squeel/adapters/active_record'
@@ -16,6 +16,7 @@ when 3
16
16
  ActiveRecord::Associations::ClassMethods::JoinDependency.send :include, Squeel::Adapters::ActiveRecord::JoinDependencyExtensions
17
17
  ActiveRecord::Base.extend Squeel::Adapters::ActiveRecord::AssociationPreloadExtensions
18
18
  when 1
19
+ require 'squeel/adapters/active_record/3.1/compat'
19
20
  require 'squeel/adapters/active_record/3.1/relation_extensions'
20
21
  require 'squeel/adapters/active_record/3.1/preloader_extensions'
21
22
  require 'squeel/adapters/active_record/3.1/context'
@@ -24,6 +25,7 @@ when 3
24
25
  ActiveRecord::Associations::JoinDependency.send :include, Squeel::Adapters::ActiveRecord::JoinDependencyExtensions
25
26
  ActiveRecord::Associations::Preloader.send :include, Squeel::Adapters::ActiveRecord::PreloaderExtensions
26
27
  else
28
+ require 'squeel/adapters/active_record/compat'
27
29
  require 'squeel/adapters/active_record/relation_extensions'
28
30
  require 'squeel/adapters/active_record/preloader_extensions'
29
31
  require 'squeel/adapters/active_record/context'
@@ -34,4 +36,4 @@ when 3
34
36
  end
35
37
  else
36
38
  raise NotImplementedError, "Squeel does not support ActiveRecord version #{ActiveRecord::VERSION::STRING}"
37
- end
39
+ end
@@ -95,6 +95,10 @@ module Arel
95
95
  super(:-, left, right)
96
96
  end
97
97
  end
98
+
99
+ class Grouping < Unary
100
+ include Arel::Predications
101
+ end unless Grouping.include?(Arel::Predications)
98
102
  end
99
103
 
100
104
  module Visitors
@@ -157,4 +161,4 @@ module Arel
157
161
  end
158
162
  end
159
163
 
160
- end
164
+ end
@@ -38,7 +38,7 @@ module Squeel
38
38
 
39
39
  def traverse(keypath, parent = @base, include_endpoint = false)
40
40
  parent = @base if keypath.absolute?
41
- keypath.path.each do |key|
41
+ keypath.path_without_endpoint.each do |key|
42
42
  parent = find(key, parent) || key
43
43
  end
44
44
  parent = find(keypath.endpoint, parent) if include_endpoint
@@ -73,4 +73,4 @@ module Squeel
73
73
  end
74
74
  end
75
75
  end
76
- end
76
+ end
@@ -0,0 +1,22 @@
1
+ module Arel
2
+
3
+ module Nodes
4
+
5
+ class Grouping < Unary
6
+ include Arel::Predications
7
+ end unless Grouping.include?(Arel::Predications)
8
+
9
+ end
10
+
11
+ module Visitors
12
+
13
+ class DepthFirst < Visitor
14
+
15
+ unless method_defined?(:visit_Arel_Nodes_InfixOperation)
16
+ alias :visit_Arel_Nodes_InfixOperation :binary
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+ end
@@ -38,7 +38,7 @@ module Squeel
38
38
 
39
39
  def traverse(keypath, parent = @base, include_endpoint = false)
40
40
  parent = @base if keypath.absolute?
41
- keypath.path.each do |key|
41
+ keypath.path_without_endpoint.each do |key|
42
42
  parent = find(key, parent) || key
43
43
  end
44
44
  parent = find(keypath.endpoint, parent) if include_endpoint
@@ -73,4 +73,4 @@ module Squeel
73
73
  end
74
74
  end
75
75
  end
76
- end
76
+ end
@@ -0,0 +1,22 @@
1
+ module Arel
2
+
3
+ module Nodes
4
+
5
+ class Grouping < Unary
6
+ include Arel::Predications
7
+ end unless Grouping.include?(Arel::Predications)
8
+
9
+ end
10
+
11
+ module Visitors
12
+
13
+ class DepthFirst < Visitor
14
+
15
+ unless method_defined?(:visit_Arel_Nodes_InfixOperation)
16
+ alias :visit_Arel_Nodes_InfixOperation :binary
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+ end
@@ -21,7 +21,7 @@ module Squeel
21
21
 
22
22
  if Nodes::KeyPath === associations
23
23
  parent ||= _join_parts.last
24
- associations.path_with_endpoint.each do |key|
24
+ associations.path.each do |key|
25
25
  parent = build(key, parent, join_type)
26
26
  end
27
27
  parent
@@ -33,4 +33,4 @@ module Squeel
33
33
  end
34
34
  end
35
35
  end
36
- end
36
+ end
@@ -1,5 +1,5 @@
1
1
  require 'squeel/constants'
2
- require 'squeel/predicate_methods'
2
+ require 'squeel/nodes'
3
3
 
4
4
  module Squeel
5
5
  # The Squeel configuration module. The Squeel module extends this to provide its
@@ -46,7 +46,7 @@ module Squeel
46
46
  def alias_predicate(new_name, existing_name)
47
47
  raise ArgumentError, 'the existing name should be the base name, not an _any/_all variation' if existing_name.to_s =~ /(_any|_all)$/
48
48
  ['', '_any', '_all'].each do |suffix|
49
- PredicateMethods.class_eval "alias :#{new_name}#{suffix} :#{existing_name}#{suffix} unless defined?(#{new_name}#{suffix})"
49
+ Nodes::PredicateMethods.class_eval "alias :#{new_name}#{suffix} :#{existing_name}#{suffix} unless defined?(#{new_name}#{suffix})"
50
50
  end
51
51
  end
52
52
 
@@ -1,6 +1,3 @@
1
- require 'squeel/predicate_methods'
2
- require 'squeel/nodes/aliasing'
3
-
4
1
  # These extensions to Symbol are loaded optionally, mostly to provide
5
2
  # a small amount of backwards compatibility with MetaWhere.
6
3
  #
@@ -9,16 +6,9 @@ require 'squeel/nodes/aliasing'
9
6
  # config.load_core_extensions :symbol
10
7
  # end
11
8
  class Symbol
12
- include Squeel::PredicateMethods
9
+ include Squeel::Nodes::PredicateMethods
13
10
  include Squeel::Nodes::Aliasing
14
-
15
- def asc
16
- Squeel::Nodes::Order.new self, 1
17
- end
18
-
19
- def desc
20
- Squeel::Nodes::Order.new self, -1
21
- end
11
+ include Squeel::Nodes::Ordering
22
12
 
23
13
  def func(*args)
24
14
  Squeel::Nodes::Function.new(self, args)
@@ -36,4 +26,4 @@ class Symbol
36
26
  Squeel::Nodes::Join.new(self, Arel::InnerJoin, klass)
37
27
  end
38
28
 
39
- end
29
+ end
data/lib/squeel/dsl.rb CHANGED
@@ -71,6 +71,15 @@ module Squeel
71
71
  Nodes::Literal.new(string)
72
72
  end
73
73
 
74
+ # Create a Squeel Grouping node. This allows you to set balanced
75
+ # pairs of parentheses around your SQL.
76
+ #
77
+ # @param expr The expression to group
78
+ # @return [Nodes::Grouping] The grouping node
79
+ def _(expr)
80
+ Nodes::Grouping.new(expr)
81
+ end
82
+
74
83
  # Create a Squeel Sifter node. This essentially substitutes the
75
84
  # sifter block of the supplied name from the model.
76
85
  #
@@ -109,4 +118,4 @@ module Squeel
109
118
  end
110
119
 
111
120
  end
112
- end
121
+ end
data/lib/squeel/nodes.rb CHANGED
@@ -4,6 +4,13 @@ module Squeel
4
4
  module Nodes
5
5
  end
6
6
  end
7
+
8
+ require 'squeel/nodes/predicate_methods'
9
+ require 'squeel/nodes/operators'
10
+ require 'squeel/nodes/predicate_operators'
11
+ require 'squeel/nodes/aliasing'
12
+ require 'squeel/nodes/ordering'
13
+
7
14
  require 'squeel/nodes/literal'
8
15
  require 'squeel/nodes/stub'
9
16
  require 'squeel/nodes/key_path'
@@ -16,4 +23,5 @@ require 'squeel/nodes/and'
16
23
  require 'squeel/nodes/or'
17
24
  require 'squeel/nodes/as'
18
25
  require 'squeel/nodes/not'
19
- require 'squeel/nodes/join'
26
+ require 'squeel/nodes/join'
27
+ require 'squeel/nodes/grouping'
@@ -5,9 +5,9 @@ module Squeel
5
5
  module Aliasing
6
6
 
7
7
  def as(name)
8
- As.new(self, Arel.sql(name))
8
+ As.new(self, Arel.sql(name.to_s))
9
9
  end
10
10
 
11
11
  end
12
12
  end
13
- end
13
+ end
@@ -14,4 +14,4 @@ module Squeel
14
14
  end
15
15
  end
16
16
  end
17
- end
17
+ end
@@ -1,5 +1,3 @@
1
- require 'squeel/nodes/predicate_operators'
2
-
3
1
  module Squeel
4
2
  module Nodes
5
3
  # A node that represents an operation with two operands.
@@ -25,8 +23,7 @@ module Squeel
25
23
  self.left.eql?(other.left) &&
26
24
  self.right.eql?(other.right)
27
25
  end
28
- alias :== :eql?
29
26
 
30
27
  end
31
28
  end
32
- end
29
+ end
@@ -1,5 +1,3 @@
1
- require 'squeel/predicate_methods'
2
-
3
1
  module Squeel
4
2
  module Nodes
5
3
  # A node that represents an SQL function call
@@ -8,6 +6,7 @@ module Squeel
8
6
  include PredicateMethods
9
7
  include PredicateOperators
10
8
  include Operators
9
+ include Ordering
11
10
 
12
11
  alias :== :eq
13
12
  alias :'^' :not_eq
@@ -46,14 +45,6 @@ module Squeel
46
45
  self
47
46
  end
48
47
 
49
- def asc
50
- Order.new self, 1
51
- end
52
-
53
- def desc
54
- Order.new self, -1
55
- end
56
-
57
48
  # expand_hash_conditions_for_aggregates assumes our hash keys can be
58
49
  # converted to symbols, so this has to be implemented, but it doesn't
59
50
  # really have to do anything useful.
@@ -0,0 +1,35 @@
1
+ module Squeel
2
+ module Nodes
3
+ # A node that represents an SQL function call
4
+ class Grouping < Unary
5
+
6
+ include PredicateMethods
7
+ include PredicateOperators
8
+ include Operators
9
+ include Aliasing
10
+ include Ordering
11
+
12
+ alias :== :eq
13
+ alias :'^' :not_eq
14
+ alias :'!=' :not_eq if respond_to?(:'!=')
15
+ alias :>> :in
16
+ alias :<< :not_in
17
+ alias :=~ :matches
18
+ alias :'!~' :does_not_match if respond_to?(:'!~')
19
+ alias :> :gt
20
+ alias :>= :gteq
21
+ alias :< :lt
22
+ alias :<= :lteq
23
+
24
+ # expand_hash_conditions_for_aggregates assumes our hash keys can be
25
+ # converted to symbols, so this has to be implemented, but it doesn't
26
+ # really have to do anything useful.
27
+ # @return [NilClass] Just to avoid bombing out on expand_hash_conditions_for_aggregates
28
+ def to_sym
29
+ nil
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+
@@ -61,9 +61,9 @@ module Squeel
61
61
  def method_missing(method_id, *args)
62
62
  super if method_id == :to_ary
63
63
  if (args.size == 1) && (Class === args[0])
64
- KeyPath.new(self, Join.new(method_id, Arel::InnerJoin, args[0]))
64
+ KeyPath.new([self, Join.new(method_id, Arel::InnerJoin, args[0])])
65
65
  else
66
- KeyPath.new(self, method_id)
66
+ KeyPath.new([self, method_id])
67
67
  end
68
68
  end
69
69
 
@@ -72,7 +72,7 @@ module Squeel
72
72
  # DSL is likely to think of them as such.
73
73
  # @return [KeyPath] An absolute KeyPath, containing only this Join
74
74
  def ~
75
- KeyPath.new [], self, true
75
+ KeyPath.new [self], true
76
76
  end
77
77
 
78
78
  # expand_hash_conditions_for_aggregates assumes our hash keys can be
@@ -1,5 +1,3 @@
1
- require 'squeel/nodes/operators'
2
- require 'squeel/nodes/predicate_operators'
3
1
  require 'active_support/core_ext/module'
4
2
 
5
3
  module Squeel
@@ -20,18 +18,13 @@ module Squeel
20
18
  # @return [Array<Symbol, Stub, Join>] The path
21
19
  attr_reader :path
22
20
 
23
- # @return The endpoint, either another key as in the path, or a predicate, function, etc.
24
- attr_reader :endpoint
25
-
26
21
  # Create a new KeyPath.
27
22
  # @param [Array, Object] path The intial path. Will be converted to an array if it isn't already.
28
- # @param endpoint the endpoint of the KeyPath
29
23
  # @param [Boolean] absolute If the KeyPath should start from the base
30
24
  # or remain relative to whatever location it's found.
31
- def initialize(path, endpoint, absolute = false)
32
- @path, @endpoint = path, endpoint
33
- @path = [@path] unless Array === @path
34
- @endpoint = Stub.new(@endpoint) if Symbol === @endpoint
25
+ def initialize(path, absolute = false)
26
+ @path = Array(path)
27
+ self.endpoint = Stub.new(endpoint) if Symbol === endpoint
35
28
  @absolute = absolute
36
29
  end
37
30
 
@@ -42,11 +35,22 @@ module Squeel
42
35
  @absolute
43
36
  end
44
37
 
38
+ # @return The endpoint, either another key as in the path, or a predicate, function, etc.
39
+ def endpoint
40
+ @path[-1]
41
+ end
42
+
43
+ # Set the new value of the KeyPath's endpoint.
44
+ # @param [Object] val The new endpoint.
45
+ # @return The value just set.
46
+ def endpoint=(val)
47
+ @path[-1] = val
48
+ end
49
+
45
50
  # Object comparison
46
51
  def eql?(other)
47
52
  self.class.eql?(other.class) &&
48
53
  self.path.eql?(other.path) &&
49
- self.endpoint.eql?(other.endpoint) &&
50
54
  self.absolute?.eql?(other.absolute?)
51
55
  end
52
56
 
@@ -120,8 +124,7 @@ module Squeel
120
124
  # @return [KeyPath] This keypath, with a sifter as its endpoint
121
125
  def sift(name, *args)
122
126
  if Stub === endpoint || Join === endpoint
123
- @path << endpoint
124
- @endpoint = Sifter.new(name, args)
127
+ @path << Sifter.new(name, args)
125
128
  self
126
129
  else
127
130
  no_method_error :sift
@@ -137,7 +140,7 @@ module Squeel
137
140
 
138
141
  # For use with equality tests
139
142
  def hash
140
- [self.class, endpoint, *path].hash
143
+ [self.class, *path].hash
141
144
  end
142
145
 
143
146
  # expand_hash_conditions_for_aggregates assumes our hash keys can be
@@ -162,14 +165,14 @@ module Squeel
162
165
  end
163
166
  end
164
167
 
165
- # @return [Array] The KeyPath's path, including its endpoint, as a single array.
166
- def path_with_endpoint
167
- path + [endpoint]
168
+ # @return [Array] The KeyPath's path, minus its endpoint, as a single array.
169
+ def path_without_endpoint
170
+ path[0..-2]
168
171
  end
169
172
 
170
- # Implement (and alias to :to_str) to play nicely with ActiveRecord grouped calculations
173
+ # Implement #to_s (and alias to #to_str) to play nicely with ActiveRecord grouped calculations
171
174
  def to_s
172
- path.map(&:to_s).join('.') << ".#{endpoint}"
175
+ path.map(&:to_s).join('.')
173
176
  end
174
177
  alias :to_str :to_s
175
178
 
@@ -179,16 +182,15 @@ module Squeel
179
182
  super if method_id == :to_ary
180
183
 
181
184
  if endpoint.respond_to? method_id
182
- @endpoint = @endpoint.send(method_id, *args)
185
+ self.endpoint = endpoint.send(method_id, *args)
183
186
  self
184
187
  elsif Stub === endpoint || Join === endpoint
185
- @path << endpoint
186
188
  if args.empty?
187
- @endpoint = Stub.new(method_id)
189
+ @path << Stub.new(method_id)
188
190
  elsif (args.size == 1) && (Class === args[0])
189
- @endpoint = Join.new(method_id, Arel::InnerJoin, args[0])
191
+ @path << Join.new(method_id, Arel::InnerJoin, args[0])
190
192
  else
191
- @endpoint = Nodes::Function.new method_id, args
193
+ @path << Nodes::Function.new(method_id, args)
192
194
  end
193
195
  self
194
196
  else