arel 1.0.1 → 2.0.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 (247) hide show
  1. data/MIT-LICENSE.txt +20 -0
  2. data/Manifest.txt +105 -0
  3. data/README.markdown +12 -32
  4. data/Rakefile +17 -0
  5. data/arel.gemspec +39 -0
  6. data/lib/arel.rb +30 -9
  7. data/lib/arel/attributes.rb +20 -0
  8. data/lib/arel/attributes/attribute.rb +190 -0
  9. data/lib/arel/compatibility/wheres.rb +33 -0
  10. data/lib/arel/crud.rb +37 -0
  11. data/lib/arel/delete_manager.rb +22 -0
  12. data/lib/arel/deprecated.rb +4 -0
  13. data/lib/arel/expression.rb +4 -0
  14. data/lib/arel/expressions.rb +23 -0
  15. data/lib/arel/insert_manager.rb +34 -0
  16. data/lib/arel/nodes.rb +44 -0
  17. data/lib/arel/nodes/and.rb +6 -0
  18. data/lib/arel/nodes/assignment.rb +6 -0
  19. data/lib/arel/nodes/avg.rb +6 -0
  20. data/lib/arel/nodes/between.rb +6 -0
  21. data/lib/arel/nodes/binary.rb +12 -0
  22. data/lib/arel/nodes/count.rb +13 -0
  23. data/lib/arel/nodes/delete_statement.rb +17 -0
  24. data/lib/arel/nodes/does_not_match.rb +6 -0
  25. data/lib/arel/nodes/equality.rb +9 -0
  26. data/lib/arel/nodes/exists.rb +11 -0
  27. data/lib/arel/nodes/function.rb +18 -0
  28. data/lib/arel/nodes/greater_than.rb +6 -0
  29. data/lib/arel/nodes/greater_than_or_equal.rb +6 -0
  30. data/lib/arel/nodes/group.rb +11 -0
  31. data/lib/arel/nodes/grouping.rb +11 -0
  32. data/lib/arel/nodes/having.rb +11 -0
  33. data/lib/arel/nodes/in.rb +6 -0
  34. data/lib/arel/nodes/inner_join.rb +6 -0
  35. data/lib/arel/nodes/insert_statement.rb +19 -0
  36. data/lib/arel/nodes/join.rb +13 -0
  37. data/lib/arel/nodes/less_than.rb +6 -0
  38. data/lib/arel/nodes/less_than_or_equal.rb +6 -0
  39. data/lib/arel/nodes/lock.rb +6 -0
  40. data/lib/arel/nodes/matches.rb +6 -0
  41. data/lib/arel/nodes/max.rb +6 -0
  42. data/lib/arel/nodes/min.rb +6 -0
  43. data/lib/arel/nodes/node.rb +30 -0
  44. data/lib/arel/nodes/not_equal.rb +6 -0
  45. data/lib/arel/nodes/not_in.rb +6 -0
  46. data/lib/arel/nodes/offset.rb +11 -0
  47. data/lib/arel/nodes/on.rb +11 -0
  48. data/lib/arel/nodes/or.rb +6 -0
  49. data/lib/arel/nodes/ordering.rb +19 -0
  50. data/lib/arel/nodes/outer_join.rb +6 -0
  51. data/lib/arel/nodes/select_core.rb +25 -0
  52. data/lib/arel/nodes/select_statement.rb +22 -0
  53. data/lib/arel/nodes/sql_literal.rb +7 -0
  54. data/lib/arel/nodes/string_join.rb +11 -0
  55. data/lib/arel/nodes/sum.rb +6 -0
  56. data/lib/arel/nodes/table_alias.rb +21 -0
  57. data/lib/arel/nodes/unqualified_column.rb +19 -0
  58. data/lib/arel/nodes/update_statement.rb +21 -0
  59. data/lib/arel/nodes/values.rb +12 -0
  60. data/lib/arel/relation.rb +6 -0
  61. data/lib/arel/select_manager.rb +203 -0
  62. data/lib/arel/sql/engine.rb +10 -0
  63. data/lib/arel/sql_literal.rb +1 -10
  64. data/lib/arel/table.rb +126 -0
  65. data/lib/arel/tree_manager.rb +26 -0
  66. data/lib/arel/update_manager.rb +48 -0
  67. data/lib/arel/visitors.rb +30 -0
  68. data/lib/arel/visitors/dot.rb +233 -0
  69. data/lib/arel/visitors/join_sql.rb +38 -0
  70. data/lib/arel/visitors/mysql.rb +16 -0
  71. data/lib/arel/visitors/oracle.rb +69 -0
  72. data/lib/arel/visitors/order_clauses.rb +9 -0
  73. data/lib/arel/visitors/postgresql.rb +54 -0
  74. data/lib/arel/visitors/to_sql.rb +301 -0
  75. data/lib/arel/visitors/where_sql.rb +9 -0
  76. data/spec/activerecord_compat_spec.rb +18 -0
  77. data/spec/attributes/attribute_spec.rb +648 -0
  78. data/spec/attributes_spec.rb +33 -6
  79. data/spec/crud_spec.rb +69 -0
  80. data/spec/delete_manager_spec.rb +53 -0
  81. data/spec/insert_manager_spec.rb +141 -0
  82. data/spec/nodes/count_spec.rb +18 -0
  83. data/spec/nodes/delete_statement_spec.rb +15 -0
  84. data/spec/nodes/equality_spec.rb +72 -0
  85. data/spec/nodes/insert_statement_spec.rb +18 -0
  86. data/spec/nodes/or_spec.rb +20 -0
  87. data/spec/nodes/select_core_spec.rb +21 -0
  88. data/spec/nodes/select_statement_spec.rb +14 -0
  89. data/spec/nodes/sql_literal_spec.rb +26 -0
  90. data/spec/nodes/sum_spec.rb +12 -0
  91. data/spec/nodes/update_statement_spec.rb +18 -0
  92. data/spec/select_manager_spec.rb +581 -0
  93. data/spec/spec.opts +3 -0
  94. data/spec/spec_helper.rb +6 -21
  95. data/spec/support/fake_record.rb +89 -0
  96. data/spec/support/shared/tree_manager_shared.rb +9 -0
  97. data/spec/table_spec.rb +176 -0
  98. data/spec/update_manager_spec.rb +89 -0
  99. data/spec/visitors/join_sql_spec.rb +35 -0
  100. data/spec/visitors/oracle_spec.rb +111 -0
  101. data/spec/visitors/to_sql_spec.rb +134 -0
  102. metadata +160 -260
  103. data/lib/arel/algebra.rb +0 -10
  104. data/lib/arel/algebra/attributes.rb +0 -7
  105. data/lib/arel/algebra/attributes/attribute.rb +0 -304
  106. data/lib/arel/algebra/attributes/boolean.rb +0 -21
  107. data/lib/arel/algebra/attributes/decimal.rb +0 -9
  108. data/lib/arel/algebra/attributes/float.rb +0 -9
  109. data/lib/arel/algebra/attributes/integer.rb +0 -10
  110. data/lib/arel/algebra/attributes/string.rb +0 -10
  111. data/lib/arel/algebra/attributes/time.rb +0 -6
  112. data/lib/arel/algebra/core_extensions.rb +0 -3
  113. data/lib/arel/algebra/core_extensions/hash.rb +0 -7
  114. data/lib/arel/algebra/core_extensions/object.rb +0 -13
  115. data/lib/arel/algebra/core_extensions/symbol.rb +0 -9
  116. data/lib/arel/algebra/expression.rb +0 -56
  117. data/lib/arel/algebra/header.rb +0 -66
  118. data/lib/arel/algebra/ordering.rb +0 -31
  119. data/lib/arel/algebra/predicates.rb +0 -306
  120. data/lib/arel/algebra/relations.rb +0 -16
  121. data/lib/arel/algebra/relations/operations/from.rb +0 -14
  122. data/lib/arel/algebra/relations/operations/group.rb +0 -14
  123. data/lib/arel/algebra/relations/operations/having.rb +0 -14
  124. data/lib/arel/algebra/relations/operations/join.rb +0 -103
  125. data/lib/arel/algebra/relations/operations/lock.rb +0 -10
  126. data/lib/arel/algebra/relations/operations/order.rb +0 -23
  127. data/lib/arel/algebra/relations/operations/project.rb +0 -20
  128. data/lib/arel/algebra/relations/operations/skip.rb +0 -14
  129. data/lib/arel/algebra/relations/operations/take.rb +0 -18
  130. data/lib/arel/algebra/relations/operations/where.rb +0 -24
  131. data/lib/arel/algebra/relations/relation.rb +0 -205
  132. data/lib/arel/algebra/relations/row.rb +0 -29
  133. data/lib/arel/algebra/relations/utilities/compound.rb +0 -55
  134. data/lib/arel/algebra/relations/utilities/externalization.rb +0 -26
  135. data/lib/arel/algebra/relations/utilities/nil.rb +0 -7
  136. data/lib/arel/algebra/relations/writes.rb +0 -47
  137. data/lib/arel/algebra/value.rb +0 -53
  138. data/lib/arel/engines.rb +0 -2
  139. data/lib/arel/engines/memory.rb +0 -2
  140. data/lib/arel/engines/memory/engine.rb +0 -10
  141. data/lib/arel/engines/memory/relations.rb +0 -2
  142. data/lib/arel/engines/memory/relations/array.rb +0 -37
  143. data/lib/arel/engines/memory/relations/operations.rb +0 -9
  144. data/lib/arel/engines/sql.rb +0 -6
  145. data/lib/arel/engines/sql/attributes.rb +0 -45
  146. data/lib/arel/engines/sql/christener.rb +0 -20
  147. data/lib/arel/engines/sql/compilers/ibm_db_compiler.rb +0 -48
  148. data/lib/arel/engines/sql/compilers/mysql_compiler.rb +0 -11
  149. data/lib/arel/engines/sql/compilers/oracle_compiler.rb +0 -106
  150. data/lib/arel/engines/sql/compilers/postgresql_compiler.rb +0 -50
  151. data/lib/arel/engines/sql/compilers/sqlite_compiler.rb +0 -9
  152. data/lib/arel/engines/sql/core_extensions.rb +0 -4
  153. data/lib/arel/engines/sql/core_extensions/array.rb +0 -24
  154. data/lib/arel/engines/sql/core_extensions/nil_class.rb +0 -15
  155. data/lib/arel/engines/sql/core_extensions/object.rb +0 -19
  156. data/lib/arel/engines/sql/core_extensions/range.rb +0 -19
  157. data/lib/arel/engines/sql/engine.rb +0 -47
  158. data/lib/arel/engines/sql/formatters.rb +0 -138
  159. data/lib/arel/engines/sql/relations.rb +0 -3
  160. data/lib/arel/engines/sql/relations/compiler.rb +0 -153
  161. data/lib/arel/engines/sql/relations/table.rb +0 -100
  162. data/lib/arel/engines/sql/relations/utilities/nil.rb +0 -6
  163. data/lib/arel/recursion/base_case.rb +0 -13
  164. data/lib/arel/session.rb +0 -35
  165. data/lib/arel/version.rb +0 -3
  166. data/spec/algebra/unit/predicates/binary_spec.rb +0 -35
  167. data/spec/algebra/unit/predicates/equality_spec.rb +0 -29
  168. data/spec/algebra/unit/predicates/in_spec.rb +0 -12
  169. data/spec/algebra/unit/predicates/inequality_spec.rb +0 -32
  170. data/spec/algebra/unit/predicates/predicate_spec.rb +0 -22
  171. data/spec/algebra/unit/primitives/attribute_spec.rb +0 -175
  172. data/spec/algebra/unit/primitives/expression_spec.rb +0 -39
  173. data/spec/algebra/unit/primitives/value_spec.rb +0 -15
  174. data/spec/algebra/unit/relations/alias_spec.rb +0 -16
  175. data/spec/algebra/unit/relations/delete_spec.rb +0 -9
  176. data/spec/algebra/unit/relations/group_spec.rb +0 -10
  177. data/spec/algebra/unit/relations/insert_spec.rb +0 -9
  178. data/spec/algebra/unit/relations/join_spec.rb +0 -18
  179. data/spec/algebra/unit/relations/order_spec.rb +0 -21
  180. data/spec/algebra/unit/relations/project_spec.rb +0 -34
  181. data/spec/algebra/unit/relations/relation_spec.rb +0 -241
  182. data/spec/algebra/unit/relations/skip_spec.rb +0 -10
  183. data/spec/algebra/unit/relations/table_spec.rb +0 -38
  184. data/spec/algebra/unit/relations/take_spec.rb +0 -10
  185. data/spec/algebra/unit/relations/update_spec.rb +0 -9
  186. data/spec/algebra/unit/relations/where_spec.rb +0 -19
  187. data/spec/algebra/unit/session/session_spec.rb +0 -84
  188. data/spec/attributes/boolean_spec.rb +0 -57
  189. data/spec/attributes/float_spec.rb +0 -119
  190. data/spec/attributes/header_spec.rb +0 -42
  191. data/spec/attributes/integer_spec.rb +0 -119
  192. data/spec/attributes/string_spec.rb +0 -43
  193. data/spec/attributes/time_spec.rb +0 -24
  194. data/spec/engines/memory/integration/joins/cross_engine_spec.rb +0 -61
  195. data/spec/engines/memory/unit/relations/array_spec.rb +0 -33
  196. data/spec/engines/memory/unit/relations/insert_spec.rb +0 -28
  197. data/spec/engines/memory/unit/relations/join_spec.rb +0 -32
  198. data/spec/engines/memory/unit/relations/order_spec.rb +0 -28
  199. data/spec/engines/memory/unit/relations/project_spec.rb +0 -27
  200. data/spec/engines/memory/unit/relations/skip_spec.rb +0 -31
  201. data/spec/engines/memory/unit/relations/take_spec.rb +0 -28
  202. data/spec/engines/memory/unit/relations/where_spec.rb +0 -43
  203. data/spec/engines/sql/integration/joins/with_adjacency_spec.rb +0 -258
  204. data/spec/engines/sql/integration/joins/with_aggregations_spec.rb +0 -221
  205. data/spec/engines/sql/integration/joins/with_compounds_spec.rb +0 -137
  206. data/spec/engines/sql/unit/engine_spec.rb +0 -65
  207. data/spec/engines/sql/unit/predicates/binary_spec.rb +0 -140
  208. data/spec/engines/sql/unit/predicates/equality_spec.rb +0 -75
  209. data/spec/engines/sql/unit/predicates/in_spec.rb +0 -179
  210. data/spec/engines/sql/unit/predicates/noteq_spec.rb +0 -75
  211. data/spec/engines/sql/unit/predicates/predicates_spec.rb +0 -79
  212. data/spec/engines/sql/unit/primitives/attribute_spec.rb +0 -36
  213. data/spec/engines/sql/unit/primitives/expression_spec.rb +0 -28
  214. data/spec/engines/sql/unit/primitives/literal_spec.rb +0 -43
  215. data/spec/engines/sql/unit/primitives/value_spec.rb +0 -29
  216. data/spec/engines/sql/unit/relations/alias_spec.rb +0 -53
  217. data/spec/engines/sql/unit/relations/delete_spec.rb +0 -83
  218. data/spec/engines/sql/unit/relations/from_spec.rb +0 -64
  219. data/spec/engines/sql/unit/relations/group_spec.rb +0 -72
  220. data/spec/engines/sql/unit/relations/having_spec.rb +0 -78
  221. data/spec/engines/sql/unit/relations/insert_spec.rb +0 -143
  222. data/spec/engines/sql/unit/relations/join_spec.rb +0 -180
  223. data/spec/engines/sql/unit/relations/lock_spec.rb +0 -86
  224. data/spec/engines/sql/unit/relations/order_spec.rb +0 -161
  225. data/spec/engines/sql/unit/relations/project_spec.rb +0 -143
  226. data/spec/engines/sql/unit/relations/skip_spec.rb +0 -41
  227. data/spec/engines/sql/unit/relations/table_spec.rb +0 -122
  228. data/spec/engines/sql/unit/relations/take_spec.rb +0 -75
  229. data/spec/engines/sql/unit/relations/update_spec.rb +0 -203
  230. data/spec/engines/sql/unit/relations/where_spec.rb +0 -72
  231. data/spec/relations/join_spec.rb +0 -42
  232. data/spec/relations/relation_spec.rb +0 -31
  233. data/spec/shared/relation_spec.rb +0 -255
  234. data/spec/sql/christener_spec.rb +0 -70
  235. data/spec/support/connections/mysql_connection.rb +0 -14
  236. data/spec/support/connections/oracle_connection.rb +0 -17
  237. data/spec/support/connections/postgresql_connection.rb +0 -13
  238. data/spec/support/connections/sqlite3_connection.rb +0 -24
  239. data/spec/support/guards.rb +0 -28
  240. data/spec/support/matchers/disambiguate_attributes.rb +0 -28
  241. data/spec/support/matchers/hash_the_same_as.rb +0 -26
  242. data/spec/support/matchers/have_rows.rb +0 -18
  243. data/spec/support/model.rb +0 -67
  244. data/spec/support/schemas/mysql_schema.rb +0 -26
  245. data/spec/support/schemas/oracle_schema.rb +0 -20
  246. data/spec/support/schemas/postgresql_schema.rb +0 -26
  247. data/spec/support/schemas/sqlite3_schema.rb +0 -26
@@ -0,0 +1,10 @@
1
+ module Arel
2
+ module Sql
3
+ class Engine
4
+ def self.new thing
5
+ #warn "#{caller.first} -- Engine will be removed"
6
+ thing
7
+ end
8
+ end
9
+ end
10
+ end
@@ -1,13 +1,4 @@
1
1
  module Arel
2
- class SqlLiteral < String
3
- def relation
4
- nil
5
- end
6
-
7
- def to_sql(formatter = nil)
8
- self
9
- end
10
-
11
- include Attribute::Expressions
2
+ class SqlLiteral < Nodes::SqlLiteral
12
3
  end
13
4
  end
@@ -0,0 +1,126 @@
1
+ module Arel
2
+ class Table
3
+ include Arel::Crud
4
+
5
+ @engine = nil
6
+ class << self; attr_accessor :engine; end
7
+
8
+ attr_accessor :name, :engine, :aliases, :table_alias
9
+
10
+ def initialize name, engine = Table.engine
11
+ @name = name
12
+ @engine = engine
13
+ @columns = nil
14
+ @aliases = []
15
+ @table_alias = nil
16
+ @primary_key = nil
17
+
18
+ if Hash === engine
19
+ @engine = engine[:engine] || Table.engine
20
+ @columns = attributes_for engine[:columns]
21
+
22
+ # Sometime AR sends an :as parameter to table, to let the table know
23
+ # that it is an Alias. We may want to override new, and return a
24
+ # TableAlias node?
25
+ @table_alias = engine[:as] unless engine[:as].to_s == name.to_s
26
+ end
27
+ end
28
+
29
+ def primary_key
30
+ @primary_key ||= begin
31
+ primary_key_name = @engine.connection.primary_key(name)
32
+ # some tables might be without primary key
33
+ primary_key_name && self[primary_key_name]
34
+ end
35
+ end
36
+
37
+ def alias
38
+ Nodes::TableAlias.new("#{name}_2", self).tap do |node|
39
+ @aliases << node
40
+ end
41
+ end
42
+
43
+ def from table
44
+ SelectManager.new(@engine, table)
45
+ end
46
+
47
+ def joins manager
48
+ nil
49
+ end
50
+
51
+ def join relation, klass = Nodes::InnerJoin
52
+ return from(self) unless relation
53
+
54
+ case relation
55
+ when String, Nodes::SqlLiteral
56
+ raise if relation.blank?
57
+ from Nodes::StringJoin.new(self, relation)
58
+ else
59
+ from klass.new(self, relation, nil)
60
+ end
61
+ end
62
+
63
+ def group *columns
64
+ from(self).group(*columns)
65
+ end
66
+
67
+ def order *expr
68
+ from(self).order(*expr)
69
+ end
70
+
71
+ def where condition
72
+ from(self).where condition
73
+ end
74
+
75
+ def project *things
76
+ from(self).project(*things)
77
+ end
78
+
79
+ def take amount
80
+ from(self).take amount
81
+ end
82
+
83
+ def having expr
84
+ from(self).having expr
85
+ end
86
+
87
+ def columns
88
+ @columns ||=
89
+ attributes_for @engine.connection.columns(@name, "#{@name} Columns")
90
+ end
91
+
92
+ def [] name
93
+ return nil unless table_exists?
94
+
95
+ name = name.to_sym
96
+ columns.find { |column| column.name == name }
97
+ end
98
+
99
+ def select_manager
100
+ SelectManager.new(@engine)
101
+ end
102
+
103
+ private
104
+
105
+ def attributes_for columns
106
+ return nil unless columns
107
+
108
+ columns.map do |column|
109
+ Attributes.for(column).new self, column.name.to_sym, column
110
+ end
111
+ end
112
+
113
+ def table_exists?
114
+ @table_exists ||= tables.key?(@name) || engine.connection.table_exists?(name)
115
+ end
116
+
117
+ def tables
118
+ self.class.table_cache(@engine)
119
+ end
120
+
121
+ @@table_cache = nil
122
+ def self.table_cache engine # :nodoc:
123
+ @@table_cache ||= Hash[engine.connection.tables.map { |x| [x,true] }]
124
+ end
125
+ end
126
+ end
@@ -0,0 +1,26 @@
1
+ module Arel
2
+ class TreeManager
3
+ # FIXME: Remove this.
4
+ include Arel::Relation
5
+
6
+ attr_accessor :visitor
7
+
8
+ def initialize engine
9
+ @engine = engine
10
+ @visitor = Visitors.visitor_for @engine
11
+ end
12
+
13
+ def to_dot
14
+ Visitors::Dot.new.accept @head
15
+ end
16
+
17
+ def to_sql
18
+ @visitor.accept @head
19
+ end
20
+
21
+ def initialize_copy other
22
+ super
23
+ @head = @head.clone
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,48 @@
1
+ module Arel
2
+ class UpdateManager < Arel::TreeManager
3
+ def initialize engine
4
+ super
5
+ @head = Nodes::UpdateStatement.new
6
+ end
7
+
8
+ def take limit
9
+ @head.limit = limit
10
+ self
11
+ end
12
+
13
+ def order *expr
14
+ @head.orders = expr
15
+ self
16
+ end
17
+
18
+ ###
19
+ # UPDATE +table+
20
+ def table table
21
+ @head.relation = table
22
+ self
23
+ end
24
+
25
+ def wheres= exprs
26
+ @head.wheres = exprs
27
+ end
28
+
29
+ def where expr
30
+ @head.wheres << expr
31
+ self
32
+ end
33
+
34
+ def set values
35
+ if String === values
36
+ @head.values = [values]
37
+ else
38
+ @head.values = values.map { |column,value|
39
+ Nodes::Assignment.new(
40
+ Nodes::UnqualifiedColumn.new(column),
41
+ value
42
+ )
43
+ }
44
+ end
45
+ self
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,30 @@
1
+ require 'arel/visitors/to_sql'
2
+ require 'arel/visitors/postgresql'
3
+ require 'arel/visitors/mysql'
4
+ require 'arel/visitors/oracle'
5
+ require 'arel/visitors/join_sql'
6
+ require 'arel/visitors/where_sql'
7
+ require 'arel/visitors/order_clauses'
8
+ require 'arel/visitors/dot'
9
+
10
+ module Arel
11
+ module Visitors
12
+ VISITORS = {
13
+ 'postgresql' => Arel::Visitors::PostgreSQL,
14
+ 'mysql' => Arel::Visitors::MySQL,
15
+ 'mysql2' => Arel::Visitors::MySQL,
16
+ 'oracle_enhanced' => Arel::Visitors::Oracle,
17
+ }
18
+
19
+ ENGINE_VISITORS = Hash.new do |hash, engine|
20
+ pool = engine.connection_pool
21
+ adapter = pool.spec.config[:adapter]
22
+ hash[engine] = (VISITORS[adapter] || Visitors::ToSql).new(engine)
23
+ end
24
+
25
+ def self.visitor_for engine
26
+ ENGINE_VISITORS[engine]
27
+ end
28
+ class << self; alias :for :visitor_for; end
29
+ end
30
+ end
@@ -0,0 +1,233 @@
1
+ module Arel
2
+ module Visitors
3
+ class Dot
4
+ class Node # :nodoc:
5
+ attr_accessor :name, :id, :fields
6
+
7
+ def initialize name, id, fields = []
8
+ @name = name
9
+ @id = id
10
+ @fields = fields
11
+ end
12
+ end
13
+
14
+ class Edge < Struct.new :name, :from, :to # :nodoc:
15
+ end
16
+
17
+ def initialize
18
+ @nodes = []
19
+ @edges = []
20
+ @node_stack = []
21
+ @edge_stack = []
22
+ @seen = {}
23
+ end
24
+
25
+ def accept object
26
+ visit object
27
+ to_dot
28
+ end
29
+
30
+ private
31
+ def visit_Arel_Nodes_Grouping o
32
+ visit_edge o, "expr"
33
+ end
34
+
35
+ def visit_Arel_Nodes_Ordering o
36
+ visit_edge o, "expr"
37
+ visit_edge o, "direction"
38
+ end
39
+
40
+ def visit_Arel_Nodes_TableAlias o
41
+ visit_edge o, "name"
42
+ visit_edge o, "relation"
43
+ visit_edge o, "columns"
44
+ end
45
+
46
+ def visit_Arel_Nodes_Sum o
47
+ visit_edge o, "expressions"
48
+ visit_edge o, "alias"
49
+ end
50
+ alias :visit_Arel_Nodes_Max :visit_Arel_Nodes_Sum
51
+ alias :visit_Arel_Nodes_Avg :visit_Arel_Nodes_Sum
52
+
53
+ def visit_Arel_Nodes_Count o
54
+ visit_edge o, "expressions"
55
+ visit_edge o, "distinct"
56
+ end
57
+
58
+ def visit_Arel_Nodes_On o
59
+ visit_edge o, "expr"
60
+ end
61
+
62
+ def visit_Arel_Nodes_Values o
63
+ visit_edge o, "expressions"
64
+ end
65
+
66
+ def visit_Arel_Nodes_StringJoin o
67
+ visit_edge o, "left"
68
+ visit_edge o, "right"
69
+ end
70
+
71
+ def visit_Arel_Nodes_InnerJoin o
72
+ visit_edge o, "left"
73
+ visit_edge o, "right"
74
+ visit_edge o, "constraint"
75
+ end
76
+ alias :visit_Arel_Nodes_OuterJoin :visit_Arel_Nodes_InnerJoin
77
+
78
+ def visit_Arel_Nodes_DeleteStatement o
79
+ visit_edge o, "relation"
80
+ visit_edge o, "wheres"
81
+ end
82
+
83
+ def visit_Arel_Nodes_UnqualifiedColumn o
84
+ visit_edge o, "attribute"
85
+ end
86
+
87
+ def visit_Arel_Nodes_Offset o
88
+ visit_edge o, "value"
89
+ end
90
+
91
+ def visit_Arel_Nodes_InsertStatement o
92
+ visit_edge o, "relation"
93
+ visit_edge o, "columns"
94
+ visit_edge o, "values"
95
+ end
96
+
97
+ def visit_Arel_Nodes_SelectCore o
98
+ visit_edge o, "froms"
99
+ visit_edge o, "projections"
100
+ visit_edge o, "wheres"
101
+ end
102
+
103
+ def visit_Arel_Nodes_SelectStatement o
104
+ visit_edge o, "cores"
105
+ visit_edge o, "limit"
106
+ visit_edge o, "orders"
107
+ visit_edge o, "offset"
108
+ end
109
+
110
+ def visit_Arel_Nodes_UpdateStatement o
111
+ visit_edge o, "relation"
112
+ visit_edge o, "wheres"
113
+ visit_edge o, "values"
114
+ end
115
+
116
+ def visit_Arel_Table o
117
+ visit_edge o, "name"
118
+ end
119
+
120
+ def visit_Arel_Attribute o
121
+ visit_edge o, "relation"
122
+ visit_edge o, "name"
123
+ end
124
+ alias :visit_Arel_Attributes_Integer :visit_Arel_Attribute
125
+ alias :visit_Arel_Attributes_Float :visit_Arel_Attribute
126
+ alias :visit_Arel_Attributes_String :visit_Arel_Attribute
127
+ alias :visit_Arel_Attributes_Time :visit_Arel_Attribute
128
+ alias :visit_Arel_Attributes_Boolean :visit_Arel_Attribute
129
+ alias :visit_Arel_Attributes_Attribute :visit_Arel_Attribute
130
+
131
+ def visit_Arel_Nodes_Equality o
132
+ visit_edge o, "left"
133
+ visit_edge o, "right"
134
+ end
135
+ alias :visit_Arel_Nodes_And :visit_Arel_Nodes_Equality
136
+ alias :visit_Arel_Nodes_Or :visit_Arel_Nodes_Equality
137
+ alias :visit_Arel_Nodes_NotEqual :visit_Arel_Nodes_Equality
138
+ alias :visit_Arel_Nodes_GreaterThan :visit_Arel_Nodes_Equality
139
+ alias :visit_Arel_Nodes_GreaterThanOrEqual :visit_Arel_Nodes_Equality
140
+ alias :visit_Arel_Nodes_Assignment :visit_Arel_Nodes_Equality
141
+ alias :visit_Arel_Nodes_In :visit_Arel_Nodes_Equality
142
+ alias :visit_Arel_Nodes_LessThan :visit_Arel_Nodes_Equality
143
+ alias :visit_Arel_Nodes_LessThanOrEqual :visit_Arel_Nodes_Equality
144
+ alias :visit_Arel_Nodes_Between :visit_Arel_Nodes_Equality
145
+ alias :visit_Arel_Nodes_NotIn :visit_Arel_Nodes_Equality
146
+ alias :visit_Arel_Nodes_DoesNotMatch :visit_Arel_Nodes_Equality
147
+ alias :visit_Arel_Nodes_Matches :visit_Arel_Nodes_Equality
148
+
149
+ def visit_String o
150
+ @node_stack.last.fields << o
151
+ end
152
+ alias :visit_Time :visit_String
153
+ alias :visit_Date :visit_String
154
+ alias :visit_DateTime :visit_String
155
+ alias :visit_NilClass :visit_String
156
+ alias :visit_TrueClass :visit_String
157
+ alias :visit_FalseClass :visit_String
158
+ alias :visit_Arel_SqlLiteral :visit_String
159
+ alias :visit_Fixnum :visit_String
160
+ alias :visit_BigDecimal :visit_String
161
+ alias :visit_Float :visit_String
162
+ alias :visit_Symbol :visit_String
163
+ alias :visit_Arel_Nodes_SqlLiteral :visit_String
164
+
165
+ def visit_Hash o
166
+ o.each_with_index do |pair, i|
167
+ edge("pair_#{i}") { visit pair }
168
+ end
169
+ end
170
+
171
+ def visit_Array o
172
+ o.each_with_index do |x,i|
173
+ edge(i) { visit x }
174
+ end
175
+ end
176
+
177
+ def visit_edge o, method
178
+ edge(method) { visit o.send(method) }
179
+ end
180
+
181
+ def visit o
182
+ if node = @seen[o.object_id]
183
+ @edge_stack.last.to = node
184
+ return
185
+ end
186
+
187
+ node = Node.new(o.class.name, o.object_id)
188
+ @seen[node.id] = node
189
+ @nodes << node
190
+ with_node node do
191
+ send "visit_#{o.class.name.gsub('::', '_')}", o
192
+ end
193
+ end
194
+
195
+ def edge name
196
+ edge = Edge.new(name, @node_stack.last)
197
+ @edge_stack.push edge
198
+ @edges << edge
199
+ yield
200
+ @edge_stack.pop
201
+ end
202
+
203
+ def with_node node
204
+ if edge = @edge_stack.last
205
+ edge.to = node
206
+ end
207
+
208
+ @node_stack.push node
209
+ yield
210
+ @node_stack.pop
211
+ end
212
+
213
+ def quote string
214
+ string.to_s.gsub('"', '\"')
215
+ end
216
+
217
+ def to_dot
218
+ "digraph \"ARel\" {\nnode [width=0.375,height=0.25,shape=record];\n" +
219
+ @nodes.map { |node|
220
+ label = "<f0>#{node.name}"
221
+
222
+ node.fields.each_with_index do |field, i|
223
+ label << "|<f#{i + 1}>#{quote field}"
224
+ end
225
+
226
+ "#{node.id} [label=\"#{label}\"];"
227
+ }.join("\n") + "\n" + @edges.map { |edge|
228
+ "#{edge.from.id} -> #{edge.to.id} [label=\"#{edge.name}\"];"
229
+ }.join("\n") + "\n}"
230
+ end
231
+ end
232
+ end
233
+ end