arel 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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,36 @@
1
+ module Arel
2
+ class Deletion < Compound
3
+ attributes :relation
4
+ deriving :initialize, :==
5
+
6
+ def call
7
+ engine.delete(self)
8
+ end
9
+ end
10
+
11
+ class Insert < Compound
12
+ attributes :relation, :record
13
+ deriving :==
14
+
15
+ def initialize(relation, record)
16
+ @relation, @record = relation, record.bind(relation)
17
+ end
18
+
19
+ def call
20
+ engine.create(self)
21
+ end
22
+ end
23
+
24
+ class Update < Compound
25
+ attributes :relation, :assignments
26
+ deriving :==
27
+
28
+ def initialize(relation, assignments)
29
+ @relation, @assignments = relation, assignments.bind(relation)
30
+ end
31
+
32
+ def call
33
+ engine.update(self)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,2 @@
1
+ require 'arel/engines/sql'
2
+ require 'arel/engines/memory'
@@ -0,0 +1,4 @@
1
+ require 'arel/engines/memory/relations'
2
+ require 'arel/engines/memory/primitives'
3
+ require 'arel/engines/memory/engine'
4
+ require 'arel/engines/memory/predicates'
@@ -0,0 +1,16 @@
1
+ module Arel
2
+ module Memory
3
+ class Engine
4
+ module CRUD
5
+ def read(relation)
6
+ relation.eval
7
+ end
8
+
9
+ def create(relation)
10
+ relation.eval
11
+ end
12
+ end
13
+ include CRUD
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,35 @@
1
+ module Arel
2
+ class Binary < Predicate
3
+ def eval(row)
4
+ operand1.eval(row).send(operator, operand2.eval(row))
5
+ end
6
+ end
7
+
8
+ class Equality < Binary
9
+ def operator; :== end
10
+ end
11
+
12
+ class GreaterThanOrEqualTo < Binary
13
+ def operator; :>= end
14
+ end
15
+
16
+ class GreaterThan < Binary
17
+ def operator; :> end
18
+ end
19
+
20
+ class LessThanOrEqualTo < Binary
21
+ def operator; :<= end
22
+ end
23
+
24
+ class LessThan < Binary
25
+ def operator; :< end
26
+ end
27
+
28
+ class Match < Binary
29
+ def operator; :=~ end
30
+ end
31
+
32
+ class In < Binary
33
+ def operator; :include? end
34
+ end
35
+ end
@@ -0,0 +1,27 @@
1
+ module Arel
2
+ class Attribute
3
+ def eval(row)
4
+ row[self]
5
+ end
6
+ end
7
+
8
+ class Value
9
+ def eval(row)
10
+ value
11
+ end
12
+ end
13
+
14
+ class Ordering
15
+ def eval(row1, row2)
16
+ (attribute.eval(row1) <=> attribute.eval(row2)) * direction
17
+ end
18
+ end
19
+
20
+ class Descending < Ordering
21
+ def direction; -1 end
22
+ end
23
+
24
+ class Ascending < Ordering
25
+ def direction; 1 end
26
+ end
27
+ end
@@ -0,0 +1,5 @@
1
+ require 'arel/engines/memory/relations/array'
2
+ require 'arel/engines/memory/relations/operations'
3
+ require 'arel/engines/memory/relations/writes'
4
+ require 'arel/engines/memory/relations/compound'
5
+
@@ -0,0 +1,25 @@
1
+ module Arel
2
+ class Array < Relation
3
+ attributes :array, :attribute_names
4
+ include Recursion::BaseCase
5
+ deriving :==, :initialize
6
+
7
+ def engine
8
+ @engine ||= Memory::Engine.new
9
+ end
10
+
11
+ def attributes
12
+ @attributes ||= @attribute_names.collect do |name|
13
+ name.to_attribute(self)
14
+ end
15
+ end
16
+
17
+ def format(attribute, value)
18
+ value
19
+ end
20
+
21
+ def eval
22
+ @array.collect { |r| Row.new(self, r) }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,9 @@
1
+ module Arel
2
+ class Compound < Relation
3
+ delegate :array, :to => :relation
4
+
5
+ def unoperated_rows
6
+ relation.call.collect { |row| row.bind(self) }
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,61 @@
1
+ module Arel
2
+ class Where < Compound
3
+ def eval
4
+ unoperated_rows.select { |row| predicate.eval(row) }
5
+ end
6
+ end
7
+
8
+ class Order < Compound
9
+ def eval
10
+ unoperated_rows.sort do |row1, row2|
11
+ ordering = orderings.detect { |o| o.eval(row1, row2) != 0 } || orderings.last
12
+ ordering.eval(row1, row2)
13
+ end
14
+ end
15
+ end
16
+
17
+ class Project < Compound
18
+ def eval
19
+ unoperated_rows.collect { |r| r.slice(*projections) }
20
+ end
21
+ end
22
+
23
+ class Take < Compound
24
+ def eval
25
+ unoperated_rows[0, taken]
26
+ end
27
+ end
28
+
29
+ class Skip < Compound
30
+ def eval
31
+ unoperated_rows[skipped..-1]
32
+ end
33
+ end
34
+
35
+ class Group < Compound
36
+ def eval
37
+ raise NotImplementedError
38
+ end
39
+ end
40
+
41
+ class Alias < Compound
42
+ def eval
43
+ unoperated_rows
44
+ end
45
+ end
46
+
47
+ class Join < Relation
48
+ def eval
49
+ result = []
50
+ relation1.call.each do |row1|
51
+ relation2.call.each do |row2|
52
+ combined_row = row1.combine(row2, self)
53
+ if predicates.all? { |p| p.eval(combined_row) }
54
+ result << combined_row
55
+ end
56
+ end
57
+ end
58
+ result
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,7 @@
1
+ module Arel
2
+ class Insert < Compound
3
+ def eval
4
+ unoperated_rows + [Row.new(self, record.values.collect(&:value))]
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'arel/engines/sql/engine'
2
+ require 'arel/engines/sql/relations'
3
+ require 'arel/engines/sql/primitives'
4
+ require 'arel/engines/sql/predicates'
5
+ require 'arel/engines/sql/formatters'
6
+ require 'arel/engines/sql/extensions'
7
+ require 'arel/engines/sql/christener'
@@ -0,0 +1,13 @@
1
+ module Arel
2
+ module Sql
3
+ class Christener
4
+ def name_for(relation)
5
+ @used_names ||= Hash.new(0)
6
+ (@relation_names ||= Hash.new do |hash, relation|
7
+ @used_names[name = relation.name] += 1
8
+ hash[relation] = name + (@used_names[name] > 1 ? "_#{@used_names[name]}" : '')
9
+ end)[relation.table]
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,37 @@
1
+ module Arel
2
+ module Sql
3
+ class Engine
4
+ def initialize(ar = nil)
5
+ @ar = ar
6
+ end
7
+
8
+ def connection
9
+ @ar.connection
10
+ end
11
+
12
+ def method_missing(method, *args, &block)
13
+ @ar.connection.send(method, *args, &block)
14
+ end
15
+
16
+ module CRUD
17
+ def create(relation)
18
+ connection.insert(relation.to_sql)
19
+ end
20
+
21
+ def read(relation)
22
+ rows = connection.select_rows(relation.to_sql)
23
+ Array.new(rows, relation.attributes)
24
+ end
25
+
26
+ def update(relation)
27
+ connection.update(relation.to_sql)
28
+ end
29
+
30
+ def delete(relation)
31
+ connection.delete(relation.to_sql)
32
+ end
33
+ end
34
+ include CRUD
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,4 @@
1
+ require 'arel/engines/sql/extensions/object'
2
+ require 'arel/engines/sql/extensions/array'
3
+ require 'arel/engines/sql/extensions/range'
4
+ require 'arel/engines/sql/extensions/nil_class'
@@ -0,0 +1,16 @@
1
+ module Arel
2
+ module Sql
3
+ module ArrayExtensions
4
+ def to_sql(formatter = nil)
5
+ "(" + collect { |e| e.to_sql(formatter) }.join(', ') + ")"
6
+ end
7
+
8
+ def inclusion_predicate_sql
9
+ "IN"
10
+ end
11
+
12
+ Array.send(:include, self)
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,11 @@
1
+ module Arel
2
+ module Sql
3
+ module NilClassExtensions
4
+ def equality_predicate_sql
5
+ 'IS'
6
+ end
7
+
8
+ NilClass.send(:include, self)
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ module Arel
2
+ module Sql
3
+ module ObjectExtensions
4
+ def to_sql(formatter)
5
+ formatter.scalar self
6
+ end
7
+
8
+ def equality_predicate_sql
9
+ '='
10
+ end
11
+
12
+ Object.send(:include, self)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module Arel
2
+ module Sql
3
+ module RangeExtensions
4
+ def to_sql(formatter = nil)
5
+ formatter.range self.begin, self.end
6
+ end
7
+
8
+ def inclusion_predicate_sql
9
+ "BETWEEN"
10
+ end
11
+
12
+ Range.send(:include, self)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,113 @@
1
+ module Arel
2
+ module Sql
3
+ class Formatter
4
+ attr_reader :environment
5
+ delegate :christener, :engine, :to => :environment
6
+ delegate :name_for, :to => :christener
7
+ delegate :quote_table_name, :quote_column_name, :quote, :to => :engine
8
+
9
+ def initialize(environment)
10
+ @environment = environment
11
+ end
12
+ end
13
+
14
+ class SelectClause < Formatter
15
+ def attribute(attribute)
16
+ # FIXME this should check that the column exists
17
+ "#{quote_table_name(name_for(attribute.original_relation))}.#{quote_column_name(attribute.name)}" +
18
+ (attribute.alias ? " AS #{quote(attribute.alias.to_s)}" : "")
19
+ end
20
+
21
+ def expression(expression)
22
+ if expression.function_sql == "DISTINCT"
23
+ "#{expression.function_sql} #{expression.attribute.to_sql(self)}" +
24
+ (expression.alias ? " AS #{quote_column_name(expression.alias)}" : '')
25
+ else
26
+ "#{expression.function_sql}(#{expression.attribute.to_sql(self)})" +
27
+ (expression.alias ? " AS #{quote_column_name(expression.alias)}" : " AS #{expression.function_sql.to_s.downcase}_id")
28
+ end
29
+ end
30
+
31
+ def select(select_sql, table)
32
+ "(#{select_sql}) AS #{quote_table_name(name_for(table))}"
33
+ end
34
+
35
+ def value(value)
36
+ value
37
+ end
38
+ end
39
+
40
+ class PassThrough < Formatter
41
+ def value(value)
42
+ value
43
+ end
44
+ end
45
+
46
+ class WhereClause < PassThrough
47
+ end
48
+
49
+ class OrderClause < PassThrough
50
+ def ordering(ordering)
51
+ "#{quote_table_name(name_for(ordering.attribute.original_relation))}.#{quote_column_name(ordering.attribute.name)} #{ordering.direction_sql}"
52
+ end
53
+ end
54
+
55
+ class GroupClause < PassThrough
56
+ def attribute(attribute)
57
+ "#{quote_table_name(name_for(attribute.original_relation))}.#{quote_column_name(attribute.name)}"
58
+ end
59
+ end
60
+
61
+ class WhereCondition < Formatter
62
+ def attribute(attribute)
63
+ "#{quote_table_name(name_for(attribute.original_relation))}.#{quote_column_name(attribute.name)}"
64
+ end
65
+
66
+ def expression(expression)
67
+ "#{expression.function_sql}(#{expression.attribute.to_sql(self)})"
68
+ end
69
+
70
+ def value(value)
71
+ value.to_sql(self)
72
+ end
73
+
74
+ def scalar(value, column = nil)
75
+ quote(value, column)
76
+ end
77
+
78
+ def select(select_sql, table)
79
+ "(#{select_sql})"
80
+ end
81
+ end
82
+
83
+ class SelectStatement < Formatter
84
+ def select(select_sql, table)
85
+ select_sql
86
+ end
87
+ end
88
+
89
+ class TableReference < Formatter
90
+ def select(select_sql, table)
91
+ "(#{select_sql}) AS #{quote_table_name(name_for(table))}"
92
+ end
93
+
94
+ def table(table)
95
+ quote_table_name(table.name) +
96
+ (table.name != name_for(table) ? " AS " + quote_table_name(name_for(table)) : '')
97
+ end
98
+ end
99
+
100
+ class Attribute < WhereCondition
101
+ def scalar(scalar)
102
+ quote(scalar, environment.column)
103
+ end
104
+
105
+ def range(left, right)
106
+ "#{left} AND #{right}"
107
+ end
108
+ end
109
+
110
+ class Value < WhereCondition
111
+ end
112
+ end
113
+ end