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,35 @@
1
+ module Squeel
2
+ module Nodes
3
+ class Nary
4
+ include PredicateOperators
5
+
6
+ attr_reader :children
7
+
8
+ def initialize(children)
9
+ raise ArgumentError, '#{self.class} requires an array' unless Array === children
10
+ # We don't dup here, as incoming arrays should be created by the
11
+ # Operators#& method on other nodes. If you're creating And nodes
12
+ # manually, by sure that they're new arays.
13
+ @children = children
14
+ end
15
+
16
+ def &(other)
17
+ @children << other
18
+ self
19
+ end
20
+
21
+ def -(other)
22
+ @children << Not.new(other)
23
+ self
24
+ end
25
+
26
+ def eql?(other)
27
+ self.class == other.class &&
28
+ self.children == other.children
29
+ end
30
+
31
+ alias :== :eql?
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,8 @@
1
+ require 'squeel/nodes/unary'
2
+
3
+ module Squeel
4
+ module Nodes
5
+ class Not < Unary
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,23 @@
1
+ require 'squeel/nodes/function'
2
+
3
+ module Squeel
4
+ module Nodes
5
+ class Operation < Function
6
+
7
+ def initialize(left, operator, right)
8
+ super(operator, [left, right])
9
+ end
10
+
11
+ alias :operator :name
12
+
13
+ def left
14
+ args[0]
15
+ end
16
+
17
+ def right
18
+ args[1]
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ module Squeel
2
+ module Nodes
3
+ module Operators
4
+
5
+ def +(value)
6
+ Operation.new(self, :+, value)
7
+ end
8
+
9
+ def -(value)
10
+ Operation.new(self, :-, value)
11
+ end
12
+
13
+ def *(value)
14
+ Operation.new(self, :*, value)
15
+ end
16
+
17
+ def /(value)
18
+ Operation.new(self, :/, value)
19
+ end
20
+
21
+ def op(operator, value)
22
+ Operation.new(self, operator, value)
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,8 @@
1
+ require 'squeel/nodes/binary'
2
+
3
+ module Squeel
4
+ module Nodes
5
+ class Or < Binary
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,35 @@
1
+ module Squeel
2
+ module Nodes
3
+ class Order
4
+ attr_reader :expr, :direction
5
+
6
+ def initialize(expr, direction = 1)
7
+ raise ArgumentError, "Direction #{direction} is not valid. Must be -1 or 1." unless [-1,1].include? direction
8
+ @expr, @direction = expr, direction
9
+ end
10
+
11
+ def asc
12
+ @direction = 1
13
+ self
14
+ end
15
+
16
+ def desc
17
+ @direction = -1
18
+ self
19
+ end
20
+
21
+ def ascending?
22
+ @direction == 1
23
+ end
24
+
25
+ def descending?
26
+ @direction == -1
27
+ end
28
+
29
+ def reverse!
30
+ @direction = - @direction
31
+ self
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,49 @@
1
+ require 'squeel/predicate_methods'
2
+ require 'squeel/nodes/predicate_operators'
3
+
4
+ module Squeel
5
+ module Nodes
6
+ class Predicate
7
+
8
+ include PredicateMethods
9
+ include PredicateOperators
10
+
11
+ attr_accessor :value
12
+ attr_reader :expr, :method_name
13
+
14
+ def initialize(expr, method_name = :eq, value = :__undefined__)
15
+ @expr, @method_name, @value = expr, method_name, value
16
+ end
17
+
18
+ def eql?(other)
19
+ self.class.eql?(other.class) &&
20
+ self.expr.eql?(other.expr) &&
21
+ self.method_name.eql?(other.method_name) &&
22
+ self.value.eql?(other.value)
23
+ end
24
+
25
+ alias :== :eql?
26
+
27
+ def hash
28
+ [self.class, expr, method_name, value].hash
29
+ end
30
+
31
+ def value?
32
+ @value != :__undefined__
33
+ end
34
+
35
+ def %(val)
36
+ @value = val
37
+ self
38
+ end
39
+
40
+ # expand_hash_conditions_for_aggregates assumes our hash keys can be
41
+ # converted to symbols, so this has to be implemented, but it doesn't
42
+ # really have to do anything useful.
43
+ def to_sym
44
+ nil
45
+ end
46
+
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,17 @@
1
+ module Squeel
2
+ module Nodes
3
+ module PredicateOperators
4
+ def |(other)
5
+ Or.new(self, other)
6
+ end
7
+
8
+ def &(other)
9
+ And.new([self, other])
10
+ end
11
+
12
+ def -@
13
+ Not.new(self)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,113 @@
1
+ require 'squeel/predicate_methods'
2
+ require 'squeel/nodes/operators'
3
+
4
+ module Squeel
5
+ module Nodes
6
+ class Stub
7
+
8
+ include PredicateMethods
9
+ include Operators
10
+
11
+ attr_reader :symbol
12
+
13
+ def initialize(symbol)
14
+ @symbol = symbol
15
+ end
16
+
17
+ def eql?(other)
18
+ self.class == other.class &&
19
+ self.symbol == other.symbol
20
+ end
21
+
22
+ def hash
23
+ symbol.hash
24
+ end
25
+
26
+ def to_sym
27
+ symbol
28
+ end
29
+
30
+ def to_s
31
+ symbol.to_s
32
+ end
33
+
34
+ def method_missing(method_id, *args)
35
+ super if method_id == :to_ary
36
+ KeyPath.new(self.symbol, method_id)
37
+ end
38
+
39
+ def ==(value)
40
+ Predicate.new self.symbol, :eq, value
41
+ end
42
+
43
+ # Won't work on Ruby 1.8.x so need to do this conditionally
44
+ define_method('!=') do |value|
45
+ Predicate.new(self.symbol, :not_eq, value)
46
+ end if respond_to?('!=')
47
+
48
+ def ^(value)
49
+ Predicate.new self.symbol, :not_eq, value
50
+ end
51
+
52
+ def >>(value)
53
+ Predicate.new self.symbol, :in, value
54
+ end
55
+
56
+ def <<(value)
57
+ Predicate.new self.symbol, :not_in, value
58
+ end
59
+
60
+ def =~(value)
61
+ Predicate.new self.symbol, :matches, value
62
+ end
63
+
64
+ # Won't work on Ruby 1.8.x so need to do this conditionally
65
+ define_method('!~') do |value|
66
+ Predicate.new(self.symbol, :does_not_match, value)
67
+ end if respond_to?('!~')
68
+
69
+ def >(value)
70
+ Predicate.new self.symbol, :gt, value
71
+ end
72
+
73
+ def >=(value)
74
+ Predicate.new self.symbol, :gteq, value
75
+ end
76
+
77
+ def <(value)
78
+ Predicate.new self.symbol, :lt, value
79
+ end
80
+
81
+ def <=(value)
82
+ Predicate.new self.symbol, :lteq, value
83
+ end
84
+
85
+ def asc
86
+ Order.new self.symbol, 1
87
+ end
88
+
89
+ def desc
90
+ Order.new self.symbol, -1
91
+ end
92
+
93
+ def func(*args)
94
+ Function.new(self.symbol, args)
95
+ end
96
+
97
+ alias :[] :func
98
+
99
+ def inner
100
+ Join.new(self.symbol, Arel::InnerJoin)
101
+ end
102
+
103
+ def outer
104
+ Join.new(self.symbol, Arel::OuterJoin)
105
+ end
106
+
107
+ def of_class(klass)
108
+ Join.new(self.symbol, Arel::InnerJoin, klass)
109
+ end
110
+
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,22 @@
1
+ require 'squeel/nodes/predicate_operators'
2
+
3
+ module Squeel
4
+ module Nodes
5
+ class Unary
6
+ include PredicateOperators
7
+
8
+ attr_reader :expr
9
+
10
+ def initialize(expr)
11
+ @expr = expr
12
+ end
13
+
14
+ def eql?(other)
15
+ self.class == other.class &&
16
+ self.expr == other.expr
17
+ end
18
+
19
+ alias :== :eql?
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ require 'squeel/predicate_methods/symbol'
2
+ require 'squeel/predicate_methods/stub'
3
+ require 'squeel/predicate_methods/predicate'
4
+ require 'squeel/predicate_methods/function'
5
+
6
+ module Squeel
7
+ module PredicateMethods
8
+
9
+ def self.included(base)
10
+ base.send :include, const_get(base.name.split(/::/)[-1].to_sym)
11
+ end
12
+
13
+ Constants::PREDICATES.each do |method_name|
14
+ class_eval <<-RUBY
15
+ def #{method_name}(value = :__undefined__)
16
+ predicate :#{method_name}, value
17
+ end
18
+ RUBY
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,9 @@
1
+ module Squeel
2
+ module PredicateMethods
3
+ module Function
4
+ def predicate(method_name, value = :__undefined__)
5
+ Nodes::Predicate.new self, method_name, value
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,11 @@
1
+ module Squeel
2
+ module PredicateMethods
3
+ module Predicate
4
+ def predicate(method_name, value = :__undefined__)
5
+ @method_name = method_name
6
+ @value = value unless value == :__undefined__
7
+ self
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ module Squeel
2
+ module PredicateMethods
3
+ module Stub
4
+ def predicate(method_name, value = :__undefined__)
5
+ Nodes::Predicate.new self.symbol, method_name, value
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Squeel
2
+ module PredicateMethods
3
+ module Symbol
4
+ def predicate(method_name, value = :__undefined__)
5
+ Nodes::Predicate.new self, method_name, value
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module Squeel
2
+ VERSION = "0.5.0"
3
+ end
@@ -0,0 +1,3 @@
1
+ require 'squeel/visitors/predicate_visitor'
2
+ require 'squeel/visitors/order_visitor'
3
+ require 'squeel/visitors/select_visitor'
@@ -0,0 +1,46 @@
1
+ require 'active_support/core_ext/module'
2
+ require 'squeel/nodes'
3
+
4
+ module Squeel
5
+ module Visitors
6
+ class Base
7
+ attr_accessor :context
8
+ delegate :contextualize, :find, :traverse, :sanitize_sql, :engine, :arel_visitor, :to => :context
9
+
10
+ def initialize(context = nil)
11
+ @context = context
12
+ end
13
+
14
+ def accept(object, parent = context.base)
15
+ visit(object, parent)
16
+ end
17
+
18
+ def can_accept?(object)
19
+ respond_to? DISPATCH[object.class]
20
+ end
21
+
22
+ def self.can_accept?(object)
23
+ method_defined? DISPATCH[object.class]
24
+ end
25
+
26
+ private
27
+
28
+ DISPATCH = Hash.new do |hash, klass|
29
+ hash[klass] = "visit_#{klass.name.gsub('::', '_')}"
30
+ end
31
+
32
+ def quoted?(object)
33
+ case object
34
+ when Arel::Nodes::SqlLiteral, Bignum, Fixnum
35
+ false
36
+ else
37
+ true
38
+ end
39
+ end
40
+
41
+ def visit(object, parent)
42
+ send(DISPATCH[object.class], object, parent)
43
+ end
44
+ end
45
+ end
46
+ end