squeel 1.0.1 → 1.0.2

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