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,23 @@
1
+ module Arel
2
+ class Ordering
3
+ delegate :relation, :to => :attribute
4
+
5
+ def bind(relation)
6
+ self.class.new(attribute.bind(relation))
7
+ end
8
+
9
+ def to_ordering
10
+ self
11
+ end
12
+ end
13
+
14
+ class Ascending < Ordering
15
+ attributes :attribute
16
+ deriving :initialize, :==
17
+ end
18
+
19
+ class Descending < Ordering
20
+ attributes :attribute
21
+ deriving :initialize, :==
22
+ end
23
+ end
@@ -0,0 +1,14 @@
1
+ module Arel
2
+ class Value
3
+ attributes :value, :relation
4
+ deriving :initialize, :==
5
+
6
+ def bind(relation)
7
+ Value.new(value, relation)
8
+ end
9
+
10
+ def to_ordering
11
+ self
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,14 @@
1
+ require 'arel/algebra/relations/relation'
2
+ require 'arel/algebra/relations/utilities/compound'
3
+ require 'arel/algebra/relations/utilities/nil'
4
+ require 'arel/algebra/relations/utilities/externalization'
5
+ require 'arel/algebra/relations/row'
6
+ require 'arel/algebra/relations/writes'
7
+ require 'arel/algebra/relations/operations/alias'
8
+ require 'arel/algebra/relations/operations/group'
9
+ require 'arel/algebra/relations/operations/join'
10
+ require 'arel/algebra/relations/operations/order'
11
+ require 'arel/algebra/relations/operations/project'
12
+ require 'arel/algebra/relations/operations/where'
13
+ require 'arel/algebra/relations/operations/skip'
14
+ require 'arel/algebra/relations/operations/take'
@@ -0,0 +1,7 @@
1
+ module Arel
2
+ class Alias < Compound
3
+ attributes :relation
4
+ deriving :initialize
5
+ alias_method :==, :equal?
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ module Arel
2
+ class Group < Compound
3
+ attributes :relation, :groupings
4
+ deriving :==
5
+
6
+ def initialize(relation, *groupings, &block)
7
+ @relation = relation
8
+ @groupings = (groupings + arguments_from_block(relation, &block)) \
9
+ .collect { |g| g.bind(relation) }
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,64 @@
1
+ module Arel
2
+ class Join < Relation
3
+ attributes :relation1, :relation2, :predicates
4
+ deriving :==
5
+ delegate :name, :to => :relation1
6
+
7
+ def initialize(relation1, relation2 = Nil.instance, *predicates)
8
+ @relation1, @relation2, @predicates = relation1, relation2, predicates
9
+ end
10
+
11
+ def hash
12
+ @hash ||= :relation1.hash
13
+ end
14
+
15
+ def eql?(other)
16
+ self == other
17
+ end
18
+
19
+ def attributes
20
+ @attributes ||= (relation1.externalize.attributes +
21
+ relation2.externalize.attributes).collect { |a| a.bind(self) }
22
+ end
23
+
24
+ def wheres
25
+ # TESTME bind to self?
26
+ relation1.externalize.wheres
27
+ end
28
+
29
+ def ons
30
+ @ons ||= @predicates.collect { |p| p.bind(self) }
31
+ end
32
+
33
+ # TESTME
34
+ def externalizable?
35
+ relation1.externalizable? or relation2.externalizable?
36
+ end
37
+
38
+ def join?
39
+ true
40
+ end
41
+
42
+ def engine
43
+ relation1.engine != relation2.engine ? Memory::Engine.new : relation1.engine
44
+ end
45
+ end
46
+
47
+ class InnerJoin < Join; end
48
+ class OuterJoin < Join; end
49
+ class StringJoin < Join
50
+ def attributes
51
+ relation1.externalize.attributes
52
+ end
53
+
54
+ def engine
55
+ relation1.engine
56
+ end
57
+ end
58
+
59
+ class Relation
60
+ def join?
61
+ false
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,18 @@
1
+ module Arel
2
+ class Order < Compound
3
+ attributes :relation, :orderings
4
+ deriving :==
5
+
6
+ def initialize(relation, *orderings, &block)
7
+ @relation = relation
8
+ @orderings = (orderings + arguments_from_block(relation, &block)) \
9
+ .collect { |o| o.bind(relation) }
10
+ end
11
+
12
+ # TESTME
13
+ def orders
14
+ # QUESTION - do we still need relation.orders ?
15
+ (orderings + relation.orders).collect { |o| o.bind(self) }.collect { |o| o.to_ordering }
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,20 @@
1
+ module Arel
2
+ class Project < Compound
3
+ attributes :relation, :projections
4
+ deriving :==
5
+
6
+ def initialize(relation, *projections, &block)
7
+ @relation = relation
8
+ @projections = (projections + arguments_from_block(relation, &block)) \
9
+ .collect { |p| p.bind(relation) }
10
+ end
11
+
12
+ def attributes
13
+ @attributes ||= projections.collect { |p| p.bind(self) }
14
+ end
15
+
16
+ def externalizable?
17
+ attributes.any?(&:aggregation?) or relation.externalizable?
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,6 @@
1
+ module Arel
2
+ class Skip < Compound
3
+ attributes :relation, :skipped
4
+ deriving :initialize, :==
5
+ end
6
+ end
@@ -0,0 +1,10 @@
1
+ module Arel
2
+ class Take < Compound
3
+ attributes :relation, :taken
4
+ deriving :initialize, :==
5
+
6
+ def externalizable?
7
+ true
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,16 @@
1
+ module Arel
2
+ class Where < Compound
3
+ attributes :relation, :predicate
4
+ deriving :==
5
+
6
+ def initialize(relation, *predicates, &block)
7
+ predicate = block_given?? yield(relation) : predicates.shift
8
+ @relation = predicates.empty?? relation : Where.new(relation, *predicates)
9
+ @predicate = predicate.bind(@relation)
10
+ end
11
+
12
+ def wheres
13
+ @wheres ||= (relation.wheres + [predicate]).collect { |p| p.bind(self) }
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,136 @@
1
+ module Arel
2
+ class Relation
3
+ attr_reader :count
4
+
5
+ def session
6
+ Session.new
7
+ end
8
+
9
+ def call
10
+ engine.read(self)
11
+ end
12
+
13
+ def bind(relation)
14
+ self
15
+ end
16
+
17
+ module Enumerable
18
+ include ::Enumerable
19
+
20
+ def each(&block)
21
+ session.read(self).each(&block)
22
+ end
23
+
24
+ def first
25
+ session.read(self).first
26
+ end
27
+ end
28
+ include Enumerable
29
+
30
+ module Operable
31
+ def join(other_relation = nil, join_class = InnerJoin)
32
+ case other_relation
33
+ when String
34
+ StringJoin.new(self, other_relation)
35
+ when Relation
36
+ JoinOperation.new(join_class, self, other_relation)
37
+ else
38
+ self
39
+ end
40
+ end
41
+
42
+ def outer_join(other_relation = nil)
43
+ join(other_relation, OuterJoin)
44
+ end
45
+
46
+ [:where, :project, :order, :take, :skip, :group].each do |operation_name|
47
+ class_eval <<-OPERATION, __FILE__, __LINE__
48
+ def #{operation_name}(*arguments, &block)
49
+ arguments.all?(&:blank?) && !block_given?? self : #{operation_name.to_s.classify}.new(self, *arguments, &block)
50
+ end
51
+ OPERATION
52
+ end
53
+
54
+ def alias
55
+ Alias.new(self)
56
+ end
57
+
58
+ module Writable
59
+ def insert(record)
60
+ session.create Insert.new(self, record)
61
+ end
62
+
63
+ def update(assignments)
64
+ session.update Update.new(self, assignments)
65
+ end
66
+
67
+ def delete
68
+ session.delete Deletion.new(self)
69
+ end
70
+ end
71
+ include Writable
72
+
73
+ JoinOperation = Struct.new(:join_class, :relation1, :relation2) do
74
+ def on(*predicates)
75
+ join_class.new(relation1, relation2, *predicates)
76
+ end
77
+ end
78
+ end
79
+ include Operable
80
+
81
+ module AttributeAccessable
82
+ def [](index)
83
+ case index
84
+ when Symbol, String
85
+ find_attribute_matching_name(index)
86
+ when Attribute, Expression
87
+ find_attribute_matching_attribute(index)
88
+ when ::Array
89
+ # TESTME
90
+ index.collect { |i| self[i] }
91
+ end
92
+ end
93
+
94
+ def find_attribute_matching_name(name)
95
+ attributes.detect { |a| a.named?(name) }
96
+ end
97
+
98
+ def find_attribute_matching_attribute(attribute)
99
+ matching_attributes(attribute).max do |a1, a2|
100
+ (a1.original_attribute / attribute) <=> (a2.original_attribute / attribute)
101
+ end
102
+ end
103
+
104
+ def position_of(attribute)
105
+ (@position_of ||= Hash.new do |h, attribute|
106
+ h[attribute] = attributes.index(self[attribute])
107
+ end)[attribute]
108
+ end
109
+
110
+ private
111
+ def matching_attributes(attribute)
112
+ (@matching_attributes ||= attributes.inject({}) do |hash, a|
113
+ (hash[a.root] ||= []) << a
114
+ hash
115
+ end)[attribute.root] || []
116
+ end
117
+
118
+ def has_attribute?(attribute)
119
+ !matching_attributes(attribute).empty?
120
+ end
121
+ end
122
+ include AttributeAccessable
123
+
124
+ module DefaultOperations
125
+ def attributes; [] end
126
+ def wheres; [] end
127
+ def orders; [] end
128
+ def inserts; [] end
129
+ def groupings; [] end
130
+ def joins(formatter = nil); nil end # FIXME
131
+ def taken; nil end
132
+ def skipped; nil end
133
+ end
134
+ include DefaultOperations
135
+ end
136
+ end
@@ -0,0 +1,26 @@
1
+ module Arel
2
+ class Row
3
+ attributes :relation, :tuple
4
+ deriving :==, :initialize
5
+
6
+ def [](attribute)
7
+ attribute.type_cast(tuple[relation.position_of(attribute)])
8
+ end
9
+
10
+ def slice(*attributes)
11
+ Row.new(relation, attributes.inject([]) do |cheese, attribute|
12
+ # FIXME TESTME method chaining
13
+ cheese << tuple[relation.relation.position_of(attribute)]
14
+ cheese
15
+ end)
16
+ end
17
+
18
+ def bind(relation)
19
+ Row.new(relation, tuple)
20
+ end
21
+
22
+ def combine(other, relation)
23
+ Row.new(relation, tuple + other.tuple)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,30 @@
1
+ module Arel
2
+ class Compound < Relation
3
+ attr_reader :relation
4
+ delegate :joins, :join?, :inserts, :taken, :skipped, :name, :externalizable?,
5
+ :column_for, :engine,
6
+ :to => :relation
7
+
8
+ [:attributes, :wheres, :groupings, :orders].each do |operation_name|
9
+ class_eval <<-OPERATION, __FILE__, __LINE__
10
+ def #{operation_name}
11
+ @#{operation_name} ||= relation.#{operation_name}.collect { |o| o.bind(self) }
12
+ end
13
+ OPERATION
14
+ end
15
+
16
+ def hash
17
+ @hash ||= :relation.hash
18
+ end
19
+
20
+ def eql?(other)
21
+ self == other
22
+ end
23
+
24
+ private
25
+
26
+ def arguments_from_block(relation, &block)
27
+ block_given?? [yield(relation)] : []
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ module Arel
2
+ class Externalization < Compound
3
+ attributes :relation
4
+ deriving :initialize, :==
5
+
6
+ def wheres
7
+ []
8
+ end
9
+
10
+ def attributes
11
+ @attributes ||= relation.attributes.collect { |a| a.to_attribute(self) }
12
+ end
13
+ end
14
+
15
+ class Relation
16
+ def externalize
17
+ @externalized ||= externalizable?? Externalization.new(self) : self
18
+ end
19
+
20
+ def externalizable?
21
+ false
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,7 @@
1
+ require 'singleton'
2
+
3
+ module Arel
4
+ class Nil < Relation
5
+ include Singleton
6
+ end
7
+ end