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