arel 0.1.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 (132) hide show
  1. data/.gitignore +6 -0
  2. data/README.markdown +184 -0
  3. data/Rakefile +60 -0
  4. data/VERSION +1 -0
  5. data/arel.gemspec +233 -0
  6. data/doc/CONVENTIONS +17 -0
  7. data/doc/TODO +118 -0
  8. data/lib/arel.rb +10 -0
  9. data/lib/arel/algebra.rb +4 -0
  10. data/lib/arel/algebra/extensions.rb +4 -0
  11. data/lib/arel/algebra/extensions/class.rb +32 -0
  12. data/lib/arel/algebra/extensions/hash.rb +11 -0
  13. data/lib/arel/algebra/extensions/object.rb +17 -0
  14. data/lib/arel/algebra/extensions/symbol.rb +9 -0
  15. data/lib/arel/algebra/predicates.rb +41 -0
  16. data/lib/arel/algebra/primitives.rb +5 -0
  17. data/lib/arel/algebra/primitives/attribute.rb +150 -0
  18. data/lib/arel/algebra/primitives/expression.rb +43 -0
  19. data/lib/arel/algebra/primitives/ordering.rb +23 -0
  20. data/lib/arel/algebra/primitives/value.rb +14 -0
  21. data/lib/arel/algebra/relations.rb +14 -0
  22. data/lib/arel/algebra/relations/operations/alias.rb +7 -0
  23. data/lib/arel/algebra/relations/operations/group.rb +12 -0
  24. data/lib/arel/algebra/relations/operations/join.rb +64 -0
  25. data/lib/arel/algebra/relations/operations/order.rb +18 -0
  26. data/lib/arel/algebra/relations/operations/project.rb +20 -0
  27. data/lib/arel/algebra/relations/operations/skip.rb +6 -0
  28. data/lib/arel/algebra/relations/operations/take.rb +10 -0
  29. data/lib/arel/algebra/relations/operations/where.rb +16 -0
  30. data/lib/arel/algebra/relations/relation.rb +136 -0
  31. data/lib/arel/algebra/relations/row.rb +26 -0
  32. data/lib/arel/algebra/relations/utilities/compound.rb +30 -0
  33. data/lib/arel/algebra/relations/utilities/externalization.rb +24 -0
  34. data/lib/arel/algebra/relations/utilities/nil.rb +7 -0
  35. data/lib/arel/algebra/relations/writes.rb +36 -0
  36. data/lib/arel/engines.rb +2 -0
  37. data/lib/arel/engines/memory.rb +4 -0
  38. data/lib/arel/engines/memory/engine.rb +16 -0
  39. data/lib/arel/engines/memory/predicates.rb +35 -0
  40. data/lib/arel/engines/memory/primitives.rb +27 -0
  41. data/lib/arel/engines/memory/relations.rb +5 -0
  42. data/lib/arel/engines/memory/relations/array.rb +25 -0
  43. data/lib/arel/engines/memory/relations/compound.rb +9 -0
  44. data/lib/arel/engines/memory/relations/operations.rb +61 -0
  45. data/lib/arel/engines/memory/relations/writes.rb +7 -0
  46. data/lib/arel/engines/sql.rb +7 -0
  47. data/lib/arel/engines/sql/christener.rb +13 -0
  48. data/lib/arel/engines/sql/engine.rb +37 -0
  49. data/lib/arel/engines/sql/extensions.rb +4 -0
  50. data/lib/arel/engines/sql/extensions/array.rb +16 -0
  51. data/lib/arel/engines/sql/extensions/nil_class.rb +11 -0
  52. data/lib/arel/engines/sql/extensions/object.rb +15 -0
  53. data/lib/arel/engines/sql/extensions/range.rb +15 -0
  54. data/lib/arel/engines/sql/formatters.rb +113 -0
  55. data/lib/arel/engines/sql/predicates.rb +51 -0
  56. data/lib/arel/engines/sql/primitives.rb +85 -0
  57. data/lib/arel/engines/sql/relations.rb +9 -0
  58. data/lib/arel/engines/sql/relations/operations/alias.rb +5 -0
  59. data/lib/arel/engines/sql/relations/operations/join.rb +33 -0
  60. data/lib/arel/engines/sql/relations/relation.rb +50 -0
  61. data/lib/arel/engines/sql/relations/table.rb +52 -0
  62. data/lib/arel/engines/sql/relations/utilities/compound.rb +10 -0
  63. data/lib/arel/engines/sql/relations/utilities/externalization.rb +14 -0
  64. data/lib/arel/engines/sql/relations/utilities/nil.rb +6 -0
  65. data/lib/arel/engines/sql/relations/utilities/recursion.rb +13 -0
  66. data/lib/arel/engines/sql/relations/writes.rb +39 -0
  67. data/lib/arel/session.rb +48 -0
  68. data/spec/arel/algebra/unit/predicates/binary_spec.rb +33 -0
  69. data/spec/arel/algebra/unit/predicates/equality_spec.rb +27 -0
  70. data/spec/arel/algebra/unit/predicates/in_spec.rb +10 -0
  71. data/spec/arel/algebra/unit/primitives/attribute_spec.rb +183 -0
  72. data/spec/arel/algebra/unit/primitives/expression_spec.rb +45 -0
  73. data/spec/arel/algebra/unit/primitives/value_spec.rb +15 -0
  74. data/spec/arel/algebra/unit/relations/alias_spec.rb +16 -0
  75. data/spec/arel/algebra/unit/relations/delete_spec.rb +9 -0
  76. data/spec/arel/algebra/unit/relations/group_spec.rb +10 -0
  77. data/spec/arel/algebra/unit/relations/insert_spec.rb +9 -0
  78. data/spec/arel/algebra/unit/relations/join_spec.rb +26 -0
  79. data/spec/arel/algebra/unit/relations/order_spec.rb +21 -0
  80. data/spec/arel/algebra/unit/relations/project_spec.rb +34 -0
  81. data/spec/arel/algebra/unit/relations/relation_spec.rb +188 -0
  82. data/spec/arel/algebra/unit/relations/skip_spec.rb +10 -0
  83. data/spec/arel/algebra/unit/relations/table_spec.rb +39 -0
  84. data/spec/arel/algebra/unit/relations/take_spec.rb +10 -0
  85. data/spec/arel/algebra/unit/relations/update_spec.rb +9 -0
  86. data/spec/arel/algebra/unit/relations/where_spec.rb +18 -0
  87. data/spec/arel/algebra/unit/session/session_spec.rb +84 -0
  88. data/spec/arel/engines/memory/integration/joins/cross_engine_spec.rb +48 -0
  89. data/spec/arel/engines/memory/unit/relations/array_spec.rb +32 -0
  90. data/spec/arel/engines/memory/unit/relations/insert_spec.rb +28 -0
  91. data/spec/arel/engines/memory/unit/relations/join_spec.rb +31 -0
  92. data/spec/arel/engines/memory/unit/relations/order_spec.rb +27 -0
  93. data/spec/arel/engines/memory/unit/relations/project_spec.rb +27 -0
  94. data/spec/arel/engines/memory/unit/relations/skip_spec.rb +26 -0
  95. data/spec/arel/engines/memory/unit/relations/take_spec.rb +26 -0
  96. data/spec/arel/engines/memory/unit/relations/where_spec.rb +39 -0
  97. data/spec/arel/engines/sql/integration/joins/with_adjacency_spec.rb +209 -0
  98. data/spec/arel/engines/sql/integration/joins/with_aggregations_spec.rb +167 -0
  99. data/spec/arel/engines/sql/integration/joins/with_compounds_spec.rb +107 -0
  100. data/spec/arel/engines/sql/unit/engine_spec.rb +45 -0
  101. data/spec/arel/engines/sql/unit/predicates/binary_spec.rb +117 -0
  102. data/spec/arel/engines/sql/unit/predicates/equality_spec.rb +46 -0
  103. data/spec/arel/engines/sql/unit/predicates/in_spec.rb +86 -0
  104. data/spec/arel/engines/sql/unit/predicates/predicates_spec.rb +65 -0
  105. data/spec/arel/engines/sql/unit/primitives/attribute_spec.rb +32 -0
  106. data/spec/arel/engines/sql/unit/primitives/expression_spec.rb +24 -0
  107. data/spec/arel/engines/sql/unit/primitives/literal_spec.rb +23 -0
  108. data/spec/arel/engines/sql/unit/primitives/value_spec.rb +29 -0
  109. data/spec/arel/engines/sql/unit/relations/alias_spec.rb +43 -0
  110. data/spec/arel/engines/sql/unit/relations/delete_spec.rb +63 -0
  111. data/spec/arel/engines/sql/unit/relations/group_spec.rb +56 -0
  112. data/spec/arel/engines/sql/unit/relations/insert_spec.rb +107 -0
  113. data/spec/arel/engines/sql/unit/relations/join_spec.rb +57 -0
  114. data/spec/arel/engines/sql/unit/relations/order_spec.rb +113 -0
  115. data/spec/arel/engines/sql/unit/relations/project_spec.rb +110 -0
  116. data/spec/arel/engines/sql/unit/relations/skip_spec.rb +32 -0
  117. data/spec/arel/engines/sql/unit/relations/table_spec.rb +69 -0
  118. data/spec/arel/engines/sql/unit/relations/take_spec.rb +32 -0
  119. data/spec/arel/engines/sql/unit/relations/update_spec.rb +151 -0
  120. data/spec/arel/engines/sql/unit/relations/where_spec.rb +56 -0
  121. data/spec/connections/mysql_connection.rb +16 -0
  122. data/spec/connections/postgresql_connection.rb +15 -0
  123. data/spec/connections/sqlite3_connection.rb +25 -0
  124. data/spec/doubles/hash.rb +23 -0
  125. data/spec/matchers/be_like.rb +24 -0
  126. data/spec/matchers/disambiguate_attributes.rb +28 -0
  127. data/spec/matchers/hash_the_same_as.rb +26 -0
  128. data/spec/schemas/mysql_schema.rb +18 -0
  129. data/spec/schemas/postgresql_schema.rb +18 -0
  130. data/spec/schemas/sqlite3_schema.rb +18 -0
  131. data/spec/spec_helper.rb +47 -0
  132. metadata +250 -0
@@ -0,0 +1,17 @@
1
+ This file should ultimately be replaced by a series of tests, something like a lint tool.
2
+
3
+ - all describes and its should use single quotes unless they have nested quotes.
4
+ - object instantiation in tests for all objects that are not the SUT should be manufactured in a before
5
+ - 'should' and other counterfactuals/subjunctive forms should not be used in tests
6
+ - no doubles should be used except for behaviorist testing
7
+ - behaviorist testing is desirable only when interacting with the database or the session
8
+ - when unit testing, always demonstrate behavior by using a real world example (as in, a public use of the API), so as to provide documentation.
9
+ - use collect rather than map
10
+ - jargon:
11
+ - 'obtains' is preferred to 'returns true'
12
+ - 'manufactures'
13
+ - in tests
14
+ - when manufacturing expected values (right-hand-side of should), avoid convenience methods -- construct it by initializing the object directly (Foo.new(...)). This ensures equality expectations in tests are rigorous.
15
+ - the SUT should be manufactured inline inside the test, not in a before
16
+ - dependencies for the SUT should be manufactured using convenience methods (or whatever is most terse).
17
+ - group conceptually related methods in a class within an inline module; immediately include that module.
@@ -0,0 +1,118 @@
1
+ todo:
2
+ - fix AR again
3
+ - blocks for joins
4
+ - fix sql insertions
5
+ - implement mnesia adapter
6
+
7
+ - CLEANUP!!!!!
8
+ - rename externalize to derived.
9
+ - deal with table tests in algebra
10
+ - fix grouping
11
+ - audit unit coverage of algebra
12
+ - data objects
13
+ - remove all explicit aliasing
14
+ - scoped writes
15
+ - refactor adapter pattern
16
+ - break out adapters into sep modules
17
+ - projection is by definition distincting?
18
+ - union/intersection
19
+ - cache expiry on write
20
+ - transactions
21
+
22
+ done:
23
+ - and/or w/ predicates
24
+ . Relation <=> Relation -> InnerJoinOperation
25
+ . Relation << Relation -> LeftOuterJoinOperation
26
+ . InnerJoinOperation.on(*Predicate) -> InnerJoinRelation
27
+ . LeftOuterJoinOperation.on(*Predicate) -> LeftOuterJoinRelation
28
+ . Relation[Symbol] -> Attribute
29
+ . Relation[Range] -> Relation
30
+ . Attribute == Attribute -> EqualityPredicate
31
+ . Attribute >= Attribute -> GreaterThanOrEqualToPredicate
32
+ . Relation.include?(Column) -> Predicate
33
+ . Relation.project(*Column) -> ProjectRelation
34
+ . Relation.select(*Predicate) -> SelectionRelation
35
+ . Relation.order(*Column) -> OrderRelation
36
+ . #to_sql
37
+ . Remove Builder
38
+ . Namespace
39
+ . Audit SqlAlchemy for missing features
40
+ - Generalized denormalizations on any aggregation (count, yes, but also max, min, average)
41
+ - Remove operator overloading of << and <=> for joins. Make it just foo.join(bar) and foo.outer_join(bar).
42
+ - Remove operator overloading of == for predicates. make it a.eq(b) (note lack of question mark).
43
+ - hookup more predicates (=, <=, =>)
44
+ - get some basic aggregations working: users.project(user[:points].max)
45
+ - Alias Table Names
46
+ - When joining with any sort of aggregation, it needs to be a nested select
47
+ - get a value select working: users.project(users[:name], addresses.select(addresses[:user_id] == users[:id]).project(addresses[:id].count))
48
+ - Session
49
+ - sublimate values to deal with the fact that they must be quoted per engine
50
+ - clean-up singleton monstrosity
51
+ - extract hashing module
52
+ - hash custom matcher
53
+ - make session engine stuff follow laws of demeter - currently doing some odd method chaining? rethink who is responsible for what
54
+ - session just calls execute, passing in a connection; by default it gets a connection from the relation.
55
+ - #formatter is now on value, attribute and relation; you must admit it's name is confusing given that e.g., relation already has a formatter (Sql::Relation) ... should it be called predicate formatter? operand1.to_sql(operand2.predicate) maybe prefer operand1.cast(operand2) or project or in light of
56
+ - renamed to #format: operand1.format(operand2)
57
+ - rename sql strategies
58
+ - need to_sql for ranges
59
+ - {:conditions=>{:id=>2..3}}
60
+ - nested orderings
61
+ - string passthrough
62
+ - conditions
63
+ - orderings
64
+ - relation inclusion when given an array (1,2,3,4) should quote the elements using the appropriate quoting formatter taken from the attribute
65
+ - descend on array, along with bind written in terms of it
66
+ - re-evaluate bind -- does bind belong inside the relation / predicate classes or in the factory methods?
67
+ - string passthrough:
68
+ :joins=>"INNER JOIN posts ON comments.post_id = posts.id"
69
+ - finish pending tests
70
+ - test relation, table reset
71
+ - test Value, in particular bind.
72
+ - test blank checks in relation.rb
73
+ - rename active_relation to arel
74
+ - mock out database
75
+ - fix complex joining cases:
76
+ - active record query adapter
77
+ - anonymous table names
78
+ - Explicitly model recursive structural decomposition / polymorphism
79
+ - Explicitly model the namer/externalizer using interpreter jargon
80
+ - All Sql Strategies should be accumulations with the top-level relation?
81
+ - instance methodify externalize
82
+ - test: find_attribute_given_attribute and all @attribute ||= everywhere and memoization of table class.
83
+ - rename select to where
84
+ - rename all ion classes
85
+ - joining with LIMIT is like aggregations!!
86
+ - blocks for non-joins
87
+ - expressions should be class-based, and joins too, anything _sql should be renamed
88
+ - implement in memory adapter
89
+ - clean up block_given stuff
90
+ - reorganize sql tests
91
+ - recursive memory operations
92
+ - reorganize memory tests
93
+ - result sets to attr correlation too
94
+ - implement joins in memory
95
+ - result sets should be array relations
96
+ - fix AR
97
+ - insertions for in memory
98
+ - cross-engine joins
99
+
100
+ icebox:
101
+ - #bind in Attribute and Expression should be doing a descend?
102
+ - try to make aggegration testing in join spec to be a bit more unit-like
103
+ - standardize quoting
104
+ - use strings everywhere, not symbols ?
105
+ - "unit" test sql strategies
106
+ - use real world examples, so they should be like a tutorial.
107
+ - rename the tion (Selection) classes so that words that don't end in tion don't seem inconsistent
108
+ - consider this code from has_many:
109
+ # replace the SELECT clause with COUNT(*), preserving any hints within /* ... */
110
+ @reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
111
+ - lock
112
+ - SELECT suchandsuch FOR UPDATE
113
+ - joins become subselects in writes:
114
+ users.delete().where(
115
+ addresses.c.user_id==
116
+ select([users.c.id]).
117
+ where(users.c.name=='jack')
118
+ )
@@ -0,0 +1,10 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+
3
+ require 'rubygems'
4
+ require 'activesupport'
5
+ require 'activerecord'
6
+ require 'active_record/connection_adapters/abstract/quoting'
7
+
8
+ require 'arel/algebra'
9
+ require 'arel/engines'
10
+ require 'arel/session'
@@ -0,0 +1,4 @@
1
+ require 'arel/algebra/extensions'
2
+ require 'arel/algebra/predicates'
3
+ require 'arel/algebra/relations'
4
+ require 'arel/algebra/primitives'
@@ -0,0 +1,4 @@
1
+ require 'arel/algebra/extensions/object'
2
+ require 'arel/algebra/extensions/class'
3
+ require 'arel/algebra/extensions/symbol'
4
+ require 'arel/algebra/extensions/hash'
@@ -0,0 +1,32 @@
1
+ module Arel
2
+ module ClassExtensions
3
+ def attributes(*attrs)
4
+ @attributes = attrs
5
+ attr_reader *attrs
6
+ end
7
+
8
+ def deriving(*methods)
9
+ methods.each { |m| derive m }
10
+ end
11
+
12
+ def derive(method_name)
13
+ methods = {
14
+ :initialize => "
15
+ def #{method_name}(#{@attributes.join(',')})
16
+ #{@attributes.collect { |a| "@#{a} = #{a}" }.join("\n")}
17
+ end
18
+ ",
19
+ :== => "
20
+ def ==(other)
21
+ #{name} === other &&
22
+ #{@attributes.collect { |a| "@#{a} == other.#{a}" }.join(" &&\n")}
23
+ end
24
+ "
25
+ }
26
+ class_eval methods[method_name], __FILE__, __LINE__
27
+ end
28
+
29
+ Class.send(:include, self)
30
+ end
31
+ end
32
+
@@ -0,0 +1,11 @@
1
+ module Arel
2
+ module HashExtensions
3
+ def bind(relation)
4
+ inject({}) do |bound, (key, value)|
5
+ bound.merge(key.bind(relation) => value.bind(relation))
6
+ end
7
+ end
8
+
9
+ Hash.send(:include, self)
10
+ end
11
+ end
@@ -0,0 +1,17 @@
1
+ module Arel
2
+ module ObjectExtensions
3
+ def bind(relation)
4
+ Arel::Value.new(self, relation)
5
+ end
6
+
7
+ def find_correlate_in(relation)
8
+ bind(relation)
9
+ end
10
+
11
+ def let
12
+ yield(self)
13
+ end
14
+
15
+ Object.send(:include, self)
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module Arel
2
+ module SymbolExtensions
3
+ def to_attribute(relation)
4
+ Arel::Attribute.new(relation, self)
5
+ end
6
+
7
+ Symbol.send(:include, self)
8
+ end
9
+ end
@@ -0,0 +1,41 @@
1
+ module Arel
2
+ class Predicate
3
+ def or(other_predicate)
4
+ Or.new(self, other_predicate)
5
+ end
6
+
7
+ def and(other_predicate)
8
+ And.new(self, other_predicate)
9
+ end
10
+ end
11
+
12
+ class Binary < Predicate
13
+ attributes :operand1, :operand2
14
+ deriving :initialize
15
+
16
+ def ==(other)
17
+ self.class === other and
18
+ @operand1 == other.operand1 and
19
+ @operand2 == other.operand2
20
+ end
21
+
22
+ def bind(relation)
23
+ self.class.new(operand1.find_correlate_in(relation), operand2.find_correlate_in(relation))
24
+ end
25
+ end
26
+
27
+ class Equality < Binary
28
+ def ==(other)
29
+ Equality === other and
30
+ ((operand1 == other.operand1 and operand2 == other.operand2) or
31
+ (operand1 == other.operand2 and operand2 == other.operand1))
32
+ end
33
+ end
34
+
35
+ class GreaterThanOrEqualTo < Binary; end
36
+ class GreaterThan < Binary; end
37
+ class LessThanOrEqualTo < Binary; end
38
+ class LessThan < Binary; end
39
+ class Match < Binary; end
40
+ class In < Binary; end
41
+ end
@@ -0,0 +1,5 @@
1
+ require 'arel/algebra/primitives/attribute'
2
+ require 'arel/algebra/primitives/ordering'
3
+ require 'arel/algebra/primitives/value'
4
+ require 'arel/algebra/primitives/expression'
5
+
@@ -0,0 +1,150 @@
1
+ require 'set'
2
+
3
+ module Arel
4
+ class Attribute
5
+ attributes :relation, :name, :alias, :ancestor
6
+ deriving :==
7
+ delegate :engine, :christener, :to => :relation
8
+
9
+ def initialize(relation, name, options = {})
10
+ @relation, @name, @alias, @ancestor = relation, name, options[:alias], options[:ancestor]
11
+ end
12
+
13
+ def named?(hypothetical_name)
14
+ (@alias || name).to_s == hypothetical_name.to_s
15
+ end
16
+
17
+ def aggregation?
18
+ false
19
+ end
20
+
21
+ def inspect
22
+ "<Attribute #{name}>"
23
+ end
24
+
25
+ module Transformations
26
+ def self.included(klass)
27
+ klass.send :alias_method, :eql?, :==
28
+ end
29
+
30
+ def hash
31
+ @hash ||= history.size + name.hash + relation.hash
32
+ end
33
+
34
+ def as(aliaz = nil)
35
+ Attribute.new(relation, name, :alias => aliaz, :ancestor => self)
36
+ end
37
+
38
+ def bind(new_relation)
39
+ relation == new_relation ? self : Attribute.new(new_relation, name, :alias => @alias, :ancestor => self)
40
+ end
41
+
42
+ def to_attribute(relation)
43
+ bind(relation)
44
+ end
45
+ end
46
+ include Transformations
47
+
48
+ module Congruence
49
+ def history
50
+ @history ||= [self] + (ancestor ? ancestor.history : [])
51
+ end
52
+
53
+ def join?
54
+ relation.join?
55
+ end
56
+
57
+ def root
58
+ history.last
59
+ end
60
+
61
+ def original_relation
62
+ @original_relation ||= original_attribute.relation
63
+ end
64
+
65
+ def original_attribute
66
+ @original_attribute ||= history.detect { |a| !a.join? }
67
+ end
68
+
69
+ def find_correlate_in(relation)
70
+ relation[self] || self
71
+ end
72
+
73
+ def descends_from?(other)
74
+ history.include?(other)
75
+ end
76
+
77
+ def /(other)
78
+ other ? (history & other.history).size : 0
79
+ end
80
+ end
81
+ include Congruence
82
+
83
+ module Predications
84
+ def eq(other)
85
+ Equality.new(self, other)
86
+ end
87
+
88
+ def lt(other)
89
+ LessThan.new(self, other)
90
+ end
91
+
92
+ def lteq(other)
93
+ LessThanOrEqualTo.new(self, other)
94
+ end
95
+
96
+ def gt(other)
97
+ GreaterThan.new(self, other)
98
+ end
99
+
100
+ def gteq(other)
101
+ GreaterThanOrEqualTo.new(self, other)
102
+ end
103
+
104
+ def matches(regexp)
105
+ Match.new(self, regexp)
106
+ end
107
+
108
+ def in(array)
109
+ In.new(self, array)
110
+ end
111
+ end
112
+ include Predications
113
+
114
+ module Expressions
115
+ def count(distinct = false)
116
+ distinct ? Distinct.new(self).count : Count.new(self)
117
+ end
118
+
119
+ def sum
120
+ Sum.new(self)
121
+ end
122
+
123
+ def maximum
124
+ Maximum.new(self)
125
+ end
126
+
127
+ def minimum
128
+ Minimum.new(self)
129
+ end
130
+
131
+ def average
132
+ Average.new(self)
133
+ end
134
+ end
135
+ include Expressions
136
+
137
+ module Orderings
138
+ def asc
139
+ Ascending.new(self)
140
+ end
141
+
142
+ def desc
143
+ Descending.new(self)
144
+ end
145
+
146
+ alias_method :to_ordering, :asc
147
+ end
148
+ include Orderings
149
+ end
150
+ end
@@ -0,0 +1,43 @@
1
+ module Arel
2
+ class Expression < Attribute
3
+ attributes :attribute, :alias, :ancestor
4
+ deriving :==
5
+ delegate :relation, :to => :attribute
6
+ alias_method :name, :alias
7
+
8
+ def initialize(attribute, aliaz = nil, ancestor = nil)
9
+ @attribute, @alias, @ancestor = attribute, aliaz, ancestor
10
+ end
11
+
12
+ def aggregation?
13
+ true
14
+ end
15
+
16
+ def inspect
17
+ "<#{self.class.name} #{attribute.inspect}>"
18
+ end
19
+
20
+ module Transformations
21
+ def as(aliaz)
22
+ self.class.new(attribute, aliaz, self)
23
+ end
24
+
25
+ def bind(new_relation)
26
+ new_relation == relation ? self : self.class.new(attribute.bind(new_relation), @alias, self)
27
+ end
28
+
29
+ def to_attribute(relation)
30
+ Attribute.new(relation, @alias, :ancestor => self)
31
+ end
32
+ end
33
+ include Transformations
34
+ end
35
+
36
+ class Count < Expression; end
37
+ class Distinct < Expression; end
38
+ class Sum < Expression; end
39
+ class Maximum < Expression; end
40
+ class Minimum < Expression; end
41
+ class Average < Expression; end
42
+ end
43
+