aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEmilio Tagua <miloops@gmail.com>2009-05-26 12:41:52 -0300
committerEmilio Tagua <miloops@gmail.com>2009-05-26 12:41:52 -0300
commitc9bbea6115be520dbd47bd30108c5622289deb26 (patch)
treeec418e01954c1bd2dcfebc7fbc8220fb04b50baf
parentae1e0ac5e98a7e5a2894d0a431f8c34af6575cae (diff)
parent86364591af807ed3fa4a7304f53e6f3458cb4961 (diff)
downloadrails-c9bbea6115be520dbd47bd30108c5622289deb26.tar.gz
rails-c9bbea6115be520dbd47bd30108c5622289deb26.tar.bz2
rails-c9bbea6115be520dbd47bd30108c5622289deb26.zip
Merge commit 'brynary/master'
Conflicts: lib/arel.rb lib/arel/session.rb
-rw-r--r--.gitignore2
-rw-r--r--README.markdown (renamed from README)6
-rw-r--r--Rakefile37
-rw-r--r--doc/TODO56
-rw-r--r--lib/arel.rb13
-rw-r--r--lib/arel/algebra.rb4
-rw-r--r--lib/arel/algebra/extensions.rb4
-rw-r--r--lib/arel/algebra/extensions/class.rb32
-rw-r--r--lib/arel/algebra/extensions/hash.rb11
-rw-r--r--lib/arel/algebra/extensions/object.rb17
-rw-r--r--lib/arel/algebra/extensions/symbol.rb9
-rw-r--r--lib/arel/algebra/predicates.rb41
-rw-r--r--lib/arel/algebra/primitives.rb5
-rw-r--r--lib/arel/algebra/primitives/attribute.rb150
-rw-r--r--lib/arel/algebra/primitives/expression.rb43
-rw-r--r--lib/arel/algebra/primitives/ordering.rb23
-rw-r--r--lib/arel/algebra/primitives/value.rb14
-rw-r--r--lib/arel/algebra/relations.rb14
-rw-r--r--lib/arel/algebra/relations/operations/alias.rb7
-rw-r--r--lib/arel/algebra/relations/operations/group.rb12
-rw-r--r--lib/arel/algebra/relations/operations/join.rb64
-rw-r--r--lib/arel/algebra/relations/operations/order.rb18
-rw-r--r--lib/arel/algebra/relations/operations/project.rb20
-rw-r--r--lib/arel/algebra/relations/operations/skip.rb6
-rw-r--r--lib/arel/algebra/relations/operations/take.rb10
-rw-r--r--lib/arel/algebra/relations/operations/where.rb16
-rw-r--r--lib/arel/algebra/relations/relation.rb136
-rw-r--r--lib/arel/algebra/relations/row.rb26
-rw-r--r--lib/arel/algebra/relations/utilities/compound.rb30
-rw-r--r--lib/arel/algebra/relations/utilities/externalization.rb24
-rw-r--r--lib/arel/algebra/relations/utilities/nil.rb7
-rw-r--r--lib/arel/algebra/relations/writes.rb36
-rw-r--r--lib/arel/engines.rb2
-rw-r--r--lib/arel/engines/memory.rb4
-rw-r--r--lib/arel/engines/memory/engine.rb16
-rw-r--r--lib/arel/engines/memory/predicates.rb35
-rw-r--r--lib/arel/engines/memory/primitives.rb27
-rw-r--r--lib/arel/engines/memory/relations.rb5
-rw-r--r--lib/arel/engines/memory/relations/array.rb25
-rw-r--r--lib/arel/engines/memory/relations/compound.rb9
-rw-r--r--lib/arel/engines/memory/relations/operations.rb61
-rw-r--r--lib/arel/engines/memory/relations/writes.rb7
-rw-r--r--lib/arel/engines/sql.rb7
-rw-r--r--lib/arel/engines/sql/christener.rb13
-rw-r--r--lib/arel/engines/sql/engine.rb37
-rw-r--r--lib/arel/engines/sql/extensions.rb4
-rw-r--r--lib/arel/engines/sql/extensions/array.rb16
-rw-r--r--lib/arel/engines/sql/extensions/nil_class.rb11
-rw-r--r--lib/arel/engines/sql/extensions/object.rb15
-rw-r--r--lib/arel/engines/sql/extensions/range.rb15
-rw-r--r--lib/arel/engines/sql/formatters.rb113
-rw-r--r--lib/arel/engines/sql/predicates.rb51
-rw-r--r--lib/arel/engines/sql/primitives.rb85
-rw-r--r--lib/arel/engines/sql/relations.rb9
-rw-r--r--lib/arel/engines/sql/relations/operations/alias.rb5
-rw-r--r--lib/arel/engines/sql/relations/operations/join.rb33
-rw-r--r--lib/arel/engines/sql/relations/relation.rb50
-rw-r--r--lib/arel/engines/sql/relations/table.rb52
-rw-r--r--lib/arel/engines/sql/relations/utilities/compound.rb10
-rw-r--r--lib/arel/engines/sql/relations/utilities/externalization.rb14
-rw-r--r--lib/arel/engines/sql/relations/utilities/nil.rb6
-rw-r--r--lib/arel/engines/sql/relations/utilities/recursion.rb13
-rw-r--r--lib/arel/engines/sql/relations/writes.rb39
-rw-r--r--lib/arel/session.rb48
-rw-r--r--spec/arel/algebra/unit/predicates/binary_spec.rb33
-rw-r--r--spec/arel/algebra/unit/predicates/equality_spec.rb (renamed from spec/arel/unit/predicates/equality_spec.rb)34
-rw-r--r--spec/arel/algebra/unit/predicates/in_spec.rb10
-rw-r--r--spec/arel/algebra/unit/primitives/attribute_spec.rb (renamed from spec/arel/unit/primitives/attribute_spec.rb)104
-rw-r--r--spec/arel/algebra/unit/primitives/expression_spec.rb (renamed from spec/arel/unit/primitives/expression_spec.rb)22
-rw-r--r--spec/arel/algebra/unit/primitives/value_spec.rb15
-rw-r--r--spec/arel/algebra/unit/relations/alias_spec.rb16
-rw-r--r--spec/arel/algebra/unit/relations/delete_spec.rb9
-rw-r--r--spec/arel/algebra/unit/relations/group_spec.rb10
-rw-r--r--spec/arel/algebra/unit/relations/insert_spec.rb9
-rw-r--r--spec/arel/algebra/unit/relations/join_spec.rb26
-rw-r--r--spec/arel/algebra/unit/relations/order_spec.rb21
-rw-r--r--spec/arel/algebra/unit/relations/project_spec.rb34
-rw-r--r--spec/arel/algebra/unit/relations/relation_spec.rb (renamed from spec/arel/unit/relations/relation_spec.rb)42
-rw-r--r--spec/arel/algebra/unit/relations/skip_spec.rb10
-rw-r--r--spec/arel/algebra/unit/relations/table_spec.rb39
-rw-r--r--spec/arel/algebra/unit/relations/take_spec.rb10
-rw-r--r--spec/arel/algebra/unit/relations/update_spec.rb9
-rw-r--r--spec/arel/algebra/unit/relations/where_spec.rb18
-rw-r--r--spec/arel/algebra/unit/session/session_spec.rb (renamed from spec/arel/unit/session/session_spec.rb)12
-rw-r--r--spec/arel/engines/memory/integration/joins/cross_engine_spec.rb48
-rw-r--r--spec/arel/engines/memory/unit/relations/array_spec.rb32
-rw-r--r--spec/arel/engines/memory/unit/relations/insert_spec.rb28
-rw-r--r--spec/arel/engines/memory/unit/relations/join_spec.rb31
-rw-r--r--spec/arel/engines/memory/unit/relations/order_spec.rb27
-rw-r--r--spec/arel/engines/memory/unit/relations/project_spec.rb27
-rw-r--r--spec/arel/engines/memory/unit/relations/skip_spec.rb26
-rw-r--r--spec/arel/engines/memory/unit/relations/take_spec.rb26
-rw-r--r--spec/arel/engines/memory/unit/relations/where_spec.rb39
-rw-r--r--spec/arel/engines/sql/integration/joins/with_adjacency_spec.rb209
-rw-r--r--spec/arel/engines/sql/integration/joins/with_aggregations_spec.rb167
-rw-r--r--spec/arel/engines/sql/integration/joins/with_compounds_spec.rb107
-rw-r--r--spec/arel/engines/sql/unit/engine_spec.rb45
-rw-r--r--spec/arel/engines/sql/unit/predicates/binary_spec.rb117
-rw-r--r--spec/arel/engines/sql/unit/predicates/equality_spec.rb46
-rw-r--r--spec/arel/engines/sql/unit/predicates/in_spec.rb86
-rw-r--r--spec/arel/engines/sql/unit/predicates/predicates_spec.rb65
-rw-r--r--spec/arel/engines/sql/unit/primitives/attribute_spec.rb32
-rw-r--r--spec/arel/engines/sql/unit/primitives/expression_spec.rb24
-rw-r--r--spec/arel/engines/sql/unit/primitives/literal_spec.rb23
-rw-r--r--spec/arel/engines/sql/unit/primitives/value_spec.rb (renamed from spec/arel/unit/primitives/value_spec.rb)17
-rw-r--r--spec/arel/engines/sql/unit/relations/alias_spec.rb43
-rw-r--r--spec/arel/engines/sql/unit/relations/delete_spec.rb63
-rw-r--r--spec/arel/engines/sql/unit/relations/group_spec.rb56
-rw-r--r--spec/arel/engines/sql/unit/relations/insert_spec.rb107
-rw-r--r--spec/arel/engines/sql/unit/relations/join_spec.rb57
-rw-r--r--spec/arel/engines/sql/unit/relations/order_spec.rb113
-rw-r--r--spec/arel/engines/sql/unit/relations/project_spec.rb110
-rw-r--r--spec/arel/engines/sql/unit/relations/skip_spec.rb32
-rw-r--r--spec/arel/engines/sql/unit/relations/table_spec.rb69
-rw-r--r--spec/arel/engines/sql/unit/relations/take_spec.rb32
-rw-r--r--spec/arel/engines/sql/unit/relations/update_spec.rb151
-rw-r--r--spec/arel/engines/sql/unit/relations/where_spec.rb56
-rw-r--r--spec/arel/integration/joins/with_adjacency_spec.rb139
-rw-r--r--spec/arel/integration/joins/with_aggregations_spec.rb89
-rw-r--r--spec/arel/integration/joins/with_compounds_spec.rb65
-rw-r--r--spec/arel/unit/predicates/binary_spec.rb95
-rw-r--r--spec/arel/unit/predicates/in_spec.rb58
-rw-r--r--spec/arel/unit/predicates/predicates_spec.rb33
-rw-r--r--spec/arel/unit/relations/alias_spec.rb36
-rw-r--r--spec/arel/unit/relations/delete_spec.rb42
-rw-r--r--spec/arel/unit/relations/group_spec.rb32
-rw-r--r--spec/arel/unit/relations/insert_spec.rb71
-rw-r--r--spec/arel/unit/relations/join_spec.rb54
-rw-r--r--spec/arel/unit/relations/order_spec.rb66
-rw-r--r--spec/arel/unit/relations/project_spec.rb81
-rw-r--r--spec/arel/unit/relations/skip_spec.rb20
-rw-r--r--spec/arel/unit/relations/table_spec.rb88
-rw-r--r--spec/arel/unit/relations/take_spec.rb20
-rw-r--r--spec/arel/unit/relations/update_spec.rb81
-rw-r--r--spec/arel/unit/relations/where_spec.rb40
-rw-r--r--spec/connections/mysql_connection.rb15
-rw-r--r--spec/connections/postgresql_connection.rb14
-rw-r--r--spec/connections/sqlite3_connection.rb25
-rw-r--r--spec/doubles/database.rb51
-rw-r--r--spec/doubles/hash.rb8
-rw-r--r--spec/matchers/be_like.rb10
-rw-r--r--spec/matchers/disambiguate_attributes.rb10
-rw-r--r--spec/matchers/hash_the_same_as.rb10
-rw-r--r--spec/schemas/mysql_schema.rb18
-rw-r--r--spec/schemas/postgresql_schema.rb18
-rw-r--r--spec/schemas/sqlite3_schema.rb18
-rw-r--r--spec/spec_helper.rb31
147 files changed, 4342 insertions, 1320 deletions
diff --git a/.gitignore b/.gitignore
index 2bab52ab21..c6dd721751 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,5 @@
coverage/*
config/database.yml
+spec/fixtures/*database*
*.DS_Store
+debug.log
diff --git a/README b/README.markdown
index e979dbc2a3..4d95234423 100644
--- a/README
+++ b/README.markdown
@@ -18,7 +18,7 @@ Generating a query with ARel is simple. For example, in order to produce
you construct a table relation and convert it to sql:
- users = Arel(:users)
+ users = Table(:users)
users.to_sql
In fact, you will probably never call `#to_sql`. Rather, you'll work with data from the table directly. You can iterate through all rows in the `users` table like this:
@@ -81,7 +81,7 @@ The `AND` operator will behave similarly.
Finally, most operations take a block form. For example:
- Arel(:users) \
+ Table(:users) \
.where { |u| u[:id].eq(1) } \
.project { |u| u[:id] }
@@ -95,7 +95,7 @@ The examples above are fairly simple and other libraries match or come close to
Where Arel really shines in its ability to handle complex joins and aggregations. As a first example, let's consider an "adjacency list", a tree represented in a table. Suppose we have a table `comments`, representing a threaded discussion:
- comments = Arel(:comments)
+ comments = Table(:comments)
And this table has the following attributes:
diff --git a/Rakefile b/Rakefile
index b1fc74a13c..17a6f8d35d 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,15 +1,40 @@
require 'rubygems'
require 'spec/rake/spectask'
-Spec::Rake::SpecTask.new do |t|
- t.spec_files = FileList['spec/**/*_spec.rb']
-end
+spec_file_list = FileList['spec/**/*_spec.rb']
+desc "Run specs using RCov (uses mysql database adapter)"
Spec::Rake::SpecTask.new(:coverage) do |t|
- t.spec_files = FileList['spec/**/*_spec.rb']
+ t.spec_files =
+ ["spec/connections/mysql_connection.rb"] +
+ spec_file_list
+
t.rcov = true
- t.rcov_opts = ['-x', 'spec,gems']
+ t.rcov_opts << '--exclude' << "spec,gems"
+ t.rcov_opts << '--text-summary'
+ t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
+ t.rcov_opts << '--only-uncovered'
+end
+
+namespace :spec do
+ for adapter in %w[mysql sqlite3 postgresql]
+ desc "Run specs with the #{adapter} database adapter"
+ Spec::Rake::SpecTask.new(adapter) do |t|
+ t.spec_files =
+ ["spec/connections/#{adapter}_connection.rb"] +
+ ["spec/schemas/#{adapter}_schema.rb"] +
+ spec_file_list
+ end
+ end
end
+desc "Run specs with mysql and sqlite3 database adapters (default)"
+task :spec => ["spec:sqlite3", "spec:mysql", "spec:postgresql"]
+
desc "Default task is to run specs"
-task :default => :spec \ No newline at end of file
+task :default => :spec
+
+desc 'Removes trailing whitespace'
+task :whitespace do
+ sh %{find . -name '*.rb' -exec sed -i '' 's/ *$//g' {} \\;}
+end
diff --git a/doc/TODO b/doc/TODO
index 65f9cfbca7..61ff24f4a0 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -1,29 +1,26 @@
todo:
-- joining with LIMIT is like aggregations!!
+- fix AR again
+- blocks for joins
+- fix sql insertions
+- implement mnesia adapter
-users.delete().where(
- addresses.c.user_id==
- select([users.c.id]).
- where(users.c.name=='jack')
- )
-
- SELECT id, name,
- (select count(*) FROM addresses WHERE
- user_id=users.id)
- FROM users
-
- SELECT users.*, (SELECT count(id) FROM addresses WHERE
- addresses.user_id=users.id) FROM users
-- blocks for all operations
-- result sets to attr correlation too
+- CLEANUP!!!!!
+- rename externalize to derived.
+- deal with table tests in algebra
+- fix grouping
+- audit unit coverage of algebra
+- data objects
+- remove all explicit aliasing
+- scoped writes
+- refactor adapter pattern
+- break out adapters into sep modules
+- projection is by definition distincting?
+- union/intersection
- cache expiry on write
- - rewrite of arecord querycache test in light of this
- transactions
-- scoped writes
done:
- and/or w/ predicates
-- mock out database
. Relation <=> Relation -> InnerJoinOperation
. Relation << Relation -> LeftOuterJoinOperation
. InnerJoinOperation.on(*Predicate) -> InnerJoinRelation
@@ -74,6 +71,7 @@ done:
- test Value, in particular bind.
- test blank checks in relation.rb
- rename active_relation to arel
+- mock out database
- fix complex joining cases:
- active record query adapter
- anonymous table names
@@ -84,6 +82,20 @@ done:
- test: find_attribute_given_attribute and all @attribute ||= everywhere and memoization of table class.
- rename select to where
- rename all ion classes
+- joining with LIMIT is like aggregations!!
+- blocks for non-joins
+- expressions should be class-based, and joins too, anything _sql should be renamed
+- implement in memory adapter
+- clean up block_given stuff
+- reorganize sql tests
+- recursive memory operations
+- reorganize memory tests
+- result sets to attr correlation too
+- implement joins in memory
+- result sets should be array relations
+- fix AR
+- insertions for in memory
+- cross-engine joins
icebox:
- #bind in Attribute and Expression should be doing a descend?
@@ -98,3 +110,9 @@ icebox:
@reflection.options[:counter_sql] = @reflection.options[:finder_sql].sub(/SELECT (\/\*.*?\*\/ )?(.*)\bFROM\b/im) { "SELECT #{$1}COUNT(*) FROM" }
- lock
- SELECT suchandsuch FOR UPDATE
+- joins become subselects in writes:
+users.delete().where(
+ addresses.c.user_id==
+ select([users.c.id]).
+ where(users.c.name=='jack')
+ ) \ No newline at end of file
diff --git a/lib/arel.rb b/lib/arel.rb
new file mode 100644
index 0000000000..54a31b6ed0
--- /dev/null
+++ b/lib/arel.rb
@@ -0,0 +1,13 @@
+$LOAD_PATH.unshift(File.dirname(__FILE__))
+
+require 'rubygems'
+require 'activesupport'
+require 'active_support/dependencies'
+require 'active_support/core_ext/class/attribute_accessors'
+require 'active_support/core_ext/module/delegation'
+require 'activerecord'
+require 'active_record/connection_adapters/abstract/quoting'
+
+require 'arel/algebra'
+require 'arel/engines'
+require 'arel/session'
diff --git a/lib/arel/algebra.rb b/lib/arel/algebra.rb
new file mode 100644
index 0000000000..c206fea0b0
--- /dev/null
+++ b/lib/arel/algebra.rb
@@ -0,0 +1,4 @@
+require 'arel/algebra/extensions'
+require 'arel/algebra/predicates'
+require 'arel/algebra/relations'
+require 'arel/algebra/primitives'
diff --git a/lib/arel/algebra/extensions.rb b/lib/arel/algebra/extensions.rb
new file mode 100644
index 0000000000..bc8edd3274
--- /dev/null
+++ b/lib/arel/algebra/extensions.rb
@@ -0,0 +1,4 @@
+require 'arel/algebra/extensions/object'
+require 'arel/algebra/extensions/class'
+require 'arel/algebra/extensions/symbol'
+require 'arel/algebra/extensions/hash'
diff --git a/lib/arel/algebra/extensions/class.rb b/lib/arel/algebra/extensions/class.rb
new file mode 100644
index 0000000000..520111b90f
--- /dev/null
+++ b/lib/arel/algebra/extensions/class.rb
@@ -0,0 +1,32 @@
+module Arel
+ module ClassExtensions
+ def attributes(*attrs)
+ @attributes = attrs
+ attr_reader *attrs
+ end
+
+ def deriving(*methods)
+ methods.each { |m| derive m }
+ end
+
+ def derive(method_name)
+ methods = {
+ :initialize => "
+ def #{method_name}(#{@attributes.join(',')})
+ #{@attributes.collect { |a| "@#{a} = #{a}" }.join("\n")}
+ end
+ ",
+ :== => "
+ def ==(other)
+ #{name} === other &&
+ #{@attributes.collect { |a| "@#{a} == other.#{a}" }.join(" &&\n")}
+ end
+ "
+ }
+ class_eval methods[method_name], __FILE__, __LINE__
+ end
+
+ Class.send(:include, self)
+ end
+end
+
diff --git a/lib/arel/algebra/extensions/hash.rb b/lib/arel/algebra/extensions/hash.rb
new file mode 100644
index 0000000000..05c15e7ebe
--- /dev/null
+++ b/lib/arel/algebra/extensions/hash.rb
@@ -0,0 +1,11 @@
+module Arel
+ module HashExtensions
+ def bind(relation)
+ inject({}) do |bound, (key, value)|
+ bound.merge(key.bind(relation) => value.bind(relation))
+ end
+ end
+
+ Hash.send(:include, self)
+ end
+end
diff --git a/lib/arel/algebra/extensions/object.rb b/lib/arel/algebra/extensions/object.rb
new file mode 100644
index 0000000000..d8c60b5dd5
--- /dev/null
+++ b/lib/arel/algebra/extensions/object.rb
@@ -0,0 +1,17 @@
+module Arel
+ module ObjectExtensions
+ def bind(relation)
+ Arel::Value.new(self, relation)
+ end
+
+ def find_correlate_in(relation)
+ bind(relation)
+ end
+
+ def let
+ yield(self)
+ end
+
+ Object.send(:include, self)
+ end
+end
diff --git a/lib/arel/algebra/extensions/symbol.rb b/lib/arel/algebra/extensions/symbol.rb
new file mode 100644
index 0000000000..9bb47ef7ab
--- /dev/null
+++ b/lib/arel/algebra/extensions/symbol.rb
@@ -0,0 +1,9 @@
+module Arel
+ module SymbolExtensions
+ def to_attribute(relation)
+ Arel::Attribute.new(relation, self)
+ end
+
+ Symbol.send(:include, self)
+ end
+end
diff --git a/lib/arel/algebra/predicates.rb b/lib/arel/algebra/predicates.rb
new file mode 100644
index 0000000000..72167c2b27
--- /dev/null
+++ b/lib/arel/algebra/predicates.rb
@@ -0,0 +1,41 @@
+module Arel
+ class Predicate
+ def or(other_predicate)
+ Or.new(self, other_predicate)
+ end
+
+ def and(other_predicate)
+ And.new(self, other_predicate)
+ end
+ end
+
+ class Binary < Predicate
+ attributes :operand1, :operand2
+ deriving :initialize
+
+ def ==(other)
+ self.class === other and
+ @operand1 == other.operand1 and
+ @operand2 == other.operand2
+ end
+
+ def bind(relation)
+ self.class.new(operand1.find_correlate_in(relation), operand2.find_correlate_in(relation))
+ end
+ end
+
+ class Equality < Binary
+ def ==(other)
+ Equality === other and
+ ((operand1 == other.operand1 and operand2 == other.operand2) or
+ (operand1 == other.operand2 and operand2 == other.operand1))
+ end
+ end
+
+ class GreaterThanOrEqualTo < Binary; end
+ class GreaterThan < Binary; end
+ class LessThanOrEqualTo < Binary; end
+ class LessThan < Binary; end
+ class Match < Binary; end
+ class In < Binary; end
+end
diff --git a/lib/arel/algebra/primitives.rb b/lib/arel/algebra/primitives.rb
new file mode 100644
index 0000000000..df8d16a5d5
--- /dev/null
+++ b/lib/arel/algebra/primitives.rb
@@ -0,0 +1,5 @@
+require 'arel/algebra/primitives/attribute'
+require 'arel/algebra/primitives/ordering'
+require 'arel/algebra/primitives/value'
+require 'arel/algebra/primitives/expression'
+
diff --git a/lib/arel/algebra/primitives/attribute.rb b/lib/arel/algebra/primitives/attribute.rb
new file mode 100644
index 0000000000..44a2f41733
--- /dev/null
+++ b/lib/arel/algebra/primitives/attribute.rb
@@ -0,0 +1,150 @@
+require 'set'
+
+module Arel
+ class Attribute
+ attributes :relation, :name, :alias, :ancestor
+ deriving :==
+ delegate :engine, :christener, :to => :relation
+
+ def initialize(relation, name, options = {})
+ @relation, @name, @alias, @ancestor = relation, name, options[:alias], options[:ancestor]
+ end
+
+ def named?(hypothetical_name)
+ (@alias || name).to_s == hypothetical_name.to_s
+ end
+
+ def aggregation?
+ false
+ end
+
+ def inspect
+ "<Attribute #{name}>"
+ end
+
+ module Transformations
+ def self.included(klass)
+ klass.send :alias_method, :eql?, :==
+ end
+
+ def hash
+ @hash ||= history.size + name.hash + relation.hash
+ end
+
+ def as(aliaz = nil)
+ Attribute.new(relation, name, :alias => aliaz, :ancestor => self)
+ end
+
+ def bind(new_relation)
+ relation == new_relation ? self : Attribute.new(new_relation, name, :alias => @alias, :ancestor => self)
+ end
+
+ def to_attribute(relation)
+ bind(relation)
+ end
+ end
+ include Transformations
+
+ module Congruence
+ def history
+ @history ||= [self] + (ancestor ? ancestor.history : [])
+ end
+
+ def join?
+ relation.join?
+ end
+
+ def root
+ history.last
+ end
+
+ def original_relation
+ @original_relation ||= original_attribute.relation
+ end
+
+ def original_attribute
+ @original_attribute ||= history.detect { |a| !a.join? }
+ end
+
+ def find_correlate_in(relation)
+ relation[self] || self
+ end
+
+ def descends_from?(other)
+ history.include?(other)
+ end
+
+ def /(other)
+ other ? (history & other.history).size : 0
+ end
+ end
+ include Congruence
+
+ module Predications
+ def eq(other)
+ Equality.new(self, other)
+ end
+
+ def lt(other)
+ LessThan.new(self, other)
+ end
+
+ def lteq(other)
+ LessThanOrEqualTo.new(self, other)
+ end
+
+ def gt(other)
+ GreaterThan.new(self, other)
+ end
+
+ def gteq(other)
+ GreaterThanOrEqualTo.new(self, other)
+ end
+
+ def matches(regexp)
+ Match.new(self, regexp)
+ end
+
+ def in(array)
+ In.new(self, array)
+ end
+ end
+ include Predications
+
+ module Expressions
+ def count(distinct = false)
+ distinct ? Distinct.new(self).count : Count.new(self)
+ end
+
+ def sum
+ Sum.new(self)
+ end
+
+ def maximum
+ Maximum.new(self)
+ end
+
+ def minimum
+ Minimum.new(self)
+ end
+
+ def average
+ Average.new(self)
+ end
+ end
+ include Expressions
+
+ module Orderings
+ def asc
+ Ascending.new(self)
+ end
+
+ def desc
+ Descending.new(self)
+ end
+
+ alias_method :to_ordering, :asc
+ end
+ include Orderings
+ end
+end
diff --git a/lib/arel/algebra/primitives/expression.rb b/lib/arel/algebra/primitives/expression.rb
new file mode 100644
index 0000000000..875498c282
--- /dev/null
+++ b/lib/arel/algebra/primitives/expression.rb
@@ -0,0 +1,43 @@
+module Arel
+ class Expression < Attribute
+ attributes :attribute, :alias, :ancestor
+ deriving :==
+ delegate :relation, :to => :attribute
+ alias_method :name, :alias
+
+ def initialize(attribute, aliaz = nil, ancestor = nil)
+ @attribute, @alias, @ancestor = attribute, aliaz, ancestor
+ end
+
+ def aggregation?
+ true
+ end
+
+ def inspect
+ "<#{self.class.name} #{attribute.inspect}>"
+ end
+
+ module Transformations
+ def as(aliaz)
+ self.class.new(attribute, aliaz, self)
+ end
+
+ def bind(new_relation)
+ new_relation == relation ? self : self.class.new(attribute.bind(new_relation), @alias, self)
+ end
+
+ def to_attribute(relation)
+ Attribute.new(relation, @alias, :ancestor => self)
+ end
+ end
+ include Transformations
+ end
+
+ class Count < Expression; end
+ class Distinct < Expression; end
+ class Sum < Expression; end
+ class Maximum < Expression; end
+ class Minimum < Expression; end
+ class Average < Expression; end
+end
+
diff --git a/lib/arel/algebra/primitives/ordering.rb b/lib/arel/algebra/primitives/ordering.rb
new file mode 100644
index 0000000000..3efb4c6280
--- /dev/null
+++ b/lib/arel/algebra/primitives/ordering.rb
@@ -0,0 +1,23 @@
+module Arel
+ class Ordering
+ delegate :relation, :to => :attribute
+
+ def bind(relation)
+ self.class.new(attribute.bind(relation))
+ end
+
+ def to_ordering
+ self
+ end
+ end
+
+ class Ascending < Ordering
+ attributes :attribute
+ deriving :initialize, :==
+ end
+
+ class Descending < Ordering
+ attributes :attribute
+ deriving :initialize, :==
+ end
+end
diff --git a/lib/arel/algebra/primitives/value.rb b/lib/arel/algebra/primitives/value.rb
new file mode 100644
index 0000000000..e363805140
--- /dev/null
+++ b/lib/arel/algebra/primitives/value.rb
@@ -0,0 +1,14 @@
+module Arel
+ class Value
+ attributes :value, :relation
+ deriving :initialize, :==
+
+ def bind(relation)
+ Value.new(value, relation)
+ end
+
+ def to_ordering
+ self
+ end
+ end
+end
diff --git a/lib/arel/algebra/relations.rb b/lib/arel/algebra/relations.rb
new file mode 100644
index 0000000000..f9fa24ba25
--- /dev/null
+++ b/lib/arel/algebra/relations.rb
@@ -0,0 +1,14 @@
+require 'arel/algebra/relations/relation'
+require 'arel/algebra/relations/utilities/compound'
+require 'arel/algebra/relations/utilities/nil'
+require 'arel/algebra/relations/utilities/externalization'
+require 'arel/algebra/relations/row'
+require 'arel/algebra/relations/writes'
+require 'arel/algebra/relations/operations/alias'
+require 'arel/algebra/relations/operations/group'
+require 'arel/algebra/relations/operations/join'
+require 'arel/algebra/relations/operations/order'
+require 'arel/algebra/relations/operations/project'
+require 'arel/algebra/relations/operations/where'
+require 'arel/algebra/relations/operations/skip'
+require 'arel/algebra/relations/operations/take'
diff --git a/lib/arel/algebra/relations/operations/alias.rb b/lib/arel/algebra/relations/operations/alias.rb
new file mode 100644
index 0000000000..0331d98b85
--- /dev/null
+++ b/lib/arel/algebra/relations/operations/alias.rb
@@ -0,0 +1,7 @@
+module Arel
+ class Alias < Compound
+ attributes :relation
+ deriving :initialize
+ alias_method :==, :equal?
+ end
+end
diff --git a/lib/arel/algebra/relations/operations/group.rb b/lib/arel/algebra/relations/operations/group.rb
new file mode 100644
index 0000000000..2bfc42214b
--- /dev/null
+++ b/lib/arel/algebra/relations/operations/group.rb
@@ -0,0 +1,12 @@
+module Arel
+ class Group < Compound
+ attributes :relation, :groupings
+ deriving :==
+
+ def initialize(relation, *groupings, &block)
+ @relation = relation
+ @groupings = (groupings + arguments_from_block(relation, &block)) \
+ .collect { |g| g.bind(relation) }
+ end
+ end
+end
diff --git a/lib/arel/algebra/relations/operations/join.rb b/lib/arel/algebra/relations/operations/join.rb
new file mode 100644
index 0000000000..e9320f28e1
--- /dev/null
+++ b/lib/arel/algebra/relations/operations/join.rb
@@ -0,0 +1,64 @@
+module Arel
+ class Join < Relation
+ attributes :relation1, :relation2, :predicates
+ deriving :==
+ delegate :name, :to => :relation1
+
+ def initialize(relation1, relation2 = Nil.instance, *predicates)
+ @relation1, @relation2, @predicates = relation1, relation2, predicates
+ end
+
+ def hash
+ @hash ||= :relation1.hash
+ end
+
+ def eql?(other)
+ self == other
+ end
+
+ def attributes
+ @attributes ||= (relation1.externalize.attributes +
+ relation2.externalize.attributes).collect { |a| a.bind(self) }
+ end
+
+ def wheres
+ # TESTME bind to self?
+ relation1.externalize.wheres
+ end
+
+ def ons
+ @ons ||= @predicates.collect { |p| p.bind(self) }
+ end
+
+ # TESTME
+ def externalizable?
+ relation1.externalizable? or relation2.externalizable?
+ end
+
+ def join?
+ true
+ end
+
+ def engine
+ relation1.engine != relation2.engine ? Memory::Engine.new : relation1.engine
+ end
+ end
+
+ class InnerJoin < Join; end
+ class OuterJoin < Join; end
+ class StringJoin < Join
+ def attributes
+ relation1.externalize.attributes
+ end
+
+ def engine
+ relation1.engine
+ end
+ end
+
+ class Relation
+ def join?
+ false
+ end
+ end
+end
diff --git a/lib/arel/algebra/relations/operations/order.rb b/lib/arel/algebra/relations/operations/order.rb
new file mode 100644
index 0000000000..a589b56997
--- /dev/null
+++ b/lib/arel/algebra/relations/operations/order.rb
@@ -0,0 +1,18 @@
+module Arel
+ class Order < Compound
+ attributes :relation, :orderings
+ deriving :==
+
+ def initialize(relation, *orderings, &block)
+ @relation = relation
+ @orderings = (orderings + arguments_from_block(relation, &block)) \
+ .collect { |o| o.bind(relation) }
+ end
+
+ # TESTME
+ def orders
+ # QUESTION - do we still need relation.orders ?
+ (orderings + relation.orders).collect { |o| o.bind(self) }.collect { |o| o.to_ordering }
+ end
+ end
+end
diff --git a/lib/arel/algebra/relations/operations/project.rb b/lib/arel/algebra/relations/operations/project.rb
new file mode 100644
index 0000000000..223d320e22
--- /dev/null
+++ b/lib/arel/algebra/relations/operations/project.rb
@@ -0,0 +1,20 @@
+module Arel
+ class Project < Compound
+ attributes :relation, :projections
+ deriving :==
+
+ def initialize(relation, *projections, &block)
+ @relation = relation
+ @projections = (projections + arguments_from_block(relation, &block)) \
+ .collect { |p| p.bind(relation) }
+ end
+
+ def attributes
+ @attributes ||= projections.collect { |p| p.bind(self) }
+ end
+
+ def externalizable?
+ attributes.any?(&:aggregation?) or relation.externalizable?
+ end
+ end
+end
diff --git a/lib/arel/algebra/relations/operations/skip.rb b/lib/arel/algebra/relations/operations/skip.rb
new file mode 100644
index 0000000000..689e06e1c3
--- /dev/null
+++ b/lib/arel/algebra/relations/operations/skip.rb
@@ -0,0 +1,6 @@
+module Arel
+ class Skip < Compound
+ attributes :relation, :skipped
+ deriving :initialize, :==
+ end
+end
diff --git a/lib/arel/algebra/relations/operations/take.rb b/lib/arel/algebra/relations/operations/take.rb
new file mode 100644
index 0000000000..eb32ec492e
--- /dev/null
+++ b/lib/arel/algebra/relations/operations/take.rb
@@ -0,0 +1,10 @@
+module Arel
+ class Take < Compound
+ attributes :relation, :taken
+ deriving :initialize, :==
+
+ def externalizable?
+ true
+ end
+ end
+end
diff --git a/lib/arel/algebra/relations/operations/where.rb b/lib/arel/algebra/relations/operations/where.rb
new file mode 100644
index 0000000000..608aaeb4b7
--- /dev/null
+++ b/lib/arel/algebra/relations/operations/where.rb
@@ -0,0 +1,16 @@
+module Arel
+ class Where < Compound
+ attributes :relation, :predicate
+ deriving :==
+
+ def initialize(relation, *predicates, &block)
+ predicate = block_given?? yield(relation) : predicates.shift
+ @relation = predicates.empty?? relation : Where.new(relation, *predicates)
+ @predicate = predicate.bind(@relation)
+ end
+
+ def wheres
+ @wheres ||= (relation.wheres + [predicate]).collect { |p| p.bind(self) }
+ end
+ end
+end
diff --git a/lib/arel/algebra/relations/relation.rb b/lib/arel/algebra/relations/relation.rb
new file mode 100644
index 0000000000..9fdac26528
--- /dev/null
+++ b/lib/arel/algebra/relations/relation.rb
@@ -0,0 +1,136 @@
+module Arel
+ class Relation
+ attr_reader :count
+
+ def session
+ Session.new
+ end
+
+ def call
+ engine.read(self)
+ end
+
+ def bind(relation)
+ self
+ end
+
+ module Enumerable
+ include ::Enumerable
+
+ def each(&block)
+ session.read(self).each(&block)
+ end
+
+ def first
+ session.read(self).first
+ end
+ end
+ include Enumerable
+
+ module Operable
+ def join(other_relation = nil, join_class = InnerJoin)
+ case other_relation
+ when String
+ StringJoin.new(self, other_relation)
+ when Relation
+ JoinOperation.new(join_class, self, other_relation)
+ else
+ self
+ end
+ end
+
+ def outer_join(other_relation = nil)
+ join(other_relation, OuterJoin)
+ end
+
+ [:where, :project, :order, :take, :skip, :group].each do |operation_name|
+ class_eval <<-OPERATION, __FILE__, __LINE__
+ def #{operation_name}(*arguments, &block)
+ arguments.all?(&:blank?) && !block_given?? self : #{operation_name.to_s.classify}.new(self, *arguments, &block)
+ end
+ OPERATION
+ end
+
+ def alias
+ Alias.new(self)
+ end
+
+ module Writable
+ def insert(record)
+ session.create Insert.new(self, record)
+ end
+
+ def update(assignments)
+ session.update Update.new(self, assignments)
+ end
+
+ def delete
+ session.delete Deletion.new(self)
+ end
+ end
+ include Writable
+
+ JoinOperation = Struct.new(:join_class, :relation1, :relation2) do
+ def on(*predicates)
+ join_class.new(relation1, relation2, *predicates)
+ end
+ end
+ end
+ include Operable
+
+ module AttributeAccessable
+ def [](index)
+ case index
+ when Symbol, String
+ find_attribute_matching_name(index)
+ when Attribute, Expression
+ find_attribute_matching_attribute(index)
+ when ::Array
+ # TESTME
+ index.collect { |i| self[i] }
+ end
+ end
+
+ def find_attribute_matching_name(name)
+ attributes.detect { |a| a.named?(name) }
+ end
+
+ def find_attribute_matching_attribute(attribute)
+ matching_attributes(attribute).max do |a1, a2|
+ (a1.original_attribute / attribute) <=> (a2.original_attribute / attribute)
+ end
+ end
+
+ def position_of(attribute)
+ (@position_of ||= Hash.new do |h, attribute|
+ h[attribute] = attributes.index(self[attribute])
+ end)[attribute]
+ end
+
+ private
+ def matching_attributes(attribute)
+ (@matching_attributes ||= attributes.inject({}) do |hash, a|
+ (hash[a.root] ||= []) << a
+ hash
+ end)[attribute.root] || []
+ end
+
+ def has_attribute?(attribute)
+ !matching_attributes(attribute).empty?
+ end
+ end
+ include AttributeAccessable
+
+ module DefaultOperations
+ def attributes; [] end
+ def wheres; [] end
+ def orders; [] end
+ def inserts; [] end
+ def groupings; [] end
+ def joins(formatter = nil); nil end # FIXME
+ def taken; nil end
+ def skipped; nil end
+ end
+ include DefaultOperations
+ end
+end
diff --git a/lib/arel/algebra/relations/row.rb b/lib/arel/algebra/relations/row.rb
new file mode 100644
index 0000000000..3158557448
--- /dev/null
+++ b/lib/arel/algebra/relations/row.rb
@@ -0,0 +1,26 @@
+module Arel
+ class Row
+ attributes :relation, :tuple
+ deriving :==, :initialize
+
+ def [](attribute)
+ attribute.type_cast(tuple[relation.position_of(attribute)])
+ end
+
+ def slice(*attributes)
+ Row.new(relation, attributes.inject([]) do |cheese, attribute|
+ # FIXME TESTME method chaining
+ cheese << tuple[relation.relation.position_of(attribute)]
+ cheese
+ end)
+ end
+
+ def bind(relation)
+ Row.new(relation, tuple)
+ end
+
+ def combine(other, relation)
+ Row.new(relation, tuple + other.tuple)
+ end
+ end
+end
diff --git a/lib/arel/algebra/relations/utilities/compound.rb b/lib/arel/algebra/relations/utilities/compound.rb
new file mode 100644
index 0000000000..5e775618f1
--- /dev/null
+++ b/lib/arel/algebra/relations/utilities/compound.rb
@@ -0,0 +1,30 @@
+module Arel
+ class Compound < Relation
+ attr_reader :relation
+ delegate :joins, :join?, :inserts, :taken, :skipped, :name, :externalizable?,
+ :column_for, :engine,
+ :to => :relation
+
+ [:attributes, :wheres, :groupings, :orders].each do |operation_name|
+ class_eval <<-OPERATION, __FILE__, __LINE__
+ def #{operation_name}
+ @#{operation_name} ||= relation.#{operation_name}.collect { |o| o.bind(self) }
+ end
+ OPERATION
+ end
+
+ def hash
+ @hash ||= :relation.hash
+ end
+
+ def eql?(other)
+ self == other
+ end
+
+ private
+
+ def arguments_from_block(relation, &block)
+ block_given?? [yield(relation)] : []
+ end
+ end
+end
diff --git a/lib/arel/algebra/relations/utilities/externalization.rb b/lib/arel/algebra/relations/utilities/externalization.rb
new file mode 100644
index 0000000000..13758ccec9
--- /dev/null
+++ b/lib/arel/algebra/relations/utilities/externalization.rb
@@ -0,0 +1,24 @@
+module Arel
+ class Externalization < Compound
+ attributes :relation
+ deriving :initialize, :==
+
+ def wheres
+ []
+ end
+
+ def attributes
+ @attributes ||= relation.attributes.collect { |a| a.to_attribute(self) }
+ end
+ end
+
+ class Relation
+ def externalize
+ @externalized ||= externalizable?? Externalization.new(self) : self
+ end
+
+ def externalizable?
+ false
+ end
+ end
+end
diff --git a/lib/arel/algebra/relations/utilities/nil.rb b/lib/arel/algebra/relations/utilities/nil.rb
new file mode 100644
index 0000000000..6a9d678c45
--- /dev/null
+++ b/lib/arel/algebra/relations/utilities/nil.rb
@@ -0,0 +1,7 @@
+require 'singleton'
+
+module Arel
+ class Nil < Relation
+ include Singleton
+ end
+end
diff --git a/lib/arel/algebra/relations/writes.rb b/lib/arel/algebra/relations/writes.rb
new file mode 100644
index 0000000000..d344987915
--- /dev/null
+++ b/lib/arel/algebra/relations/writes.rb
@@ -0,0 +1,36 @@
+module Arel
+ class Deletion < Compound
+ attributes :relation
+ deriving :initialize, :==
+
+ def call
+ engine.delete(self)
+ end
+ end
+
+ class Insert < Compound
+ attributes :relation, :record
+ deriving :==
+
+ def initialize(relation, record)
+ @relation, @record = relation, record.bind(relation)
+ end
+
+ def call
+ engine.create(self)
+ end
+ end
+
+ class Update < Compound
+ attributes :relation, :assignments
+ deriving :==
+
+ def initialize(relation, assignments)
+ @relation, @assignments = relation, assignments.bind(relation)
+ end
+
+ def call
+ engine.update(self)
+ end
+ end
+end
diff --git a/lib/arel/engines.rb b/lib/arel/engines.rb
new file mode 100644
index 0000000000..cd848d83e2
--- /dev/null
+++ b/lib/arel/engines.rb
@@ -0,0 +1,2 @@
+require 'arel/engines/sql'
+require 'arel/engines/memory'
diff --git a/lib/arel/engines/memory.rb b/lib/arel/engines/memory.rb
new file mode 100644
index 0000000000..9e7193ef13
--- /dev/null
+++ b/lib/arel/engines/memory.rb
@@ -0,0 +1,4 @@
+require 'arel/engines/memory/relations'
+require 'arel/engines/memory/primitives'
+require 'arel/engines/memory/engine'
+require 'arel/engines/memory/predicates'
diff --git a/lib/arel/engines/memory/engine.rb b/lib/arel/engines/memory/engine.rb
new file mode 100644
index 0000000000..c7ac9422d4
--- /dev/null
+++ b/lib/arel/engines/memory/engine.rb
@@ -0,0 +1,16 @@
+module Arel
+ module Memory
+ class Engine
+ module CRUD
+ def read(relation)
+ relation.eval
+ end
+
+ def create(relation)
+ relation.eval
+ end
+ end
+ include CRUD
+ end
+ end
+end
diff --git a/lib/arel/engines/memory/predicates.rb b/lib/arel/engines/memory/predicates.rb
new file mode 100644
index 0000000000..03d4f25b0a
--- /dev/null
+++ b/lib/arel/engines/memory/predicates.rb
@@ -0,0 +1,35 @@
+module Arel
+ class Binary < Predicate
+ def eval(row)
+ operand1.eval(row).send(operator, operand2.eval(row))
+ end
+ end
+
+ class Equality < Binary
+ def operator; :== end
+ end
+
+ class GreaterThanOrEqualTo < Binary
+ def operator; :>= end
+ end
+
+ class GreaterThan < Binary
+ def operator; :> end
+ end
+
+ class LessThanOrEqualTo < Binary
+ def operator; :<= end
+ end
+
+ class LessThan < Binary
+ def operator; :< end
+ end
+
+ class Match < Binary
+ def operator; :=~ end
+ end
+
+ class In < Binary
+ def operator; :include? end
+ end
+end
diff --git a/lib/arel/engines/memory/primitives.rb b/lib/arel/engines/memory/primitives.rb
new file mode 100644
index 0000000000..935b34f5ee
--- /dev/null
+++ b/lib/arel/engines/memory/primitives.rb
@@ -0,0 +1,27 @@
+module Arel
+ class Attribute
+ def eval(row)
+ row[self]
+ end
+ end
+
+ class Value
+ def eval(row)
+ value
+ end
+ end
+
+ class Ordering
+ def eval(row1, row2)
+ (attribute.eval(row1) <=> attribute.eval(row2)) * direction
+ end
+ end
+
+ class Descending < Ordering
+ def direction; -1 end
+ end
+
+ class Ascending < Ordering
+ def direction; 1 end
+ end
+end
diff --git a/lib/arel/engines/memory/relations.rb b/lib/arel/engines/memory/relations.rb
new file mode 100644
index 0000000000..c67af2d63b
--- /dev/null
+++ b/lib/arel/engines/memory/relations.rb
@@ -0,0 +1,5 @@
+require 'arel/engines/memory/relations/array'
+require 'arel/engines/memory/relations/operations'
+require 'arel/engines/memory/relations/writes'
+require 'arel/engines/memory/relations/compound'
+
diff --git a/lib/arel/engines/memory/relations/array.rb b/lib/arel/engines/memory/relations/array.rb
new file mode 100644
index 0000000000..5e7c0a4ab1
--- /dev/null
+++ b/lib/arel/engines/memory/relations/array.rb
@@ -0,0 +1,25 @@
+module Arel
+ class Array < Relation
+ attributes :array, :attribute_names
+ include Recursion::BaseCase
+ deriving :==, :initialize
+
+ def engine
+ @engine ||= Memory::Engine.new
+ end
+
+ def attributes
+ @attributes ||= @attribute_names.collect do |name|
+ name.to_attribute(self)
+ end
+ end
+
+ def format(attribute, value)
+ value
+ end
+
+ def eval
+ @array.collect { |r| Row.new(self, r) }
+ end
+ end
+end
diff --git a/lib/arel/engines/memory/relations/compound.rb b/lib/arel/engines/memory/relations/compound.rb
new file mode 100644
index 0000000000..6dda92a6a1
--- /dev/null
+++ b/lib/arel/engines/memory/relations/compound.rb
@@ -0,0 +1,9 @@
+module Arel
+ class Compound < Relation
+ delegate :array, :to => :relation
+
+ def unoperated_rows
+ relation.call.collect { |row| row.bind(self) }
+ end
+ end
+end
diff --git a/lib/arel/engines/memory/relations/operations.rb b/lib/arel/engines/memory/relations/operations.rb
new file mode 100644
index 0000000000..8e01938360
--- /dev/null
+++ b/lib/arel/engines/memory/relations/operations.rb
@@ -0,0 +1,61 @@
+module Arel
+ class Where < Compound
+ def eval
+ unoperated_rows.select { |row| predicate.eval(row) }
+ end
+ end
+
+ class Order < Compound
+ def eval
+ unoperated_rows.sort do |row1, row2|
+ ordering = orderings.detect { |o| o.eval(row1, row2) != 0 } || orderings.last
+ ordering.eval(row1, row2)
+ end
+ end
+ end
+
+ class Project < Compound
+ def eval
+ unoperated_rows.collect { |r| r.slice(*projections) }
+ end
+ end
+
+ class Take < Compound
+ def eval
+ unoperated_rows[0, taken]
+ end
+ end
+
+ class Skip < Compound
+ def eval
+ unoperated_rows[skipped..-1]
+ end
+ end
+
+ class Group < Compound
+ def eval
+ raise NotImplementedError
+ end
+ end
+
+ class Alias < Compound
+ def eval
+ unoperated_rows
+ end
+ end
+
+ class Join < Relation
+ def eval
+ result = []
+ relation1.call.each do |row1|
+ relation2.call.each do |row2|
+ combined_row = row1.combine(row2, self)
+ if predicates.all? { |p| p.eval(combined_row) }
+ result << combined_row
+ end
+ end
+ end
+ result
+ end
+ end
+end
diff --git a/lib/arel/engines/memory/relations/writes.rb b/lib/arel/engines/memory/relations/writes.rb
new file mode 100644
index 0000000000..12c4f36c0d
--- /dev/null
+++ b/lib/arel/engines/memory/relations/writes.rb
@@ -0,0 +1,7 @@
+module Arel
+ class Insert < Compound
+ def eval
+ unoperated_rows + [Row.new(self, record.values.collect(&:value))]
+ end
+ end
+end
diff --git a/lib/arel/engines/sql.rb b/lib/arel/engines/sql.rb
new file mode 100644
index 0000000000..f31cfc7dac
--- /dev/null
+++ b/lib/arel/engines/sql.rb
@@ -0,0 +1,7 @@
+require 'arel/engines/sql/engine'
+require 'arel/engines/sql/relations'
+require 'arel/engines/sql/primitives'
+require 'arel/engines/sql/predicates'
+require 'arel/engines/sql/formatters'
+require 'arel/engines/sql/extensions'
+require 'arel/engines/sql/christener'
diff --git a/lib/arel/engines/sql/christener.rb b/lib/arel/engines/sql/christener.rb
new file mode 100644
index 0000000000..c1c9325208
--- /dev/null
+++ b/lib/arel/engines/sql/christener.rb
@@ -0,0 +1,13 @@
+module Arel
+ module Sql
+ class Christener
+ def name_for(relation)
+ @used_names ||= Hash.new(0)
+ (@relation_names ||= Hash.new do |hash, relation|
+ @used_names[name = relation.name] += 1
+ hash[relation] = name + (@used_names[name] > 1 ? "_#{@used_names[name]}" : '')
+ end)[relation.table]
+ end
+ end
+ end
+end
diff --git a/lib/arel/engines/sql/engine.rb b/lib/arel/engines/sql/engine.rb
new file mode 100644
index 0000000000..7d2926040c
--- /dev/null
+++ b/lib/arel/engines/sql/engine.rb
@@ -0,0 +1,37 @@
+module Arel
+ module Sql
+ class Engine
+ def initialize(ar = nil)
+ @ar = ar
+ end
+
+ def connection
+ @ar.connection
+ end
+
+ def method_missing(method, *args, &block)
+ @ar.connection.send(method, *args, &block)
+ end
+
+ module CRUD
+ def create(relation)
+ connection.insert(relation.to_sql)
+ end
+
+ def read(relation)
+ rows = connection.select_rows(relation.to_sql)
+ Array.new(rows, relation.attributes)
+ end
+
+ def update(relation)
+ connection.update(relation.to_sql)
+ end
+
+ def delete(relation)
+ connection.delete(relation.to_sql)
+ end
+ end
+ include CRUD
+ end
+ end
+end
diff --git a/lib/arel/engines/sql/extensions.rb b/lib/arel/engines/sql/extensions.rb
new file mode 100644
index 0000000000..1ea31bc140
--- /dev/null
+++ b/lib/arel/engines/sql/extensions.rb
@@ -0,0 +1,4 @@
+require 'arel/engines/sql/extensions/object'
+require 'arel/engines/sql/extensions/array'
+require 'arel/engines/sql/extensions/range'
+require 'arel/engines/sql/extensions/nil_class'
diff --git a/lib/arel/engines/sql/extensions/array.rb b/lib/arel/engines/sql/extensions/array.rb
new file mode 100644
index 0000000000..7f15179721
--- /dev/null
+++ b/lib/arel/engines/sql/extensions/array.rb
@@ -0,0 +1,16 @@
+module Arel
+ module Sql
+ module ArrayExtensions
+ def to_sql(formatter = nil)
+ "(" + collect { |e| e.to_sql(formatter) }.join(', ') + ")"
+ end
+
+ def inclusion_predicate_sql
+ "IN"
+ end
+
+ Array.send(:include, self)
+ end
+ end
+end
+
diff --git a/lib/arel/engines/sql/extensions/nil_class.rb b/lib/arel/engines/sql/extensions/nil_class.rb
new file mode 100644
index 0000000000..8c335f904a
--- /dev/null
+++ b/lib/arel/engines/sql/extensions/nil_class.rb
@@ -0,0 +1,11 @@
+module Arel
+ module Sql
+ module NilClassExtensions
+ def equality_predicate_sql
+ 'IS'
+ end
+
+ NilClass.send(:include, self)
+ end
+ end
+end
diff --git a/lib/arel/engines/sql/extensions/object.rb b/lib/arel/engines/sql/extensions/object.rb
new file mode 100644
index 0000000000..8fe63dba2f
--- /dev/null
+++ b/lib/arel/engines/sql/extensions/object.rb
@@ -0,0 +1,15 @@
+module Arel
+ module Sql
+ module ObjectExtensions
+ def to_sql(formatter)
+ formatter.scalar self
+ end
+
+ def equality_predicate_sql
+ '='
+ end
+
+ Object.send(:include, self)
+ end
+ end
+end
diff --git a/lib/arel/engines/sql/extensions/range.rb b/lib/arel/engines/sql/extensions/range.rb
new file mode 100644
index 0000000000..25bf1d01da
--- /dev/null
+++ b/lib/arel/engines/sql/extensions/range.rb
@@ -0,0 +1,15 @@
+module Arel
+ module Sql
+ module RangeExtensions
+ def to_sql(formatter = nil)
+ formatter.range self.begin, self.end
+ end
+
+ def inclusion_predicate_sql
+ "BETWEEN"
+ end
+
+ Range.send(:include, self)
+ end
+ end
+end
diff --git a/lib/arel/engines/sql/formatters.rb b/lib/arel/engines/sql/formatters.rb
new file mode 100644
index 0000000000..ae80feb18e
--- /dev/null
+++ b/lib/arel/engines/sql/formatters.rb
@@ -0,0 +1,113 @@
+module Arel
+ module Sql
+ class Formatter
+ attr_reader :environment
+ delegate :christener, :engine, :to => :environment
+ delegate :name_for, :to => :christener
+ delegate :quote_table_name, :quote_column_name, :quote, :to => :engine
+
+ def initialize(environment)
+ @environment = environment
+ end
+ end
+
+ class SelectClause < Formatter
+ def attribute(attribute)
+ # FIXME this should check that the column exists
+ "#{quote_table_name(name_for(attribute.original_relation))}.#{quote_column_name(attribute.name)}" +
+ (attribute.alias ? " AS #{quote(attribute.alias.to_s)}" : "")
+ end
+
+ def expression(expression)
+ if expression.function_sql == "DISTINCT"
+ "#{expression.function_sql} #{expression.attribute.to_sql(self)}" +
+ (expression.alias ? " AS #{quote_column_name(expression.alias)}" : '')
+ else
+ "#{expression.function_sql}(#{expression.attribute.to_sql(self)})" +
+ (expression.alias ? " AS #{quote_column_name(expression.alias)}" : " AS #{expression.function_sql.to_s.downcase}_id")
+ end
+ end
+
+ def select(select_sql, table)
+ "(#{select_sql}) AS #{quote_table_name(name_for(table))}"
+ end
+
+ def value(value)
+ value
+ end
+ end
+
+ class PassThrough < Formatter
+ def value(value)
+ value
+ end
+ end
+
+ class WhereClause < PassThrough
+ end
+
+ class OrderClause < PassThrough
+ def ordering(ordering)
+ "#{quote_table_name(name_for(ordering.attribute.original_relation))}.#{quote_column_name(ordering.attribute.name)} #{ordering.direction_sql}"
+ end
+ end
+
+ class GroupClause < PassThrough
+ def attribute(attribute)
+ "#{quote_table_name(name_for(attribute.original_relation))}.#{quote_column_name(attribute.name)}"
+ end
+ end
+
+ class WhereCondition < Formatter
+ def attribute(attribute)
+ "#{quote_table_name(name_for(attribute.original_relation))}.#{quote_column_name(attribute.name)}"
+ end
+
+ def expression(expression)
+ "#{expression.function_sql}(#{expression.attribute.to_sql(self)})"
+ end
+
+ def value(value)
+ value.to_sql(self)
+ end
+
+ def scalar(value, column = nil)
+ quote(value, column)
+ end
+
+ def select(select_sql, table)
+ "(#{select_sql})"
+ end
+ end
+
+ class SelectStatement < Formatter
+ def select(select_sql, table)
+ select_sql
+ end
+ end
+
+ class TableReference < Formatter
+ def select(select_sql, table)
+ "(#{select_sql}) AS #{quote_table_name(name_for(table))}"
+ end
+
+ def table(table)
+ quote_table_name(table.name) +
+ (table.name != name_for(table) ? " AS " + quote_table_name(name_for(table)) : '')
+ end
+ end
+
+ class Attribute < WhereCondition
+ def scalar(scalar)
+ quote(scalar, environment.column)
+ end
+
+ def range(left, right)
+ "#{left} AND #{right}"
+ end
+ end
+
+ class Value < WhereCondition
+ end
+ end
+end
diff --git a/lib/arel/engines/sql/predicates.rb b/lib/arel/engines/sql/predicates.rb
new file mode 100644
index 0000000000..b84c183c1d
--- /dev/null
+++ b/lib/arel/engines/sql/predicates.rb
@@ -0,0 +1,51 @@
+module Arel
+ class Binary < Predicate
+ def to_sql(formatter = nil)
+ "#{operand1.to_sql} #{predicate_sql} #{operand1.format(operand2)}"
+ end
+ end
+
+ class CompoundPredicate < Binary
+ def to_sql(formatter = nil)
+ "(#{operand1.to_sql(formatter)} #{predicate_sql} #{operand2.to_sql(formatter)})"
+ end
+ end
+
+ class Or < CompoundPredicate
+ def predicate_sql; "OR" end
+ end
+
+ class And < CompoundPredicate
+ def predicate_sql; "AND" end
+ end
+
+ class Equality < Binary
+ def predicate_sql
+ operand2.equality_predicate_sql
+ end
+ end
+
+ class GreaterThanOrEqualTo < Binary
+ def predicate_sql; '>=' end
+ end
+
+ class GreaterThan < Binary
+ def predicate_sql; '>' end
+ end
+
+ class LessThanOrEqualTo < Binary
+ def predicate_sql; '<=' end
+ end
+
+ class LessThan < Binary
+ def predicate_sql; '<' end
+ end
+
+ class Match < Binary
+ def predicate_sql; 'LIKE' end
+ end
+
+ class In < Binary
+ def predicate_sql; operand2.inclusion_predicate_sql end
+ end
+end
diff --git a/lib/arel/engines/sql/primitives.rb b/lib/arel/engines/sql/primitives.rb
new file mode 100644
index 0000000000..bb3bed78e6
--- /dev/null
+++ b/lib/arel/engines/sql/primitives.rb
@@ -0,0 +1,85 @@
+module Arel
+ class SqlLiteral < String
+ def relation
+ nil
+ end
+
+ def to_sql(formatter = nil)
+ self
+ end
+ end
+
+ class Attribute
+ def column
+ original_relation.column_for(self)
+ end
+
+ def type_cast(value)
+ root.relation.format(self, value)
+ end
+
+ def format(object)
+ object.to_sql(Sql::Attribute.new(self))
+ end
+
+ def to_sql(formatter = Sql::WhereCondition.new(relation))
+ formatter.attribute self
+ end
+ end
+
+ class Value
+ delegate :inclusion_predicate_sql, :equality_predicate_sql, :to => :value
+
+ def to_sql(formatter = Sql::WhereCondition.new(relation))
+ formatter.value value
+ end
+
+ def format(object)
+ object.to_sql(Sql::Value.new(relation))
+ end
+ end
+
+ class Ordering
+ def to_sql(formatter = Sql::OrderClause.new(relation))
+ formatter.ordering self
+ end
+ end
+
+ class Ascending < Ordering
+ def direction_sql; 'ASC' end
+ end
+
+ class Descending < Ordering
+ def direction_sql; 'DESC' end
+ end
+
+ class Expression < Attribute
+ def to_sql(formatter = Sql::SelectClause.new(relation))
+ formatter.expression self
+ end
+ end
+
+ class Count < Expression
+ def function_sql; 'COUNT' end
+ end
+
+ class Distinct < Expression
+ def function_sql; 'DISTINCT' end
+ end
+
+ class Sum < Expression
+ def function_sql; 'SUM' end
+ end
+
+ class Maximum < Expression
+ def function_sql; 'MAX' end
+ end
+
+ class Minimum < Expression
+ def function_sql; 'MIN' end
+ end
+
+ class Average < Expression
+ def function_sql; 'AVG' end
+ end
+end
diff --git a/lib/arel/engines/sql/relations.rb b/lib/arel/engines/sql/relations.rb
new file mode 100644
index 0000000000..8360a1f806
--- /dev/null
+++ b/lib/arel/engines/sql/relations.rb
@@ -0,0 +1,9 @@
+require 'arel/engines/sql/relations/utilities/compound'
+require 'arel/engines/sql/relations/utilities/recursion'
+require 'arel/engines/sql/relations/utilities/externalization'
+require 'arel/engines/sql/relations/utilities/nil'
+require 'arel/engines/sql/relations/relation'
+require 'arel/engines/sql/relations/table'
+require 'arel/engines/sql/relations/operations/join'
+require 'arel/engines/sql/relations/operations/alias'
+require 'arel/engines/sql/relations/writes'
diff --git a/lib/arel/engines/sql/relations/operations/alias.rb b/lib/arel/engines/sql/relations/operations/alias.rb
new file mode 100644
index 0000000000..9b6a484463
--- /dev/null
+++ b/lib/arel/engines/sql/relations/operations/alias.rb
@@ -0,0 +1,5 @@
+module Arel
+ class Alias < Compound
+ include Recursion::BaseCase
+ end
+end
diff --git a/lib/arel/engines/sql/relations/operations/join.rb b/lib/arel/engines/sql/relations/operations/join.rb
new file mode 100644
index 0000000000..7c5e13510a
--- /dev/null
+++ b/lib/arel/engines/sql/relations/operations/join.rb
@@ -0,0 +1,33 @@
+module Arel
+ class Join < Relation
+ def table_sql(formatter = Sql::TableReference.new(self))
+ relation1.externalize.table_sql(formatter)
+ end
+
+ def joins(environment, formatter = Sql::TableReference.new(environment))
+ @joins ||= begin
+ this_join = [
+ join_sql,
+ relation2.externalize.table_sql(formatter),
+ ("ON" unless predicates.blank?),
+ (ons + relation2.externalize.wheres).collect { |p| p.bind(environment).to_sql(Sql::WhereClause.new(environment)) }.join(' AND ')
+ ].compact.join(" ")
+ [relation1.joins(environment), this_join, relation2.joins(environment)].compact.join(" ")
+ end
+ end
+ end
+
+ class InnerJoin < Join
+ def join_sql; "INNER JOIN" end
+ end
+
+ class OuterJoin < Join
+ def join_sql; "LEFT OUTER JOIN" end
+ end
+
+ class StringJoin < Join
+ def joins(_, __ = nil)
+ relation2
+ end
+ end
+end
diff --git a/lib/arel/engines/sql/relations/relation.rb b/lib/arel/engines/sql/relations/relation.rb
new file mode 100644
index 0000000000..4cfb83a601
--- /dev/null
+++ b/lib/arel/engines/sql/relations/relation.rb
@@ -0,0 +1,50 @@
+module Arel
+ class Relation
+ def to_sql(formatter = Sql::SelectStatement.new(self))
+ formatter.select select_sql, self
+ end
+
+ def select_sql
+ build_query \
+ "SELECT #{select_clauses.join(', ')}",
+ "FROM #{table_sql(Sql::TableReference.new(self))}",
+ (joins(self) unless joins(self).blank? ),
+ ("WHERE #{where_clauses.join("\n\tAND ")}" unless wheres.blank? ),
+ ("GROUP BY #{group_clauses.join(', ')}" unless groupings.blank? ),
+ ("ORDER BY #{order_clauses.join(', ')}" unless orders.blank? ),
+ ("LIMIT #{taken}" unless taken.blank? ),
+ ("OFFSET #{skipped}" unless skipped.blank? )
+ end
+
+ def inclusion_predicate_sql
+ "IN"
+ end
+
+ def christener
+ @christener ||= Sql::Christener.new
+ end
+
+ protected
+
+ def build_query(*parts)
+ parts.compact.join("\n")
+ end
+
+ def select_clauses
+ attributes.collect { |a| a.to_sql(Sql::SelectClause.new(self)) }
+ end
+
+ def where_clauses
+ wheres.collect { |w| w.to_sql(Sql::WhereClause.new(self)) }
+ end
+
+ def group_clauses
+ groupings.collect { |g| g.to_sql(Sql::GroupClause.new(self)) }
+ end
+
+ def order_clauses
+ orders.collect { |o| o.to_sql(Sql::OrderClause.new(self)) }
+ end
+
+ end
+end
diff --git a/lib/arel/engines/sql/relations/table.rb b/lib/arel/engines/sql/relations/table.rb
new file mode 100644
index 0000000000..dd22f44226
--- /dev/null
+++ b/lib/arel/engines/sql/relations/table.rb
@@ -0,0 +1,52 @@
+module Arel
+ class Table < Relation
+ include Recursion::BaseCase
+
+ cattr_accessor :engine
+ attr_reader :name, :engine
+
+ def initialize(name, engine = Table.engine)
+ @name, @engine = name.to_s, engine
+ end
+
+ def attributes
+ @attributes ||= columns.collect do |column|
+ Attribute.new(self, column.name.to_sym)
+ end
+ end
+
+ def eql?(other)
+ self == other
+ end
+
+ def hash
+ @hash ||= :name.hash
+ end
+
+ def format(attribute, value)
+ attribute.column.type_cast(value)
+ end
+
+ def column_for(attribute)
+ has_attribute?(attribute) and columns.detect { |c| c.name == attribute.name.to_s }
+ end
+
+ def columns
+ @columns ||= engine.columns(name, "#{name} Columns")
+ end
+
+ def reset
+ @attributes = @columns = nil
+ end
+
+ def ==(other)
+ Table === other and
+ name == other.name
+ end
+ end
+end
+
+def Table(name, engine = Arel::Table.engine)
+ Arel::Table.new(name, engine)
+end
+
diff --git a/lib/arel/engines/sql/relations/utilities/compound.rb b/lib/arel/engines/sql/relations/utilities/compound.rb
new file mode 100644
index 0000000000..f8ce4033fd
--- /dev/null
+++ b/lib/arel/engines/sql/relations/utilities/compound.rb
@@ -0,0 +1,10 @@
+module Arel
+ class Compound < Relation
+ delegate :table, :table_sql, :to => :relation
+
+ def build_query(*parts)
+ parts.compact.join("\n")
+ end
+ end
+end
+
diff --git a/lib/arel/engines/sql/relations/utilities/externalization.rb b/lib/arel/engines/sql/relations/utilities/externalization.rb
new file mode 100644
index 0000000000..7f937e8423
--- /dev/null
+++ b/lib/arel/engines/sql/relations/utilities/externalization.rb
@@ -0,0 +1,14 @@
+module Arel
+ class Externalization < Compound
+ include Recursion::BaseCase
+
+ def table_sql(formatter = Sql::TableReference.new(relation))
+ formatter.select relation.select_sql, self
+ end
+
+ # REMOVEME
+ def name
+ relation.name + '_external'
+ end
+ end
+end
diff --git a/lib/arel/engines/sql/relations/utilities/nil.rb b/lib/arel/engines/sql/relations/utilities/nil.rb
new file mode 100644
index 0000000000..519ea8acf1
--- /dev/null
+++ b/lib/arel/engines/sql/relations/utilities/nil.rb
@@ -0,0 +1,6 @@
+module Arel
+ class Nil < Relation
+ def table_sql(formatter = nil); '' end
+ def name; '' end
+ end
+end
diff --git a/lib/arel/engines/sql/relations/utilities/recursion.rb b/lib/arel/engines/sql/relations/utilities/recursion.rb
new file mode 100644
index 0000000000..84a526f57c
--- /dev/null
+++ b/lib/arel/engines/sql/relations/utilities/recursion.rb
@@ -0,0 +1,13 @@
+module Arel
+ module Recursion
+ module BaseCase
+ def table
+ self
+ end
+
+ def table_sql(formatter = Sql::TableReference.new(self))
+ formatter.table self
+ end
+ end
+ end
+end
diff --git a/lib/arel/engines/sql/relations/writes.rb b/lib/arel/engines/sql/relations/writes.rb
new file mode 100644
index 0000000000..f1a9bfd2ac
--- /dev/null
+++ b/lib/arel/engines/sql/relations/writes.rb
@@ -0,0 +1,39 @@
+module Arel
+ class Deletion < Compound
+ def to_sql(formatter = nil)
+ build_query \
+ "DELETE",
+ "FROM #{table_sql}",
+ ("WHERE #{wheres.collect(&:to_sql).join('\n\tAND ')}" unless wheres.blank? ),
+ ("LIMIT #{taken}" unless taken.blank? )
+ end
+ end
+
+ class Insert < Compound
+ def to_sql(formatter = nil)
+ build_query \
+ "INSERT",
+ "INTO #{table_sql}",
+ "(#{record.keys.collect { |key| engine.quote_column_name(key.name) }.join(', ')})",
+ "VALUES (#{record.collect { |key, value| key.format(value) }.join(', ')})"
+ end
+ end
+
+ class Update < Compound
+ def to_sql(formatter = nil)
+ build_query \
+ "UPDATE #{table_sql} SET",
+ assignment_sql,
+ ("WHERE #{wheres.collect(&:to_sql).join('\n\tAND ')}" unless wheres.blank? ),
+ ("LIMIT #{taken}" unless taken.blank? )
+ end
+
+ protected
+
+ def assignment_sql
+ assignments.collect do |attribute, value|
+ "#{engine.quote_column_name(attribute.name)} = #{attribute.format(value)}"
+ end.join(",\n")
+ end
+ end
+end
diff --git a/lib/arel/session.rb b/lib/arel/session.rb
new file mode 100644
index 0000000000..921ad0a7ac
--- /dev/null
+++ b/lib/arel/session.rb
@@ -0,0 +1,48 @@
+module Arel
+ class Session
+ class << self
+ attr_accessor :instance
+ alias_method :manufacture, :new
+
+ def start
+ if @started
+ yield
+ else
+ begin
+ @started = true
+ @instance = manufacture
+ metaclass.send :alias_method, :new, :instance
+ yield
+ ensure
+ metaclass.send :alias_method, :new, :manufacture
+ @started = false
+ end
+ end
+ end
+ end
+
+ module CRUD
+ def create(insert)
+ insert.call
+ insert
+ end
+
+ def read(select)
+ (@read ||= Hash.new do |hash, select|
+ hash[select] = select.call
+ end)[select]
+ end
+
+ def update(update)
+ update.call
+ update
+ end
+
+ def delete(delete)
+ delete.call
+ delete
+ end
+ end
+ include CRUD
+ end
+end
diff --git a/spec/arel/algebra/unit/predicates/binary_spec.rb b/spec/arel/algebra/unit/predicates/binary_spec.rb
new file mode 100644
index 0000000000..14fd7ab21b
--- /dev/null
+++ b/spec/arel/algebra/unit/predicates/binary_spec.rb
@@ -0,0 +1,33 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Binary do
+ before do
+ @relation = Table.new(:users)
+ @attribute1 = @relation[:id]
+ @attribute2 = @relation[:name]
+ class ConcreteBinary < Binary
+ end
+ end
+
+ describe '#bind' do
+ before do
+ @another_relation = @relation.alias
+ end
+
+ describe 'when both operands are attributes' do
+ it "manufactures an expression with the attributes bound to the relation" do
+ ConcreteBinary.new(@attribute1, @attribute2).bind(@another_relation). \
+ should == ConcreteBinary.new(@another_relation[@attribute1], @another_relation[@attribute2])
+ end
+ end
+
+ describe 'when an operand is a value' do
+ it "manufactures an expression with unmodified values" do
+ ConcreteBinary.new(@attribute1, "asdf").bind(@another_relation). \
+ should == ConcreteBinary.new(@attribute1.find_correlate_in(@another_relation), "asdf".find_correlate_in(@another_relation))
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/unit/predicates/equality_spec.rb b/spec/arel/algebra/unit/predicates/equality_spec.rb
index 8a58ba3096..af91f8b51b 100644
--- a/spec/arel/unit/predicates/equality_spec.rb
+++ b/spec/arel/algebra/unit/predicates/equality_spec.rb
@@ -1,4 +1,4 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
module Arel
describe Equality do
@@ -8,42 +8,20 @@ module Arel
@attribute1 = @relation1[:id]
@attribute2 = @relation2[:user_id]
end
-
- describe '==' do
+
+ describe '==' do
it "obtains if attribute1 and attribute2 are identical" do
Equality.new(@attribute1, @attribute2).should == Equality.new(@attribute1, @attribute2)
Equality.new(@attribute1, @attribute2).should_not == Equality.new(@attribute1, @attribute1)
end
-
+
it "obtains if the concrete type of the predicates are identical" do
Equality.new(@attribute1, @attribute2).should_not == Binary.new(@attribute1, @attribute2)
end
-
+
it "is commutative on the attributes" do
Equality.new(@attribute1, @attribute2).should == Equality.new(@attribute2, @attribute1)
end
end
-
- describe '#to_sql' do
- describe 'when relating to a non-nil value' do
- it "manufactures an equality predicate" do
- Equality.new(@attribute1, @attribute2).to_sql.should be_like("
- `users`.`id` = `photos`.`user_id`
- ")
- end
- end
-
- describe 'when relation to a nil value' do
- before do
- @nil = nil
- end
-
- it "manufactures an is null predicate" do
- Equality.new(@attribute1, @nil).to_sql.should be_like("
- `users`.`id` IS NULL
- ")
- end
- end
- end
end
-end \ No newline at end of file
+end
diff --git a/spec/arel/algebra/unit/predicates/in_spec.rb b/spec/arel/algebra/unit/predicates/in_spec.rb
new file mode 100644
index 0000000000..a8a15ce4e3
--- /dev/null
+++ b/spec/arel/algebra/unit/predicates/in_spec.rb
@@ -0,0 +1,10 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe In do
+ before do
+ @relation = Table.new(:users)
+ @attribute = @relation[:id]
+ end
+ end
+end
diff --git a/spec/arel/unit/primitives/attribute_spec.rb b/spec/arel/algebra/unit/primitives/attribute_spec.rb
index b341c6f88e..2ca63ba48e 100644
--- a/spec/arel/unit/primitives/attribute_spec.rb
+++ b/spec/arel/algebra/unit/primitives/attribute_spec.rb
@@ -1,4 +1,4 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
module Arel
describe Attribute do
@@ -6,57 +6,71 @@ module Arel
@relation = Table.new(:users)
@attribute = @relation[:id]
end
-
+
+ describe "#inspect" do
+ it "returns a simple, short inspect string" do
+ @attribute.inspect.should == "<Attribute id>"
+ end
+ end
+
describe Attribute::Transformations do
describe '#as' do
it "manufactures an aliased attributed" do
@attribute.as(:alias).should == Attribute.new(@relation, @attribute.name, :alias => :alias, :ancestor => @attribute)
end
end
-
+
describe '#bind' do
it "manufactures an attribute with the relation bound and self as an ancestor" do
derived_relation = @relation.where(@relation[:id].eq(1))
@attribute.bind(derived_relation).should == Attribute.new(derived_relation, @attribute.name, :ancestor => @attribute)
end
-
+
it "returns self if the substituting to the same relation" do
@attribute.bind(@relation).should == @attribute
end
end
-
+
describe '#to_attribute' do
- it "returns self" do
- @attribute.to_attribute.should == @attribute
+ describe 'when the given relation is the same as the attributes relation' do
+ it "returns self" do
+ @attribute.to_attribute(@relation).should == @attribute
+ end
+ end
+
+ describe 'when the given relation differs from the attributes relation' do
+ it 'binds to the new relation' do
+ @attribute.to_attribute(new_relation = @relation.alias).should == @attribute.bind(new_relation)
+ end
end
end
end
-
+
describe '#column' do
it "returns the corresponding column in the relation" do
@attribute.column.should == @relation.column_for(@attribute)
end
end
-
+
describe '#engine' do
it "delegates to its relation" do
Attribute.new(@relation, :id).engine.should == @relation.engine
end
end
-
+
describe Attribute::Congruence do
describe '/' do
before do
@aliased_relation = @relation.alias
@doubly_aliased_relation = @aliased_relation.alias
end
-
+
describe 'when dividing two unrelated attributes' do
it "returns 0.0" do
(@relation[:id] / @relation[:name]).should == 0.0
end
end
-
+
describe 'when dividing two matching attributes' do
it 'returns a the highest score for the most similar attributes' do
(@aliased_relation[:id] / @relation[:id]) \
@@ -67,97 +81,103 @@ module Arel
end
end
end
-
- describe '#to_sql' do
- describe 'for a simple attribute' do
- it "manufactures sql with an alias" do
- @attribute.to_sql.should be_like("`users`.`id`")
- end
- end
- end
-
+
describe Attribute::Predications do
before do
@attribute = Attribute.new(@relation, :name)
end
-
+
describe '#eq' do
it "manufactures an equality predicate" do
@attribute.eq('name').should == Equality.new(@attribute, 'name')
end
end
-
+
describe '#lt' do
it "manufactures a less-than predicate" do
@attribute.lt(10).should == LessThan.new(@attribute, 10)
end
end
-
+
describe '#lteq' do
it "manufactures a less-than or equal-to predicate" do
@attribute.lteq(10).should == LessThanOrEqualTo.new(@attribute, 10)
end
end
-
+
describe '#gt' do
it "manufactures a greater-than predicate" do
@attribute.gt(10).should == GreaterThan.new(@attribute, 10)
end
end
-
+
describe '#gteq' do
it "manufactures a greater-than or equal-to predicate" do
@attribute.gteq(10).should == GreaterThanOrEqualTo.new(@attribute, 10)
end
end
-
+
describe '#matches' do
it "manufactures a match predicate" do
@attribute.matches(/.*/).should == Match.new(@attribute, /.*/)
end
end
-
+
describe '#in' do
it "manufactures an in predicate" do
@attribute.in(1..30).should == In.new(@attribute, (1..30))
end
end
end
-
+
describe Attribute::Expressions do
before do
- @attribute = Attribute.new(@relation, :name)
+ @attribute = Attribute.new(@relation, :name)
end
-
+
describe '#count' do
it "manufactures a count Expression" do
- @attribute.count.should == Expression.new(@attribute, "COUNT")
+ @attribute.count.should == Count.new(@attribute)
end
end
-
+
describe '#sum' do
it "manufactures a sum Expression" do
- @attribute.sum.should == Expression.new(@attribute, "SUM")
+ @attribute.sum.should == Sum.new(@attribute)
end
end
-
+
describe '#maximum' do
it "manufactures a maximum Expression" do
- @attribute.maximum.should == Expression.new(@attribute, "MAX")
+ @attribute.maximum.should == Maximum.new(@attribute)
end
end
-
+
describe '#minimum' do
it "manufactures a minimum Expression" do
- @attribute.minimum.should == Expression.new(@attribute, "MIN")
+ @attribute.minimum.should == Minimum.new(@attribute)
end
end
-
+
describe '#average' do
it "manufactures an average Expression" do
- @attribute.average.should == Expression.new(@attribute, "AVG")
+ @attribute.average.should == Average.new(@attribute)
+ end
+ end
+ end
+
+ describe Attribute::Orderings do
+ describe '#asc' do
+ it 'manufactures an ascending ordering' do
+ @attribute.asc.should == Ascending.new(@attribute)
end
- end
+ end
+
+ describe '#desc' do
+ it 'manufactures a descending ordering' do
+ @attribute.desc.should == Descending.new(@attribute)
+ end
+ end
end
end
-end \ No newline at end of file
+end
diff --git a/spec/arel/unit/primitives/expression_spec.rb b/spec/arel/algebra/unit/primitives/expression_spec.rb
index 4943f4ef33..768bb492a7 100644
--- a/spec/arel/unit/primitives/expression_spec.rb
+++ b/spec/arel/algebra/unit/primitives/expression_spec.rb
@@ -1,4 +1,4 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
module Arel
describe Expression do
@@ -7,15 +7,21 @@ module Arel
@attribute = @relation[:id]
end
+ describe "#inspect" do
+ it "returns a simple, short inspect string" do
+ @attribute.count.inspect.should == "<Arel::Count <Attribute id>>"
+ end
+ end
+
describe Expression::Transformations do
before do
- @expression = Expression.new(@attribute, "COUNT")
+ @expression = Count.new(@attribute)
end
describe '#bind' do
it "manufactures an attribute with a rebound relation and self as the ancestor" do
derived_relation = @relation.where(@relation[:id].eq(1))
- @expression.bind(derived_relation).should == Expression.new(@attribute.bind(derived_relation), "COUNT", nil, @expression)
+ @expression.bind(derived_relation).should == Count.new(@attribute.bind(derived_relation), nil, @expression)
end
it "returns self if the substituting to the same relation" do
@@ -25,21 +31,15 @@ module Arel
describe '#as' do
it "manufactures an aliased expression" do
- @expression.as(:alias).should == Expression.new(@attribute, "COUNT", :alias, @expression)
+ @expression.as(:alias).should == Expression.new(@attribute, :alias, @expression)
end
end
describe '#to_attribute' do
it "manufactures an attribute with the expression as an ancestor" do
- @expression.to_attribute.should == Attribute.new(@expression.relation, @expression.alias, :ancestor => @expression)
+ @expression.to_attribute(@relation).should == Attribute.new(@relation, @expression.alias, :ancestor => @expression)
end
end
end
-
- describe '#to_sql' do
- it "manufactures sql with the expression and alias" do
- Expression.new(@attribute, "COUNT", :alias).to_sql.should == "COUNT(`users`.`id`) AS `alias`"
- end
- end
end
end
diff --git a/spec/arel/algebra/unit/primitives/value_spec.rb b/spec/arel/algebra/unit/primitives/value_spec.rb
new file mode 100644
index 0000000000..45208e6c5d
--- /dev/null
+++ b/spec/arel/algebra/unit/primitives/value_spec.rb
@@ -0,0 +1,15 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Value do
+ before do
+ @relation = Table.new(:users)
+ end
+
+ describe '#bind' do
+ it "manufactures a new value whose relation is the provided relation" do
+ Value.new(1, @relation).bind(another_relation = Table.new(:photos)).should == Value.new(1, another_relation)
+ end
+ end
+ end
+end
diff --git a/spec/arel/algebra/unit/relations/alias_spec.rb b/spec/arel/algebra/unit/relations/alias_spec.rb
new file mode 100644
index 0000000000..a5d716a638
--- /dev/null
+++ b/spec/arel/algebra/unit/relations/alias_spec.rb
@@ -0,0 +1,16 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Alias do
+ before do
+ @relation = Table.new(:users)
+ end
+
+ describe '==' do
+ it "obtains if the objects are the same" do
+ Alias.new(@relation).should_not == Alias.new(@relation)
+ (aliaz = Alias.new(@relation)).should == aliaz
+ end
+ end
+ end
+end
diff --git a/spec/arel/algebra/unit/relations/delete_spec.rb b/spec/arel/algebra/unit/relations/delete_spec.rb
new file mode 100644
index 0000000000..7578e12a3e
--- /dev/null
+++ b/spec/arel/algebra/unit/relations/delete_spec.rb
@@ -0,0 +1,9 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Deletion do
+ before do
+ @relation = Table.new(:users)
+ end
+ end
+end
diff --git a/spec/arel/algebra/unit/relations/group_spec.rb b/spec/arel/algebra/unit/relations/group_spec.rb
new file mode 100644
index 0000000000..58f9252356
--- /dev/null
+++ b/spec/arel/algebra/unit/relations/group_spec.rb
@@ -0,0 +1,10 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Group do
+ before do
+ @relation = Table.new(:users)
+ @attribute = @relation[:id]
+ end
+ end
+end
diff --git a/spec/arel/algebra/unit/relations/insert_spec.rb b/spec/arel/algebra/unit/relations/insert_spec.rb
new file mode 100644
index 0000000000..feb1a5eae4
--- /dev/null
+++ b/spec/arel/algebra/unit/relations/insert_spec.rb
@@ -0,0 +1,9 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Insert do
+ before do
+ @relation = Table.new(:users)
+ end
+ end
+end
diff --git a/spec/arel/algebra/unit/relations/join_spec.rb b/spec/arel/algebra/unit/relations/join_spec.rb
new file mode 100644
index 0000000000..f5a8bd32aa
--- /dev/null
+++ b/spec/arel/algebra/unit/relations/join_spec.rb
@@ -0,0 +1,26 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Join do
+ before do
+ @relation1 = Table.new(:users)
+ @relation2 = Table.new(:photos)
+ @predicate = @relation1[:id].eq(@relation2[:user_id])
+ end
+
+ describe 'hashing' do
+ it 'implements hash equality' do
+ InnerJoin.new(@relation1, @relation2, @predicate) \
+ .should hash_the_same_as(InnerJoin.new(@relation1, @relation2, @predicate))
+ end
+ end
+
+ describe '#attributes' do
+ it 'combines the attributes of the two relations' do
+ join = InnerJoin.new(@relation1, @relation2, @predicate)
+ join.attributes.should ==
+ (@relation1.attributes + @relation2.attributes).collect { |a| a.bind(join) }
+ end
+ end
+ end
+end
diff --git a/spec/arel/algebra/unit/relations/order_spec.rb b/spec/arel/algebra/unit/relations/order_spec.rb
new file mode 100644
index 0000000000..8b3c932fb9
--- /dev/null
+++ b/spec/arel/algebra/unit/relations/order_spec.rb
@@ -0,0 +1,21 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Order do
+ before do
+ @relation = Table.new(:users)
+ @attribute = @relation[:id]
+ end
+
+ describe "#==" do
+ it "returns true when the Orders are for the same attribute and direction" do
+ Ascending.new(@attribute).should == Ascending.new(@attribute)
+ end
+
+ it "returns false when the Orders are for a diferent direction" do
+ Ascending.new(@attribute).should_not == Descending.new(@attribute)
+ end
+ end
+ end
+end
+
diff --git a/spec/arel/algebra/unit/relations/project_spec.rb b/spec/arel/algebra/unit/relations/project_spec.rb
new file mode 100644
index 0000000000..9f4358ea54
--- /dev/null
+++ b/spec/arel/algebra/unit/relations/project_spec.rb
@@ -0,0 +1,34 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Project do
+ before do
+ @relation = Table.new(:users)
+ @attribute = @relation[:id]
+ end
+
+ describe '#attributes' do
+ before do
+ @projection = Project.new(@relation, @attribute)
+ end
+
+ it "manufactures attributes associated with the projection relation" do
+ @projection.attributes.should == [@attribute].collect { |a| a.bind(@projection) }
+ end
+ end
+
+ describe '#externalizable?' do
+ describe 'when the projections are attributes' do
+ it 'returns false' do
+ Project.new(@relation, @attribute).should_not be_externalizable
+ end
+ end
+
+ describe 'when the projections include an aggregation' do
+ it "obtains" do
+ Project.new(@relation, @attribute.sum).should be_externalizable
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/unit/relations/relation_spec.rb b/spec/arel/algebra/unit/relations/relation_spec.rb
index a3bfa67353..adf82847ac 100644
--- a/spec/arel/unit/relations/relation_spec.rb
+++ b/spec/arel/algebra/unit/relations/relation_spec.rb
@@ -1,4 +1,4 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
module Arel
describe Relation do
@@ -34,13 +34,13 @@ module Arel
describe 'when given a relation' do
it "manufactures an inner join operation between those two relations" do
@relation.join(@relation).on(@predicate). \
- should == Join.new("INNER JOIN", @relation, @relation, @predicate)
+ should == InnerJoin.new(@relation, @relation, @predicate)
end
end
describe "when given a string" do
it "manufactures a join operation with the string passed through" do
- @relation.join(arbitrary_string = "ASDF").should == Join.new(arbitrary_string, @relation)
+ @relation.join(arbitrary_string = "ASDF").should == StringJoin.new(@relation, arbitrary_string)
end
end
@@ -54,7 +54,7 @@ module Arel
describe '#outer_join' do
it "manufactures a left outer join operation between those two relations" do
@relation.outer_join(@relation).on(@predicate). \
- should == Join.new("LEFT OUTER JOIN", @relation, @relation, @predicate)
+ should == OuterJoin.new(@relation, @relation, @predicate)
end
end
end
@@ -147,12 +147,31 @@ module Arel
end
describe Relation::Operable::Writable do
+ describe '#delete' do
+ it 'manufactures a deletion relation' do
+ Session.start do
+ mock(Session.new).delete(Deletion.new(@relation))
+ @relation.delete
+ end
+ end
+ end
+
describe '#insert' do
it 'manufactures an insertion relation' do
Session.start do
- record = {@relation[:name] => 'carl'}
+ record = { @relation[:name] => 'carl' }
mock(Session.new).create(Insert.new(@relation, record))
- @relation.insert(record).should == @relation
+ @relation.insert(record)
+ end
+ end
+ end
+
+ describe '#update' do
+ it 'manufactures an update relation' do
+ Session.start do
+ assignments = { @relation[:name] => Value.new('bob', @relation) }
+ mock(Session.new).update(Update.new(@relation, assignments))
+ @relation.update(assignments)
end
end
end
@@ -161,15 +180,8 @@ module Arel
describe Relation::Enumerable do
it "implements enumerable" do
- @relation.collect.should == @relation.session.read(@relation)
- @relation.first.should == @relation.session.read(@relation).first
- end
- end
-
- describe '#call' do
- it 'executes a select_all on the connection' do
- mock(connection = Object.new).execute(@relation.to_sql) { [] }
- @relation.call(connection)
+ @relation.collect.should == @relation.session.read(@relation).collect
+ @relation.first.should == @relation.session.read(@relation).first
end
end
end
diff --git a/spec/arel/algebra/unit/relations/skip_spec.rb b/spec/arel/algebra/unit/relations/skip_spec.rb
new file mode 100644
index 0000000000..a41913436e
--- /dev/null
+++ b/spec/arel/algebra/unit/relations/skip_spec.rb
@@ -0,0 +1,10 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Skip do
+ before do
+ @relation = Table.new(:users)
+ @skipped = 4
+ end
+ end
+end
diff --git a/spec/arel/algebra/unit/relations/table_spec.rb b/spec/arel/algebra/unit/relations/table_spec.rb
new file mode 100644
index 0000000000..dfe457043c
--- /dev/null
+++ b/spec/arel/algebra/unit/relations/table_spec.rb
@@ -0,0 +1,39 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Table do
+ before do
+ @relation = Table.new(:users)
+ end
+
+ describe '[]' do
+ describe 'when given a', Symbol do
+ it "manufactures an attribute if the symbol names an attribute within the relation" do
+ @relation[:id].should == Attribute.new(@relation, :id)
+ @relation[:does_not_exist].should be_nil
+ end
+ end
+
+ describe 'when given an', Attribute do
+ it "returns the attribute if the attribute is within the relation" do
+ @relation[@relation[:id]].should == @relation[:id]
+ end
+
+ it "returns nil if the attribtue is not within the relation" do
+ another_relation = Table.new(:photos)
+ @relation[another_relation[:id]].should be_nil
+ end
+ end
+
+ describe 'when given an', Expression do
+ before do
+ @expression = @relation[:id].count
+ end
+
+ it "returns the Expression if the Expression is within the relation" do
+ @relation[@expression].should be_nil
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/algebra/unit/relations/take_spec.rb b/spec/arel/algebra/unit/relations/take_spec.rb
new file mode 100644
index 0000000000..2bc17db5a1
--- /dev/null
+++ b/spec/arel/algebra/unit/relations/take_spec.rb
@@ -0,0 +1,10 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Take do
+ before do
+ @relation = Table.new(:users)
+ @taken = 4
+ end
+ end
+end
diff --git a/spec/arel/algebra/unit/relations/update_spec.rb b/spec/arel/algebra/unit/relations/update_spec.rb
new file mode 100644
index 0000000000..e9642ffc99
--- /dev/null
+++ b/spec/arel/algebra/unit/relations/update_spec.rb
@@ -0,0 +1,9 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Update do
+ before do
+ @relation = Table.new(:users)
+ end
+ end
+end
diff --git a/spec/arel/algebra/unit/relations/where_spec.rb b/spec/arel/algebra/unit/relations/where_spec.rb
new file mode 100644
index 0000000000..6c3074a3a5
--- /dev/null
+++ b/spec/arel/algebra/unit/relations/where_spec.rb
@@ -0,0 +1,18 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Where do
+ before do
+ @relation = Table.new(:users)
+ @predicate = @relation[:id].eq(1)
+ end
+
+ describe '#initialize' do
+ it "manufactures nested where relations if multiple predicates are provided" do
+ another_predicate = @relation[:name].lt(2)
+ Where.new(@relation, @predicate, another_predicate). \
+ should == Where.new(Where.new(@relation, another_predicate), @predicate)
+ end
+ end
+ end
+end
diff --git a/spec/arel/unit/session/session_spec.rb b/spec/arel/algebra/unit/session/session_spec.rb
index 6e73d74f2d..ca0a43f278 100644
--- a/spec/arel/unit/session/session_spec.rb
+++ b/spec/arel/algebra/unit/session/session_spec.rb
@@ -1,4 +1,4 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
module Arel
describe Session do
@@ -40,19 +40,19 @@ module Arel
describe '#create' do
it "executes an insertion on the connection" do
- mock(@insert).call(@insert.engine)
+ mock(@insert).call
@session.create(@insert)
end
end
describe '#read' do
it "executes an selection on the connection" do
- mock(@read).call(@read.engine)
+ mock(@read).call
@session.read(@read)
end
it "is memoized" do
- mock(@read).call(@read.engine).once
+ mock(@read).call.once
@session.read(@read)
@session.read(@read)
end
@@ -60,14 +60,14 @@ module Arel
describe '#update' do
it "executes an update on the connection" do
- mock(@update).call(@update.engine)
+ mock(@update).call
@session.update(@update)
end
end
describe '#delete' do
it "executes a delete on the connection" do
- mock(@delete).call(@delete.engine)
+ mock(@delete).call
@session.delete(@delete)
end
end
diff --git a/spec/arel/engines/memory/integration/joins/cross_engine_spec.rb b/spec/arel/engines/memory/integration/joins/cross_engine_spec.rb
new file mode 100644
index 0000000000..bffecc9182
--- /dev/null
+++ b/spec/arel/engines/memory/integration/joins/cross_engine_spec.rb
@@ -0,0 +1,48 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Join do
+ before do
+ @users = Array.new([
+ [1, 'bryan' ],
+ [2, 'emilio' ],
+ [3, 'nick']
+ ], [:id, :name])
+ @photos = Table.new(:photos)
+ @photos.delete
+ @photos \
+ .insert(@photos[:id] => 1, @photos[:user_id] => 1, @photos[:camera_id] => 6) \
+ .insert(@photos[:id] => 2, @photos[:user_id] => 2, @photos[:camera_id] => 42)
+ end
+
+ describe 'when the in memory relation is on the left' do
+ it 'joins across engines' do
+ @users \
+ .join(@photos) \
+ .on(@users[:id].eq(@photos[:user_id])) \
+ .project(@users[:name], @photos[:camera_id]) \
+ .let do |relation|
+ relation.call.should == [
+ Row.new(relation, ['bryan', '6']),
+ Row.new(relation, ['emilio', '42'])
+ ]
+ end
+ end
+ end
+
+ describe 'when the in memory relation is on the right' do
+ it 'joins across engines' do
+ @photos \
+ .join(@users) \
+ .on(@users[:id].eq(@photos[:user_id])) \
+ .project(@users[:name], @photos[:camera_id]) \
+ .let do |relation|
+ relation.call.should == [
+ Row.new(relation, ['bryan', '6']),
+ Row.new(relation, ['emilio', '42'])
+ ]
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/memory/unit/relations/array_spec.rb b/spec/arel/engines/memory/unit/relations/array_spec.rb
new file mode 100644
index 0000000000..dd9da41569
--- /dev/null
+++ b/spec/arel/engines/memory/unit/relations/array_spec.rb
@@ -0,0 +1,32 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Array do
+ before do
+ @relation = Array.new([
+ [1, 'duck' ],
+ [2, 'duck' ],
+ [3, 'goose']
+ ], [:id, :name])
+ end
+
+ describe '#attributes' do
+ it 'manufactures attributes corresponding to the names given on construction' do
+ @relation.attributes.should == [
+ Attribute.new(@relation, :id),
+ Attribute.new(@relation, :name)
+ ]
+ end
+ end
+
+ describe '#call' do
+ it "manufactures an array of hashes of attributes to values" do
+ @relation.call.should == [
+ Row.new(@relation, [1, 'duck']),
+ Row.new(@relation, [2, 'duck']),
+ Row.new(@relation, [3, 'goose'])
+ ]
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/memory/unit/relations/insert_spec.rb b/spec/arel/engines/memory/unit/relations/insert_spec.rb
new file mode 100644
index 0000000000..59e43328a3
--- /dev/null
+++ b/spec/arel/engines/memory/unit/relations/insert_spec.rb
@@ -0,0 +1,28 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Insert do
+ before do
+ @relation = Array.new([
+ [1, 'duck' ],
+ [2, 'duck' ],
+ [3, 'goose']
+ ], [:id, :name])
+ end
+
+ describe '#call' do
+ it "manufactures an array of hashes of attributes to values" do
+ @relation \
+ .insert(@relation[:id] => 4, @relation[:name] => 'guinea fowl') \
+ .let do |relation|
+ relation.call.should == [
+ Row.new(relation, [1, 'duck']),
+ Row.new(relation, [2, 'duck']),
+ Row.new(relation, [3, 'goose']),
+ Row.new(relation, [4, 'guinea fowl'])
+ ]
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/memory/unit/relations/join_spec.rb b/spec/arel/engines/memory/unit/relations/join_spec.rb
new file mode 100644
index 0000000000..110fdb03b7
--- /dev/null
+++ b/spec/arel/engines/memory/unit/relations/join_spec.rb
@@ -0,0 +1,31 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Join do
+ before do
+ @relation1 = Array.new([
+ [1, 'duck' ],
+ [2, 'duck' ],
+ [3, 'goose']
+ ], [:id, :name])
+ @relation2 = @relation1.alias
+ end
+
+ describe InnerJoin do
+ describe '#call' do
+ it 'combines the two tables where the predicate obtains' do
+ @relation1 \
+ .join(@relation2) \
+ .on(@relation1[:id].eq(@relation2[:id])) \
+ .let do |relation|
+ relation.call.should == [
+ Row.new(relation, [1, 'duck', 1, 'duck' ]),
+ Row.new(relation, [2, 'duck', 2, 'duck' ]),
+ Row.new(relation, [3, 'goose', 3, 'goose'])
+ ]
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/memory/unit/relations/order_spec.rb b/spec/arel/engines/memory/unit/relations/order_spec.rb
new file mode 100644
index 0000000000..1e9690bbbf
--- /dev/null
+++ b/spec/arel/engines/memory/unit/relations/order_spec.rb
@@ -0,0 +1,27 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Order do
+ before do
+ @relation = Array.new([
+ [1, 'duck' ],
+ [2, 'duck' ],
+ [3, 'goose']
+ ], [:id, :name])
+ end
+
+ describe '#call' do
+ it 'sorts the relation with the provided ordering' do
+ @relation \
+ .order(@relation[:id].desc) \
+ .let do |relation|
+ relation.call.should == [
+ Row.new(relation, [3, 'goose']),
+ Row.new(relation, [2, 'duck' ]),
+ Row.new(relation, [1, 'duck' ])
+ ]
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/memory/unit/relations/project_spec.rb b/spec/arel/engines/memory/unit/relations/project_spec.rb
new file mode 100644
index 0000000000..1690910026
--- /dev/null
+++ b/spec/arel/engines/memory/unit/relations/project_spec.rb
@@ -0,0 +1,27 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Project do
+ before do
+ @relation = Array.new([
+ [1, 'duck' ],
+ [2, 'duck' ],
+ [3, 'goose']
+ ], [:id, :name])
+ end
+
+ describe '#call' do
+ it 'retains only the attributes that are provided' do
+ @relation \
+ .project(@relation[:id]) \
+ .let do |relation|
+ relation.call.should == [
+ Row.new(relation, [1]),
+ Row.new(relation, [2]),
+ Row.new(relation, [3])
+ ]
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/memory/unit/relations/skip_spec.rb b/spec/arel/engines/memory/unit/relations/skip_spec.rb
new file mode 100644
index 0000000000..3411c5493b
--- /dev/null
+++ b/spec/arel/engines/memory/unit/relations/skip_spec.rb
@@ -0,0 +1,26 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Skip do
+ before do
+ @relation = Array.new([
+ [1, 'duck' ],
+ [2, 'duck' ],
+ [3, 'goose']
+ ], [:id, :name])
+ end
+
+ describe '#call' do
+ it 'removes the first n rows' do
+ @relation \
+ .skip(1) \
+ .let do |relation|
+ relation.call.should == [
+ Row.new(relation, [2, 'duck']),
+ Row.new(relation, [3, 'goose']),
+ ]
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/memory/unit/relations/take_spec.rb b/spec/arel/engines/memory/unit/relations/take_spec.rb
new file mode 100644
index 0000000000..5e7c4fb462
--- /dev/null
+++ b/spec/arel/engines/memory/unit/relations/take_spec.rb
@@ -0,0 +1,26 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Take do
+ before do
+ @relation = Array.new([
+ [1, 'duck' ],
+ [2, 'duck' ],
+ [3, 'goose']
+ ], [:id, :name])
+ end
+
+ describe '#call' do
+ it 'removes the rows after the first n' do
+ @relation \
+ .take(2) \
+ .let do |relation|
+ relation.call.should == [
+ Row.new(relation, [1, 'duck']),
+ Row.new(relation, [2, 'duck']),
+ ]
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/memory/unit/relations/where_spec.rb b/spec/arel/engines/memory/unit/relations/where_spec.rb
new file mode 100644
index 0000000000..1d2c2eb39c
--- /dev/null
+++ b/spec/arel/engines/memory/unit/relations/where_spec.rb
@@ -0,0 +1,39 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Where do
+ before do
+ @relation = Array.new([
+ [1, 'duck' ],
+ [2, 'duck' ],
+ [3, 'goose']
+ ], [:id, :name])
+ end
+
+ describe '#call' do
+ it 'filters the relation with the provided predicate' do
+ @relation \
+ .where(@relation[:id].lt(3)) \
+ .let do |relation|
+ relation.call.should == [
+ Row.new(relation, [1, 'duck']),
+ Row.new(relation, [2, 'duck']),
+ ]
+ end
+ end
+
+ describe 'when filtering a where relation' do
+ it 'further filters the already-filtered relation with the provided predicate' do
+ @relation \
+ .where(@relation[:id].gt(1)) \
+ .where(@relation[:id].lt(3)) \
+ .let do |relation|
+ relation.call.should == [
+ Row.new(relation, [2, 'duck'])
+ ]
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/integration/joins/with_adjacency_spec.rb b/spec/arel/engines/sql/integration/joins/with_adjacency_spec.rb
new file mode 100644
index 0000000000..50b0908441
--- /dev/null
+++ b/spec/arel/engines/sql/integration/joins/with_adjacency_spec.rb
@@ -0,0 +1,209 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Join do
+ before do
+ @relation1 = Table(:users)
+ @relation2 = @relation1.alias
+ @predicate = @relation1[:id].eq(@relation2[:id])
+ end
+
+ describe 'when joining a relation to itself' do
+ describe '#to_sql' do
+ it 'manufactures sql aliasing the table and attributes properly in the join predicate and the where clause' do
+ sql = @relation1.join(@relation2).on(@predicate).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`, `users_2`.`id`, `users_2`.`name`
+ FROM `users`
+ INNER JOIN `users` AS `users_2`
+ ON `users`.`id` = `users_2`.`id`
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name", "users_2"."id", "users_2"."name"
+ FROM "users"
+ INNER JOIN "users" AS "users_2"
+ ON "users"."id" = "users_2"."id"
+ })
+ end
+ end
+
+ describe 'when joining with a where on the same relation' do
+ it 'manufactures sql aliasing the tables properly' do
+ sql = @relation1 \
+ .join(@relation2.where(@relation2[:id].eq(1))) \
+ .on(@predicate) \
+ .to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`, `users_2`.`id`, `users_2`.`name`
+ FROM `users`
+ INNER JOIN `users` AS `users_2`
+ ON `users`.`id` = `users_2`.`id` AND `users_2`.`id` = 1
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name", "users_2"."id", "users_2"."name"
+ FROM "users"
+ INNER JOIN "users" AS "users_2"
+ ON "users"."id" = "users_2"."id" AND "users_2"."id" = 1
+ })
+ end
+ end
+
+ describe 'when the where occurs before the alias' do
+ it 'manufactures sql aliasing the predicates properly' do
+ relation2 = @relation1.where(@relation1[:id].eq(1)).alias
+
+ sql = @relation1 \
+ .join(relation2) \
+ .on(relation2[:id].eq(@relation1[:id])) \
+ .to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`, `users_2`.`id`, `users_2`.`name`
+ FROM `users`
+ INNER JOIN `users` AS `users_2`
+ ON `users_2`.`id` = `users`.`id` AND `users_2`.`id` = 1
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name", "users_2"."id", "users_2"."name"
+ FROM "users"
+ INNER JOIN "users" AS "users_2"
+ ON "users_2"."id" = "users"."id" AND "users_2"."id" = 1
+ })
+ end
+ end
+ end
+ end
+
+ describe 'when joining the relation to itself multiple times' do
+ before do
+ @relation3 = @relation1.alias
+ end
+
+ describe 'when joining left-associatively' do
+ it 'manufactures sql aliasing the tables properly' do
+ sql = @relation1 \
+ .join(@relation2 \
+ .join(@relation3) \
+ .on(@relation2[:id].eq(@relation3[:id]))) \
+ .on(@relation1[:id].eq(@relation2[:id])) \
+ .to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`, `users_2`.`id`, `users_2`.`name`, `users_3`.`id`, `users_3`.`name`
+ FROM `users`
+ INNER JOIN `users` AS `users_2`
+ ON `users`.`id` = `users_2`.`id`
+ INNER JOIN `users` AS `users_3`
+ ON `users_2`.`id` = `users_3`.`id`
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name", "users_2"."id", "users_2"."name", "users_3"."id", "users_3"."name"
+ FROM "users"
+ INNER JOIN "users" AS "users_2"
+ ON "users"."id" = "users_2"."id"
+ INNER JOIN "users" AS "users_3"
+ ON "users_2"."id" = "users_3"."id"
+ })
+ end
+ end
+ end
+
+ describe 'when joining right-associatively' do
+ it 'manufactures sql aliasing the tables properly' do
+ sql = @relation1 \
+ .join(@relation2).on(@relation1[:id].eq(@relation2[:id])) \
+ .join(@relation3).on(@relation2[:id].eq(@relation3[:id])) \
+ .to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`, `users_2`.`id`, `users_2`.`name`, `users_3`.`id`, `users_3`.`name`
+ FROM `users`
+ INNER JOIN `users` AS `users_2`
+ ON `users`.`id` = `users_2`.`id`
+ INNER JOIN `users` AS `users_3`
+ ON `users_2`.`id` = `users_3`.`id`
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name", "users_2"."id", "users_2"."name", "users_3"."id", "users_3"."name"
+ FROM "users"
+ INNER JOIN "users" AS "users_2"
+ ON "users"."id" = "users_2"."id"
+ INNER JOIN "users" AS "users_3"
+ ON "users_2"."id" = "users_3"."id"
+ })
+ end
+ end
+ end
+ end
+ end
+
+ describe '[]' do
+ describe 'when given an attribute belonging to both sub-relations' do
+ it 'disambiguates the relation that serves as the ancestor to the attribute' do
+ @relation1 \
+ .join(@relation2) \
+ .on(@predicate) \
+ .should disambiguate_attributes(@relation1[:id], @relation2[:id])
+ end
+
+ describe 'when both relations are compound and only one is an alias' do
+ it 'disambiguates the relation that serves as the ancestor to the attribute' do
+ compound1 = @relation1.where(@predicate)
+ compound2 = compound1.alias
+ compound1 \
+ .join(compound2) \
+ .on(@predicate) \
+ .should disambiguate_attributes(compound1[:id], compound2[:id])
+ end
+ end
+
+ describe 'when the left relation is extremely compound' do
+ it 'disambiguates the relation that serves as the ancestor to the attribute' do
+ @relation1 \
+ .where(@predicate) \
+ .where(@predicate) \
+ .join(@relation2) \
+ .on(@predicate) \
+ .should disambiguate_attributes(@relation1[:id], @relation2[:id])
+ end
+ end
+
+ describe 'when the right relation is extremely compound' do
+ it 'disambiguates the relation that serves as the ancestor to the attribute' do
+ @relation1 \
+ .join( \
+ @relation2 \
+ .where(@predicate) \
+ .where(@predicate) \
+ .where(@predicate)) \
+ .on(@predicate) \
+ .should disambiguate_attributes(@relation1[:id], @relation2[:id])
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/integration/joins/with_aggregations_spec.rb b/spec/arel/engines/sql/integration/joins/with_aggregations_spec.rb
new file mode 100644
index 0000000000..709ae9f8d1
--- /dev/null
+++ b/spec/arel/engines/sql/integration/joins/with_aggregations_spec.rb
@@ -0,0 +1,167 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Join do
+ before do
+ @relation1 = Table(:users)
+ @relation2 = Table(:photos)
+ @predicate = @relation1[:id].eq(@relation2[:user_id])
+ end
+
+ describe 'when joining aggregated relations' do
+ before do
+ @aggregation = @relation2 \
+ .group(@relation2[:user_id]) \
+ .project(@relation2[:user_id], @relation2[:id].count.as(:cnt)) \
+ end
+
+ describe '#to_sql' do
+ # CLEANUP
+ it '' do
+ sql = @relation1.join(@relation2.take(3)).on(@predicate).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`, `photos_external`.`id`, `photos_external`.`user_id`, `photos_external`.`camera_id`
+ FROM `users`
+ INNER JOIN (SELECT `photos`.`id`, `photos`.`user_id`, `photos`.`camera_id` FROM `photos` LIMIT 3) AS `photos_external`
+ ON `users`.`id` = `photos_external`.`user_id`
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name", "photos_external"."id", "photos_external"."user_id", "photos_external"."camera_id"
+ FROM "users"
+ INNER JOIN (SELECT "photos"."id", "photos"."user_id", "photos"."camera_id" FROM "photos" LIMIT 3) AS "photos_external"
+ ON "users"."id" = "photos_external"."user_id"
+ })
+ end
+ end
+
+ describe 'with the aggregation on the right' do
+ it 'manufactures sql joining the left table to a derived table' do
+ sql = @relation1.join(@aggregation).on(@predicate).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`, `photos_external`.`user_id`, `photos_external`.`cnt`
+ FROM `users`
+ INNER JOIN (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` GROUP BY `photos`.`user_id`) AS `photos_external`
+ ON `users`.`id` = `photos_external`.`user_id`
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name", "photos_external"."user_id", "photos_external"."cnt"
+ FROM "users"
+ INNER JOIN (SELECT "photos"."user_id", COUNT("photos"."id") AS "cnt" FROM "photos" GROUP BY "photos"."user_id") AS "photos_external"
+ ON "users"."id" = "photos_external"."user_id"
+ })
+ end
+ end
+ end
+
+ describe 'with the aggregation on the left' do
+ it 'manufactures sql joining the right table to a derived table' do
+ sql = @aggregation.join(@relation1).on(@predicate).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `photos_external`.`user_id`, `photos_external`.`cnt`, `users`.`id`, `users`.`name`
+ FROM (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` GROUP BY `photos`.`user_id`) AS `photos_external`
+ INNER JOIN `users`
+ ON `users`.`id` = `photos_external`.`user_id`
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "photos_external"."user_id", "photos_external"."cnt", "users"."id", "users"."name"
+ FROM (SELECT "photos"."user_id", COUNT("photos"."id") AS "cnt" FROM "photos" GROUP BY "photos"."user_id") AS "photos_external"
+ INNER JOIN "users"
+ ON "users"."id" = "photos_external"."user_id"
+ })
+ end
+ end
+ end
+
+ describe 'with the aggregation on both sides' do
+ it 'it properly aliases the aggregations' do
+ aggregation2 = @aggregation.alias
+ sql = @aggregation.join(aggregation2).on(aggregation2[:user_id].eq(@aggregation[:user_id])).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `photos_external`.`user_id`, `photos_external`.`cnt`, `photos_external_2`.`user_id`, `photos_external_2`.`cnt`
+ FROM (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` GROUP BY `photos`.`user_id`) AS `photos_external`
+ INNER JOIN (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` GROUP BY `photos`.`user_id`) AS `photos_external_2`
+ ON `photos_external_2`.`user_id` = `photos_external`.`user_id`
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "photos_external"."user_id", "photos_external"."cnt", "photos_external_2"."user_id", "photos_external_2"."cnt"
+ FROM (SELECT "photos"."user_id", COUNT("photos"."id") AS "cnt" FROM "photos" GROUP BY "photos"."user_id") AS "photos_external"
+ INNER JOIN (SELECT "photos"."user_id", COUNT("photos"."id") AS "cnt" FROM "photos" GROUP BY "photos"."user_id") AS "photos_external_2"
+ ON "photos_external_2"."user_id" = "photos_external"."user_id"
+ })
+ end
+ end
+ end
+
+ describe 'when the aggration has a where' do
+ describe 'with the aggregation on the left' do
+ it "manufactures sql keeping wheres on the aggregation within the derived table" do
+ sql = @relation1.join(@aggregation.where(@aggregation[:user_id].eq(1))).on(@predicate).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`, `photos_external`.`user_id`, `photos_external`.`cnt`
+ FROM `users`
+ INNER JOIN (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` WHERE `photos`.`user_id` = 1 GROUP BY `photos`.`user_id`) AS `photos_external`
+ ON `users`.`id` = `photos_external`.`user_id`
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name", "photos_external"."user_id", "photos_external"."cnt"
+ FROM "users"
+ INNER JOIN (SELECT "photos"."user_id", COUNT("photos"."id") AS "cnt" FROM "photos" WHERE "photos"."user_id" = 1 GROUP BY "photos"."user_id") AS "photos_external"
+ ON "users"."id" = "photos_external"."user_id"
+ })
+ end
+ end
+ end
+
+ describe 'with the aggregation on the right' do
+ it "manufactures sql keeping wheres on the aggregation within the derived table" do
+ sql = @aggregation.where(@aggregation[:user_id].eq(1)).join(@relation1).on(@predicate).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `photos_external`.`user_id`, `photos_external`.`cnt`, `users`.`id`, `users`.`name`
+ FROM (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` WHERE `photos`.`user_id` = 1 GROUP BY `photos`.`user_id`) AS `photos_external`
+ INNER JOIN `users`
+ ON `users`.`id` = `photos_external`.`user_id`
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "photos_external"."user_id", "photos_external"."cnt", "users"."id", "users"."name"
+ FROM (SELECT "photos"."user_id", COUNT("photos"."id") AS "cnt" FROM "photos" WHERE "photos"."user_id" = 1 GROUP BY "photos"."user_id") AS "photos_external"
+ INNER JOIN "users"
+ ON "users"."id" = "photos_external"."user_id"
+ })
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/integration/joins/with_compounds_spec.rb b/spec/arel/engines/sql/integration/joins/with_compounds_spec.rb
new file mode 100644
index 0000000000..4bceef4975
--- /dev/null
+++ b/spec/arel/engines/sql/integration/joins/with_compounds_spec.rb
@@ -0,0 +1,107 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Join do
+ before do
+ @relation1 = Table(:users)
+ @relation2 = Table(:photos)
+ @predicate = @relation1[:id].eq(@relation2[:user_id])
+ end
+
+ describe '#to_sql' do
+ describe 'when the join contains a where' do
+ describe 'and the where is given a string' do
+ it 'does not escape the string' do
+ sql = @relation1 \
+ .join(@relation2.where("asdf")) \
+ .on(@predicate) \
+ .to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`, `photos`.`id`, `photos`.`user_id`, `photos`.`camera_id`
+ FROM `users`
+ INNER JOIN `photos`
+ ON `users`.`id` = `photos`.`user_id` AND asdf
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name", "photos"."id", "photos"."user_id", "photos"."camera_id"
+ FROM "users"
+ INNER JOIN "photos"
+ ON "users"."id" = "photos"."user_id" AND asdf
+ })
+ end
+ end
+ end
+ end
+
+ describe 'when a compound contains a join' do
+ describe 'and the compound is a where' do
+ it 'manufactures sql disambiguating the tables' do
+ sql = @relation1 \
+ .where(@relation1[:id].eq(1)) \
+ .join(@relation2) \
+ .on(@predicate) \
+ .where(@relation1[:id].eq(1)) \
+ .to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`, `photos`.`id`, `photos`.`user_id`, `photos`.`camera_id`
+ FROM `users`
+ INNER JOIN `photos`
+ ON `users`.`id` = `photos`.`user_id`
+ WHERE `users`.`id` = 1
+ AND `users`.`id` = 1
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name", "photos"."id", "photos"."user_id", "photos"."camera_id"
+ FROM "users"
+ INNER JOIN "photos"
+ ON "users"."id" = "photos"."user_id"
+ WHERE "users"."id" = 1
+ AND "users"."id" = 1
+ })
+ end
+ end
+ end
+
+ describe 'and the compound is a group' do
+ it 'manufactures sql disambiguating the tables' do
+ sql = @relation1 \
+ .join(@relation2) \
+ .on(@predicate) \
+ .group(@relation1[:id]) \
+ .to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`, `photos`.`id`, `photos`.`user_id`, `photos`.`camera_id`
+ FROM `users`
+ INNER JOIN `photos`
+ ON `users`.`id` = `photos`.`user_id`
+ GROUP BY `users`.`id`
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name", "photos"."id", "photos"."user_id", "photos"."camera_id"
+ FROM "users"
+ INNER JOIN "photos"
+ ON "users"."id" = "photos"."user_id"
+ GROUP BY "users"."id"
+ })
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/unit/engine_spec.rb b/spec/arel/engines/sql/unit/engine_spec.rb
new file mode 100644
index 0000000000..c607abcfa1
--- /dev/null
+++ b/spec/arel/engines/sql/unit/engine_spec.rb
@@ -0,0 +1,45 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Sql::Engine do
+ before do
+ @users = Table.new(:users)
+ @users.delete
+ end
+
+ describe "CRUD" do
+ describe "#create" do
+ it "inserts into the relation" do
+ @users.insert @users[:name] => "Bryan"
+ @users.first[@users[:name]].should == "Bryan"
+ end
+ end
+
+ describe "#read" do
+ it "reads from the relation" do
+ @users.insert @users[:name] => "Bryan"
+
+ @users.each do |row|
+ row[@users[:name]].should == "Bryan"
+ end
+ end
+ end
+
+ describe "#update" do
+ it "updates the relation" do
+ @users.insert @users[:name] => "Nick"
+ @users.update @users[:name] => "Bryan"
+ @users.first[@users[:name]].should == "Bryan"
+ end
+ end
+
+ describe "#delete" do
+ it "deletes from the relation" do
+ @users.insert @users[:name] => "Bryan"
+ @users.delete
+ @users.first.should == nil
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/unit/predicates/binary_spec.rb b/spec/arel/engines/sql/unit/predicates/binary_spec.rb
new file mode 100644
index 0000000000..b1400e2588
--- /dev/null
+++ b/spec/arel/engines/sql/unit/predicates/binary_spec.rb
@@ -0,0 +1,117 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Binary do
+ before do
+ @relation = Table.new(:users)
+ @attribute1 = @relation[:id]
+ @attribute2 = @relation[:name]
+ class ConcreteBinary < Binary
+ def predicate_sql
+ "<=>"
+ end
+ end
+ end
+
+ describe "with compound predicates" do
+ before do
+ @operand1 = ConcreteBinary.new(@attribute1, 1)
+ @operand2 = ConcreteBinary.new(@attribute2, "name")
+ end
+
+ describe Or do
+ describe "#to_sql" do
+ it "manufactures sql with an OR operation" do
+ sql = Or.new(@operand1, @operand2).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{(`users`.`id` <=> 1 OR `users`.`name` <=> 'name')})
+ end
+
+ adapter_is :postgresql do
+ sql.should be_like(%Q{("users"."id" <=> 1 OR "users"."name" <=> E'name')})
+ end
+
+ adapter_is :sqlite3 do
+ sql.should be_like(%Q{("users"."id" <=> 1 OR "users"."name" <=> 'name')})
+ end
+ end
+ end
+ end
+
+ describe And do
+ describe "#to_sql" do
+ it "manufactures sql with an AND operation" do
+ sql = And.new(@operand1, @operand2).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{(`users`.`id` <=> 1 AND `users`.`name` <=> 'name')})
+ end
+
+ adapter_is :sqlite3 do
+ sql.should be_like(%Q{("users"."id" <=> 1 AND "users"."name" <=> 'name')})
+ end
+
+ adapter_is :postgresql do
+ sql.should be_like(%Q{("users"."id" <=> 1 AND "users"."name" <=> E'name')})
+ end
+ end
+ end
+ end
+ end
+
+ describe '#to_sql' do
+ describe 'when relating two attributes' do
+ it 'manufactures sql with a binary operation' do
+ sql = ConcreteBinary.new(@attribute1, @attribute2).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{`users`.`id` <=> `users`.`name`})
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{"users"."id" <=> "users"."name"})
+ end
+ end
+ end
+
+ describe 'when relating an attribute and a value' do
+ before do
+ @value = "1-asdf"
+ end
+
+ describe 'when relating to an integer attribute' do
+ it 'formats values as integers' do
+ sql = ConcreteBinary.new(@attribute1, @value).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{`users`.`id` <=> 1})
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{"users"."id" <=> 1})
+ end
+ end
+ end
+
+ describe 'when relating to a string attribute' do
+ it 'formats values as strings' do
+ sql = ConcreteBinary.new(@attribute2, @value).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{`users`.`name` <=> '1-asdf'})
+ end
+
+ adapter_is :sqlite3 do
+ sql.should be_like(%Q{"users"."name" <=> '1-asdf'})
+ end
+
+ adapter_is :postgresql do
+ sql.should be_like(%Q{"users"."name" <=> E'1-asdf'})
+ end
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/unit/predicates/equality_spec.rb b/spec/arel/engines/sql/unit/predicates/equality_spec.rb
new file mode 100644
index 0000000000..688a6a20be
--- /dev/null
+++ b/spec/arel/engines/sql/unit/predicates/equality_spec.rb
@@ -0,0 +1,46 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Equality do
+ before do
+ @relation1 = Table.new(:users)
+ @relation2 = Table.new(:photos)
+ @attribute1 = @relation1[:id]
+ @attribute2 = @relation2[:user_id]
+ end
+
+ describe '#to_sql' do
+ describe 'when relating to a non-nil value' do
+ it "manufactures an equality predicate" do
+ sql = Equality.new(@attribute1, @attribute2).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{`users`.`id` = `photos`.`user_id`})
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{"users"."id" = "photos"."user_id"})
+ end
+ end
+ end
+
+ describe 'when relation to a nil value' do
+ before do
+ @nil = nil
+ end
+
+ it "manufactures an is null predicate" do
+ sql = Equality.new(@attribute1, @nil).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{`users`.`id` IS NULL})
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{"users"."id" IS NULL})
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/unit/predicates/in_spec.rb b/spec/arel/engines/sql/unit/predicates/in_spec.rb
new file mode 100644
index 0000000000..d3e75cfb84
--- /dev/null
+++ b/spec/arel/engines/sql/unit/predicates/in_spec.rb
@@ -0,0 +1,86 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe In do
+ before do
+ @relation = Table.new(:users)
+ @attribute = @relation[:id]
+ end
+
+ describe '#to_sql' do
+ describe 'when relating to an array' do
+ describe 'when the array\'s elements are the same type as the attribute' do
+ before do
+ @array = [1, 2, 3]
+ end
+
+ it 'manufactures sql with a comma separated list' do
+ sql = In.new(@attribute, @array).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{`users`.`id` IN (1, 2, 3)})
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{"users"."id" IN (1, 2, 3)})
+ end
+ end
+ end
+
+ describe 'when the array\'s elements are not same type as the attribute' do
+ before do
+ @array = ['1-asdf', 2, 3]
+ end
+
+ it 'formats values in the array as the type of the attribute' do
+ sql = In.new(@attribute, @array).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{`users`.`id` IN (1, 2, 3)})
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{"users"."id" IN (1, 2, 3)})
+ end
+ end
+ end
+ end
+
+ describe 'when relating to a range' do
+ before do
+ @range = 1..2
+ end
+
+ it 'manufactures sql with a between' do
+ sql = In.new(@attribute, @range).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{`users`.`id` BETWEEN 1 AND 2})
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{"users"."id" BETWEEN 1 AND 2})
+ end
+ end
+ end
+
+ describe 'when relating to a relation' do
+ it 'manufactures sql with a subselect' do
+ sql = In.new(@attribute, @relation).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ `users`.`id` IN (SELECT `users`.`id`, `users`.`name` FROM `users`)
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ "users"."id" IN (SELECT "users"."id", "users"."name" FROM "users")
+ })
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/unit/predicates/predicates_spec.rb b/spec/arel/engines/sql/unit/predicates/predicates_spec.rb
new file mode 100644
index 0000000000..d55e178e43
--- /dev/null
+++ b/spec/arel/engines/sql/unit/predicates/predicates_spec.rb
@@ -0,0 +1,65 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Predicate do
+ before do
+ @relation = Table.new(:users)
+ @attribute1 = @relation[:id]
+ @attribute2 = @relation[:name]
+ @operand1 = Equality.new(@attribute1, 1)
+ @operand2 = Equality.new(@attribute2, "name")
+ end
+
+ describe "when being combined with another predicate with AND logic" do
+ describe "#to_sql" do
+ it "manufactures sql with an AND operation" do
+ sql = @operand1.and(@operand2).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ (`users`.`id` = 1 AND `users`.`name` = 'name')
+ })
+ end
+
+ adapter_is :sqlite3 do
+ sql.should be_like(%Q{
+ ("users"."id" = 1 AND "users"."name" = 'name')
+ })
+ end
+
+ adapter_is :postgresql do
+ sql.should be_like(%Q{
+ ("users"."id" = 1 AND "users"."name" = E'name')
+ })
+ end
+ end
+ end
+ end
+
+ describe "when being combined with another predicate with OR logic" do
+ describe "#to_sql" do
+ it "manufactures sql with an OR operation" do
+ sql = @operand1.or(@operand2).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ (`users`.`id` = 1 OR `users`.`name` = 'name')
+ })
+ end
+
+ adapter_is :sqlite3 do
+ sql.should be_like(%Q{
+ ("users"."id" = 1 OR "users"."name" = 'name')
+ })
+ end
+
+ adapter_is :postgresql do
+ sql.should be_like(%Q{
+ ("users"."id" = 1 OR "users"."name" = E'name')
+ })
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/unit/primitives/attribute_spec.rb b/spec/arel/engines/sql/unit/primitives/attribute_spec.rb
new file mode 100644
index 0000000000..6cb72f3c19
--- /dev/null
+++ b/spec/arel/engines/sql/unit/primitives/attribute_spec.rb
@@ -0,0 +1,32 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Attribute do
+ before do
+ @relation = Table.new(:users)
+ @attribute = @relation[:id]
+ end
+
+ describe '#column' do
+ it "returns the corresponding column in the relation" do
+ @attribute.column.should == @relation.column_for(@attribute)
+ end
+ end
+
+ describe '#to_sql' do
+ describe 'for a simple attribute' do
+ it "manufactures sql with an alias" do
+ sql = @attribute.to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{`users`.`id`})
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{"users"."id"})
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/unit/primitives/expression_spec.rb b/spec/arel/engines/sql/unit/primitives/expression_spec.rb
new file mode 100644
index 0000000000..ee7f2c1461
--- /dev/null
+++ b/spec/arel/engines/sql/unit/primitives/expression_spec.rb
@@ -0,0 +1,24 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Expression do
+ before do
+ @relation = Table.new(:users)
+ @attribute = @relation[:id]
+ end
+
+ describe '#to_sql' do
+ it "manufactures sql with the expression and alias" do
+ sql = Count.new(@attribute, :alias).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{COUNT(`users`.`id`) AS `alias`})
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{COUNT("users"."id") AS "alias"})
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/unit/primitives/literal_spec.rb b/spec/arel/engines/sql/unit/primitives/literal_spec.rb
new file mode 100644
index 0000000000..c7ff1cf879
--- /dev/null
+++ b/spec/arel/engines/sql/unit/primitives/literal_spec.rb
@@ -0,0 +1,23 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe SqlLiteral do
+ before do
+ @relation = Table.new(:users)
+ end
+
+ describe '#to_sql' do
+ it "manufactures sql with a literal SQL fragment" do
+ sql = @relation.project(Count.new(SqlLiteral.new("*"))).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{SELECT COUNT(*) AS count_id FROM `users`})
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{SELECT COUNT(*) AS count_id FROM "users"})
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/unit/primitives/value_spec.rb b/spec/arel/engines/sql/unit/primitives/value_spec.rb
index ba9a80bb49..ff3533f6ef 100644
--- a/spec/arel/unit/primitives/value_spec.rb
+++ b/spec/arel/engines/sql/unit/primitives/value_spec.rb
@@ -1,4 +1,4 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
module Arel
describe Value do
@@ -9,7 +9,14 @@ module Arel
describe '#to_sql' do
it "appropriately quotes the value" do
Value.new(1, @relation).to_sql.should be_like('1')
- Value.new('asdf', @relation).to_sql.should be_like("'asdf'")
+
+ adapter_is_not :postgresql do
+ Value.new('asdf', @relation).to_sql.should be_like("'asdf'")
+ end
+
+ adapter_is :postgresql do
+ Value.new('asdf', @relation).to_sql.should be_like("E'asdf'")
+ end
end
end
@@ -18,11 +25,5 @@ module Arel
Value.new(1, @relation).format(@relation[:id]).should == @relation[:id].to_sql
end
end
-
- describe '#bind' do
- it "manufactures a new value whose relation is the provided relation" do
- Value.new(1, @relation).bind(another_relation = Table.new(:photos)).should == Value.new(1, another_relation)
- end
- end
end
end
diff --git a/spec/arel/engines/sql/unit/relations/alias_spec.rb b/spec/arel/engines/sql/unit/relations/alias_spec.rb
new file mode 100644
index 0000000000..b67a0bbc89
--- /dev/null
+++ b/spec/arel/engines/sql/unit/relations/alias_spec.rb
@@ -0,0 +1,43 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Alias do
+ before do
+ @relation = Table.new(:users)
+ end
+
+ describe '#to_sql' do
+ describe 'when there is no ambiguity' do
+ it 'does not alias table names anywhere a table name can appear' do
+ sql = @relation \
+ .where(@relation[:id].eq(1)) \
+ .order(@relation[:id]) \
+ .project(@relation[:id]) \
+ .group(@relation[:id]) \
+ .alias \
+ .to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`
+ FROM `users`
+ WHERE `users`.`id` = 1
+ GROUP BY `users`.`id`
+ ORDER BY `users`.`id` ASC
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id"
+ FROM "users"
+ WHERE "users"."id" = 1
+ GROUP BY "users"."id"
+ ORDER BY "users"."id" ASC
+ })
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/unit/relations/delete_spec.rb b/spec/arel/engines/sql/unit/relations/delete_spec.rb
new file mode 100644
index 0000000000..7a5e2b0088
--- /dev/null
+++ b/spec/arel/engines/sql/unit/relations/delete_spec.rb
@@ -0,0 +1,63 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Deletion do
+ before do
+ @relation = Table.new(:users)
+ end
+
+ describe '#to_sql' do
+ it 'manufactures sql deleting a table relation' do
+ sql = Deletion.new(@relation).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{DELETE FROM `users`})
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{DELETE FROM "users"})
+ end
+ end
+
+ it 'manufactures sql deleting a where relation' do
+ sql = Deletion.new(@relation.where(@relation[:id].eq(1))).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ DELETE
+ FROM `users`
+ WHERE `users`.`id` = 1
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ DELETE
+ FROM "users"
+ WHERE "users"."id" = 1
+ })
+ end
+ end
+
+ it "manufactures sql deleting a ranged relation" do
+ sql = Deletion.new(@relation.take(1)).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ DELETE
+ FROM `users`
+ LIMIT 1
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ DELETE
+ FROM "users"
+ LIMIT 1
+ })
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/unit/relations/group_spec.rb b/spec/arel/engines/sql/unit/relations/group_spec.rb
new file mode 100644
index 0000000000..5e0c675c8b
--- /dev/null
+++ b/spec/arel/engines/sql/unit/relations/group_spec.rb
@@ -0,0 +1,56 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Group do
+ before do
+ @relation = Table.new(:users)
+ @attribute = @relation[:id]
+ end
+
+ describe '#to_sql' do
+ describe 'when given a predicate' do
+ it "manufactures sql with where clause conditions" do
+ sql = Group.new(@relation, @attribute).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`
+ FROM `users`
+ GROUP BY `users`.`id`
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name"
+ FROM "users"
+ GROUP BY "users"."id"
+ })
+ end
+ end
+ end
+
+ describe 'when given a string' do
+ it "passes the string through to the where clause" do
+ sql = Group.new(@relation, 'asdf').to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`
+ FROM `users`
+ GROUP BY asdf
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name"
+ FROM "users"
+ GROUP BY asdf
+ })
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/unit/relations/insert_spec.rb b/spec/arel/engines/sql/unit/relations/insert_spec.rb
new file mode 100644
index 0000000000..29a5e0bf42
--- /dev/null
+++ b/spec/arel/engines/sql/unit/relations/insert_spec.rb
@@ -0,0 +1,107 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Insert do
+ before do
+ @relation = Table.new(:users)
+ end
+
+ describe '#to_sql' do
+ it 'manufactures sql inserting data when given multiple rows' do
+ pending 'it should insert multiple rows' do
+ @insertion = Insert.new(@relation, [@relation[:name] => "nick", @relation[:name] => "bryan"])
+
+ @insertion.to_sql.should be_like("
+ INSERT
+ INTO `users`
+ (`name`) VALUES ('nick'), ('bryan')
+ ")
+ end
+ end
+
+ it 'manufactures sql inserting data when given multiple values' do
+ @insertion = Insert.new(@relation, @relation[:id] => "1", @relation[:name] => "nick")
+
+ adapter_is :mysql do
+ @insertion.to_sql.should be_like(%Q{
+ INSERT
+ INTO `users`
+ (`id`, `name`) VALUES (1, 'nick')
+ })
+ end
+
+ adapter_is :sqlite3 do
+ @insertion.to_sql.should be_like(%Q{
+ INSERT
+ INTO "users"
+ ("id", "name") VALUES (1, 'nick')
+ })
+ end
+
+ adapter_is :postgresql do
+ @insertion.to_sql.should be_like(%Q{
+ INSERT
+ INTO "users"
+ ("id", "name") VALUES (1, E'nick')
+ })
+ end
+ end
+
+ describe 'when given values whose types correspond to the types of the attributes' do
+ before do
+ @insertion = Insert.new(@relation, @relation[:name] => "nick")
+ end
+
+ it 'manufactures sql inserting data' do
+ adapter_is :mysql do
+ @insertion.to_sql.should be_like(%Q{
+ INSERT
+ INTO `users`
+ (`name`) VALUES ('nick')
+ })
+ end
+
+ adapter_is :sqlite3 do
+ @insertion.to_sql.should be_like(%Q{
+ INSERT
+ INTO "users"
+ ("name") VALUES ('nick')
+ })
+ end
+
+ adapter_is :postgresql do
+ @insertion.to_sql.should be_like(%Q{
+ INSERT
+ INTO "users"
+ ("name") VALUES (E'nick')
+ })
+ end
+ end
+ end
+
+ describe 'when given values whose types differ from from the types of the attributes' do
+ before do
+ @insertion = Insert.new(@relation, @relation[:id] => '1-asdf')
+ end
+
+ it 'manufactures sql inserting data' do
+ adapter_is :mysql do
+ @insertion.to_sql.should be_like(%Q{
+ INSERT
+ INTO `users`
+ (`id`) VALUES (1)
+ })
+ end
+
+ adapter_is_not :mysql do
+ @insertion.to_sql.should be_like(%Q{
+ INSERT
+ INTO "users"
+ ("id") VALUES (1)
+ })
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/unit/relations/join_spec.rb b/spec/arel/engines/sql/unit/relations/join_spec.rb
new file mode 100644
index 0000000000..f904b61870
--- /dev/null
+++ b/spec/arel/engines/sql/unit/relations/join_spec.rb
@@ -0,0 +1,57 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Join do
+ before do
+ @relation1 = Table.new(:users)
+ @relation2 = Table.new(:photos)
+ @predicate = @relation1[:id].eq(@relation2[:user_id])
+ end
+
+ describe '#to_sql' do
+ describe 'when joining with another relation' do
+ it 'manufactures sql joining the two tables on the predicate' do
+ sql = InnerJoin.new(@relation1, @relation2, @predicate).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`, `photos`.`id`, `photos`.`user_id`, `photos`.`camera_id`
+ FROM `users`
+ INNER JOIN `photos` ON `users`.`id` = `photos`.`user_id`
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name", "photos"."id", "photos"."user_id", "photos"."camera_id"
+ FROM "users"
+ INNER JOIN "photos" ON "users"."id" = "photos"."user_id"
+ })
+ end
+ end
+ end
+
+ describe 'when joining with a string' do
+ it "passes the string through to the where clause" do
+ sql = StringJoin.new(@relation1, "INNER JOIN asdf ON fdsa").to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`
+ FROM `users`
+ INNER JOIN asdf ON fdsa
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name"
+ FROM "users"
+ INNER JOIN asdf ON fdsa
+ })
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/unit/relations/order_spec.rb b/spec/arel/engines/sql/unit/relations/order_spec.rb
new file mode 100644
index 0000000000..ce97a4dd5e
--- /dev/null
+++ b/spec/arel/engines/sql/unit/relations/order_spec.rb
@@ -0,0 +1,113 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Order do
+ before do
+ @relation = Table.new(:users)
+ @attribute = @relation[:id]
+ end
+
+ describe '#to_sql' do
+ describe "when given an attribute" do
+ it "manufactures sql with an order clause populated by the attribute" do
+ sql = Order.new(@relation, @attribute).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`
+ FROM `users`
+ ORDER BY `users`.`id` ASC
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name"
+ FROM "users"
+ ORDER BY "users"."id" ASC
+ })
+ end
+ end
+ end
+
+ describe "when given multiple attributes" do
+ before do
+ @another_attribute = @relation[:name]
+ end
+
+ it "manufactures sql with an order clause populated by comma-separated attributes" do
+ sql = Order.new(@relation, @attribute, @another_attribute).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`
+ FROM `users`
+ ORDER BY `users`.`id` ASC, `users`.`name` ASC
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name"
+ FROM "users"
+ ORDER BY "users"."id" ASC, "users"."name" ASC
+ })
+ end
+ end
+ end
+
+ describe "when given a string" do
+ before do
+ @string = "asdf"
+ end
+
+ it "passes the string through to the order clause" do
+ sql = Order.new(@relation, @string).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`
+ FROM `users`
+ ORDER BY asdf
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name"
+ FROM "users"
+ ORDER BY asdf
+ })
+ end
+ end
+ end
+
+ describe "when ordering an ordered relation" do
+ before do
+ @ordered_relation = Order.new(@relation, @attribute)
+ @another_attribute = @relation[:name]
+ end
+
+ it "manufactures sql with the order clause of the last ordering preceding the first ordering" do
+ sql = Order.new(@ordered_relation, @another_attribute).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`
+ FROM `users`
+ ORDER BY `users`.`name` ASC, `users`.`id` ASC
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name"
+ FROM "users"
+ ORDER BY "users"."name" ASC, "users"."id" ASC
+ })
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/unit/relations/project_spec.rb b/spec/arel/engines/sql/unit/relations/project_spec.rb
new file mode 100644
index 0000000000..5e29124cfa
--- /dev/null
+++ b/spec/arel/engines/sql/unit/relations/project_spec.rb
@@ -0,0 +1,110 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Project do
+ before do
+ @relation = Table.new(:users)
+ @attribute = @relation[:id]
+ end
+
+ describe '#to_sql' do
+ describe 'when given an attribute' do
+ it "manufactures sql with a limited select clause" do
+ sql = Project.new(@relation, @attribute).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`
+ FROM `users`
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id"
+ FROM "users"
+ })
+ end
+ end
+ end
+
+ describe 'when given a relation' do
+ before do
+ @scalar_relation = Project.new(@relation, @relation[:name])
+ end
+
+ it "manufactures sql with scalar selects" do
+ sql = Project.new(@relation, @scalar_relation).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT (SELECT `users`.`name` FROM `users`) AS `users` FROM `users`
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT (SELECT "users"."name" FROM "users") AS "users" FROM "users"
+ })
+ end
+ end
+ end
+
+ describe 'when given a string' do
+ it "passes the string through to the select clause" do
+ sql = Project.new(@relation, 'asdf').to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT asdf FROM `users`
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT asdf FROM "users"
+ })
+ end
+ end
+ end
+
+ describe 'when given an expression' do
+ it 'manufactures sql with expressions' do
+ sql = @relation.project(@attribute.count).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT COUNT(`users`.`id`) AS count_id
+ FROM `users`
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT COUNT("users"."id") AS count_id
+ FROM "users"
+ })
+ end
+ end
+
+ it 'manufactures sql with distinct expressions' do
+ sql = @relation.project(@attribute.count(true)).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT COUNT(DISTINCT `users`.`id`) AS count_id
+ FROM `users`
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT COUNT(DISTINCT "users"."id") AS count_id
+ FROM "users"
+ })
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/unit/relations/skip_spec.rb b/spec/arel/engines/sql/unit/relations/skip_spec.rb
new file mode 100644
index 0000000000..c14bd1ce95
--- /dev/null
+++ b/spec/arel/engines/sql/unit/relations/skip_spec.rb
@@ -0,0 +1,32 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Skip do
+ before do
+ @relation = Table.new(:users)
+ @skipped = 4
+ end
+
+ describe '#to_sql' do
+ it "manufactures sql with limit and offset" do
+ sql = Skip.new(@relation, @skipped).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`
+ FROM `users`
+ OFFSET 4
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name"
+ FROM "users"
+ OFFSET 4
+ })
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/unit/relations/table_spec.rb b/spec/arel/engines/sql/unit/relations/table_spec.rb
new file mode 100644
index 0000000000..9797b38822
--- /dev/null
+++ b/spec/arel/engines/sql/unit/relations/table_spec.rb
@@ -0,0 +1,69 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Table do
+ before do
+ @relation = Table.new(:users)
+ end
+
+ describe '#to_sql' do
+ it "manufactures a simple select query" do
+ sql = @relation.to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`
+ FROM `users`
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name"
+ FROM "users"
+ })
+ end
+ end
+ end
+
+ describe '#column_for' do
+ it "returns the column corresponding to the attribute" do
+ @relation.column_for(@relation[:id]).should == @relation.columns.detect { |c| c.name == 'id' }
+ end
+ end
+
+ describe '#attributes' do
+ it 'manufactures attributes corresponding to columns in the table' do
+ @relation.attributes.should == [
+ Attribute.new(@relation, :id),
+ Attribute.new(@relation, :name)
+ ]
+ end
+
+ describe '#reset' do
+ it "reloads columns from the database" do
+ lambda { stub(@relation.engine).columns { [] } }.should_not change { @relation.attributes }
+ lambda { @relation.reset }.should change { @relation.attributes }
+ end
+ end
+ end
+
+ describe 'hashing' do
+ it "implements hash equality" do
+ Table.new(:users).should hash_the_same_as(Table.new(:users))
+ Table.new(:users).should_not hash_the_same_as(Table.new(:photos))
+ end
+ end
+
+ describe '#engine' do
+ it "defaults to global engine" do
+ Table.engine = engine = Sql::Engine.new
+ Table.new(:users).engine.should == engine
+ end
+
+ it "can be specified" do
+ Table.new(:users, engine = Sql::Engine.new).engine.should == engine
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/unit/relations/take_spec.rb b/spec/arel/engines/sql/unit/relations/take_spec.rb
new file mode 100644
index 0000000000..8f1240fc17
--- /dev/null
+++ b/spec/arel/engines/sql/unit/relations/take_spec.rb
@@ -0,0 +1,32 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Take do
+ before do
+ @relation = Table.new(:users)
+ @taken = 4
+ end
+
+ describe '#to_sql' do
+ it "manufactures sql with limit and offset" do
+ sql = Take.new(@relation, @taken).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`
+ FROM `users`
+ LIMIT 4
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name"
+ FROM "users"
+ LIMIT 4
+ })
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/engines/sql/unit/relations/update_spec.rb b/spec/arel/engines/sql/unit/relations/update_spec.rb
new file mode 100644
index 0000000000..4d728eb241
--- /dev/null
+++ b/spec/arel/engines/sql/unit/relations/update_spec.rb
@@ -0,0 +1,151 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Update do
+ before do
+ @relation = Table.new(:users)
+ end
+
+ describe '#to_sql' do
+ it "manufactures sql updating attributes when given multiple attributes" do
+ sql = Update.new(@relation, @relation[:id] => 1, @relation[:name] => "nick").to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ UPDATE `users`
+ SET `id` = 1, `name` = 'nick'
+ })
+ end
+
+ adapter_is :sqlite3 do
+ sql.should be_like(%Q{
+ UPDATE "users"
+ SET "id" = 1, "name" = 'nick'
+ })
+ end
+
+ adapter_is :postgresql do
+ sql.should be_like(%Q{
+ UPDATE "users"
+ SET "id" = 1, "name" = E'nick'
+ })
+ end
+ end
+
+ it "manufactures sql updating attributes when given a ranged relation" do
+ sql = Update.new(@relation.take(1), @relation[:name] => "nick").to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ UPDATE `users`
+ SET `name` = 'nick'
+ LIMIT 1
+ })
+ end
+
+ adapter_is :sqlite3 do
+ sql.should be_like(%Q{
+ UPDATE "users"
+ SET "name" = 'nick'
+ LIMIT 1
+ })
+ end
+
+ adapter_is :postgresql do
+ sql.should be_like(%Q{
+ UPDATE "users"
+ SET "name" = E'nick'
+ LIMIT 1
+ })
+ end
+ end
+
+ describe 'when given values whose types correspond to the types of the attributes' do
+ before do
+ @update = Update.new(@relation, @relation[:name] => "nick")
+ end
+
+ it 'manufactures sql updating attributes' do
+ adapter_is :mysql do
+ @update.to_sql.should be_like(%Q{
+ UPDATE `users`
+ SET `name` = 'nick'
+ })
+ end
+
+ adapter_is :sqlite3 do
+ @update.to_sql.should be_like(%Q{
+ UPDATE "users"
+ SET "name" = 'nick'
+ })
+ end
+
+ adapter_is :postgresql do
+ @update.to_sql.should be_like(%Q{
+ UPDATE "users"
+ SET "name" = E'nick'
+ })
+ end
+ end
+ end
+
+ describe 'when given values whose types differ from from the types of the attributes' do
+ before do
+ @update = Update.new(@relation, @relation[:id] => '1-asdf')
+ end
+
+ it 'manufactures sql updating attributes' do
+ adapter_is :mysql do
+ @update.to_sql.should be_like(%Q{
+ UPDATE `users`
+ SET `id` = 1
+ })
+ end
+
+ adapter_is_not :mysql do
+ @update.to_sql.should be_like(%Q{
+ UPDATE "users"
+ SET "id" = 1
+ })
+ end
+ end
+ end
+
+ describe 'when the relation is a where' do
+ before do
+ @update = Update.new(
+ @relation.where(@relation[:id].eq(1)),
+ @relation[:name] => "nick"
+ )
+ end
+
+ it 'manufactures sql updating a where relation' do
+ adapter_is :mysql do
+ @update.to_sql.should be_like(%Q{
+ UPDATE `users`
+ SET `name` = 'nick'
+ WHERE `users`.`id` = 1
+ })
+ end
+
+ adapter_is :sqlite3 do
+ @update.to_sql.should be_like(%Q{
+ UPDATE "users"
+ SET "name" = 'nick'
+ WHERE "users"."id" = 1
+ })
+ end
+
+ adapter_is :postgresql do
+ @update.to_sql.should be_like(%Q{
+ UPDATE "users"
+ SET "name" = E'nick'
+ WHERE "users"."id" = 1
+ })
+ end
+ end
+ end
+ end
+
+ end
+end
diff --git a/spec/arel/engines/sql/unit/relations/where_spec.rb b/spec/arel/engines/sql/unit/relations/where_spec.rb
new file mode 100644
index 0000000000..4f0cce1e01
--- /dev/null
+++ b/spec/arel/engines/sql/unit/relations/where_spec.rb
@@ -0,0 +1,56 @@
+require File.join(File.dirname(__FILE__), '..', '..', '..', '..', '..', 'spec_helper')
+
+module Arel
+ describe Where do
+ before do
+ @relation = Table.new(:users)
+ @predicate = @relation[:id].eq(1)
+ end
+
+ describe '#to_sql' do
+ describe 'when given a predicate' do
+ it "manufactures sql with where clause conditions" do
+ sql = Where.new(@relation, @predicate).to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`
+ FROM `users`
+ WHERE `users`.`id` = 1
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name"
+ FROM "users"
+ WHERE "users"."id" = 1
+ })
+ end
+ end
+ end
+
+ describe 'when given a string' do
+ it "passes the string through to the where clause" do
+ sql = Where.new(@relation, 'asdf').to_sql
+
+ adapter_is :mysql do
+ sql.should be_like(%Q{
+ SELECT `users`.`id`, `users`.`name`
+ FROM `users`
+ WHERE asdf
+ })
+ end
+
+ adapter_is_not :mysql do
+ sql.should be_like(%Q{
+ SELECT "users"."id", "users"."name"
+ FROM "users"
+ WHERE asdf
+ })
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/arel/integration/joins/with_adjacency_spec.rb b/spec/arel/integration/joins/with_adjacency_spec.rb
deleted file mode 100644
index 559b5bbe1a..0000000000
--- a/spec/arel/integration/joins/with_adjacency_spec.rb
+++ /dev/null
@@ -1,139 +0,0 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
-
-module Arel
- describe Join do
- before do
- @relation1 = Arel(:users)
- @relation2 = @relation1.alias
- @predicate = @relation1[:id].eq(@relation2[:id])
- end
-
- describe 'when joining a relation to itself' do
- describe '#to_sql' do
- it 'manufactures sql aliasing the table and attributes properly in the join predicate and the where clause' do
- @relation1.join(@relation2).on(@predicate).to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`, `users_2`.`id`, `users_2`.`name`
- FROM `users`
- INNER JOIN `users` AS `users_2`
- ON `users`.`id` = `users_2`.`id`
- ")
- end
-
- describe 'when joining with a where on the same relation' do
- it 'manufactures sql aliasing the tables properly' do
- @relation1 \
- .join(@relation2.where(@relation2[:id].eq(1))) \
- .on(@predicate) \
- .to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`, `users_2`.`id`, `users_2`.`name`
- FROM `users`
- INNER JOIN `users` AS `users_2`
- ON `users`.`id` = `users_2`.`id` AND `users_2`.`id` = 1
- ")
- end
-
- describe 'when the where occurs before the alias' do
- it 'manufactures sql aliasing the predicates properly' do
- relation2 = @relation1.where(@relation1[:id].eq(1)).alias
- @relation1 \
- .join(relation2) \
- .on(relation2[:id].eq(@relation1[:id])) \
- .to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`, `users_2`.`id`, `users_2`.`name`
- FROM `users`
- INNER JOIN `users` AS `users_2`
- ON `users_2`.`id` = `users`.`id` AND `users_2`.`id` = 1
- ")
- end
- end
- end
-
- describe 'when joining the relation to itself multiple times' do
- before do
- @relation3 = @relation1.alias
- end
-
- describe 'when joining left-associatively' do
- it 'manufactures sql aliasing the tables properly' do
- @relation1 \
- .join(@relation2 \
- .join(@relation3) \
- .on(@relation2[:id].eq(@relation3[:id]))) \
- .on(@relation1[:id].eq(@relation2[:id])) \
- .to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`, `users_2`.`id`, `users_2`.`name`, `users_3`.`id`, `users_3`.`name`
- FROM `users`
- INNER JOIN `users` AS `users_2`
- ON `users`.`id` = `users_2`.`id`
- INNER JOIN `users` AS `users_3`
- ON `users_2`.`id` = `users_3`.`id`
- ")
- end
- end
-
- describe 'when joining right-associatively' do
- it 'manufactures sql aliasing the tables properly' do
- @relation1 \
- .join(@relation2).on(@relation1[:id].eq(@relation2[:id])) \
- .join(@relation3).on(@relation2[:id].eq(@relation3[:id])) \
- .to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`, `users_2`.`id`, `users_2`.`name`, `users_3`.`id`, `users_3`.`name`
- FROM `users`
- INNER JOIN `users` AS `users_2`
- ON `users`.`id` = `users_2`.`id`
- INNER JOIN `users` AS `users_3`
- ON `users_2`.`id` = `users_3`.`id`
- ")
- end
- end
- end
- end
-
- describe '[]' do
- describe 'when given an attribute belonging to both sub-relations' do
- it 'disambiguates the relation that serves as the ancestor to the attribute' do
- @relation1 \
- .join(@relation2) \
- .on(@predicate) \
- .should disambiguate_attributes(@relation1[:id], @relation2[:id])
- end
-
- describe 'when both relations are compound and only one is an alias' do
- it 'disambiguates the relation that serves as the ancestor to the attribute' do
- compound1 = @relation1.where(@predicate)
- compound2 = compound1.alias
- compound1 \
- .join(compound2) \
- .on(@predicate) \
- .should disambiguate_attributes(compound1[:id], compound2[:id])
- end
- end
-
- describe 'when the left relation is extremely compound' do
- it 'disambiguates the relation that serves as the ancestor to the attribute' do
- @relation1 \
- .where(@predicate) \
- .where(@predicate) \
- .join(@relation2) \
- .on(@predicate) \
- .should disambiguate_attributes(@relation1[:id], @relation2[:id])
- end
- end
-
- describe 'when the right relation is extremely compound' do
- it 'disambiguates the relation that serves as the ancestor to the attribute' do
- @relation1 \
- .join( \
- @relation2 \
- .where(@predicate) \
- .where(@predicate) \
- .where(@predicate)) \
- .on(@predicate) \
- .should disambiguate_attributes(@relation1[:id], @relation2[:id])
- end
- end
- end
- end
- end
- end
-end
diff --git a/spec/arel/integration/joins/with_aggregations_spec.rb b/spec/arel/integration/joins/with_aggregations_spec.rb
deleted file mode 100644
index 2b21dcaa1e..0000000000
--- a/spec/arel/integration/joins/with_aggregations_spec.rb
+++ /dev/null
@@ -1,89 +0,0 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
-
-module Arel
- describe Join do
- before do
- @relation1 = Arel(:users)
- @relation2 = Arel(:photos)
- @predicate = @relation1[:id].eq(@relation2[:user_id])
- end
-
- describe 'when joining aggregated relations' do
- before do
- @aggregation = @relation2 \
- .group(@relation2[:user_id]) \
- .project(@relation2[:user_id], @relation2[:id].count.as(:cnt)) \
- end
-
- describe '#to_sql' do
- # CLEANUP
- it '' do
- @relation1.join(@relation2.take(3)).on(@predicate).to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`, `photos_external`.`id`, `photos_external`.`user_id`, `photos_external`.`camera_id`
- FROM `users`
- INNER JOIN (SELECT `photos`.`id`, `photos`.`user_id`, `photos`.`camera_id` FROM `photos` LIMIT 3) AS `photos_external`
- ON `users`.`id` = `photos_external`.`user_id`
- ")
- end
-
- describe 'with the aggregation on the right' do
- it 'manufactures sql joining the left table to a derived table' do
- @relation1.join(@aggregation).on(@predicate).to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`, `photos_external`.`user_id`, `photos_external`.`cnt`
- FROM `users`
- INNER JOIN (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` GROUP BY `photos`.`user_id`) AS `photos_external`
- ON `users`.`id` = `photos_external`.`user_id`
- ")
- end
- end
-
- describe 'with the aggregation on the left' do
- it 'manufactures sql joining the right table to a derived table' do
- @aggregation.join(@relation1).on(@predicate).to_sql.should be_like("
- SELECT `photos_external`.`user_id`, `photos_external`.`cnt`, `users`.`id`, `users`.`name`
- FROM (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` GROUP BY `photos`.`user_id`) AS `photos_external`
- INNER JOIN `users`
- ON `users`.`id` = `photos_external`.`user_id`
- ")
- end
- end
-
- describe 'with the aggregation on both sides' do
- it 'it properly aliases the aggregations' do
- aggregation2 = @aggregation.alias
- @aggregation.join(aggregation2).on(aggregation2[:user_id].eq(@aggregation[:user_id])).to_sql.should be_like("
- SELECT `photos_external`.`user_id`, `photos_external`.`cnt`, `photos_external_2`.`user_id`, `photos_external_2`.`cnt`
- FROM (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` GROUP BY `photos`.`user_id`) AS `photos_external`
- INNER JOIN (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` GROUP BY `photos`.`user_id`) AS `photos_external_2`
- ON `photos_external_2`.`user_id` = `photos_external`.`user_id`
- ")
- end
- end
-
- describe 'when the aggration has a where' do
- describe 'with the aggregation on the left' do
- it "manufactures sql keeping wheres on the aggregation within the derived table" do
- @relation1.join(@aggregation.where(@aggregation[:user_id].eq(1))).on(@predicate).to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`, `photos_external`.`user_id`, `photos_external`.`cnt`
- FROM `users`
- INNER JOIN (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` WHERE `photos`.`user_id` = 1 GROUP BY `photos`.`user_id`) AS `photos_external`
- ON `users`.`id` = `photos_external`.`user_id`
- ")
- end
- end
-
- describe 'with the aggregation on the right' do
- it "manufactures sql keeping wheres on the aggregation within the derived table" do
- @aggregation.where(@aggregation[:user_id].eq(1)).join(@relation1).on(@predicate).to_sql.should be_like("
- SELECT `photos_external`.`user_id`, `photos_external`.`cnt`, `users`.`id`, `users`.`name`
- FROM (SELECT `photos`.`user_id`, COUNT(`photos`.`id`) AS `cnt` FROM `photos` WHERE `photos`.`user_id` = 1 GROUP BY `photos`.`user_id`) AS `photos_external`
- INNER JOIN `users`
- ON `users`.`id` = `photos_external`.`user_id`
- ")
- end
- end
- end
- end
- end
- end
-end
diff --git a/spec/arel/integration/joins/with_compounds_spec.rb b/spec/arel/integration/joins/with_compounds_spec.rb
deleted file mode 100644
index 95fadc23d6..0000000000
--- a/spec/arel/integration/joins/with_compounds_spec.rb
+++ /dev/null
@@ -1,65 +0,0 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
-
-module Arel
- describe Join do
- before do
- @relation1 = Arel(:users)
- @relation2 = Arel(:photos)
- @predicate = @relation1[:id].eq(@relation2[:user_id])
- end
-
- describe '#to_sql' do
- describe 'when the join contains a where' do
- describe 'and the where is given a string' do
- it 'does not escape the string' do
- @relation1 \
- .join(@relation2.where("asdf")) \
- .on(@predicate) \
- .to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`, `photos`.`id`, `photos`.`user_id`, `photos`.`camera_id`
- FROM `users`
- INNER JOIN `photos`
- ON `users`.`id` = `photos`.`user_id` AND asdf
- ")
- end
- end
- end
-
- describe 'when a compound contains a join' do
- describe 'and the compound is a where' do
- it 'manufactures sql disambiguating the tables' do
- @relation1 \
- .where(@relation1[:id].eq(1)) \
- .join(@relation2) \
- .on(@predicate) \
- .where(@relation1[:id].eq(1)) \
- .to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`, `photos`.`id`, `photos`.`user_id`, `photos`.`camera_id`
- FROM `users`
- INNER JOIN `photos`
- ON `users`.`id` = `photos`.`user_id`
- WHERE `users`.`id` = 1
- AND `users`.`id` = 1
- ")
- end
- end
-
- describe 'and the compound is a group' do
- it 'manufactures sql disambiguating the tables' do
- @relation1 \
- .join(@relation2) \
- .on(@predicate) \
- .group(@relation1[:id]) \
- .to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`, `photos`.`id`, `photos`.`user_id`, `photos`.`camera_id`
- FROM `users`
- INNER JOIN `photos`
- ON `users`.`id` = `photos`.`user_id`
- GROUP BY `users`.`id`
- ")
- end
- end
- end
- end
- end
-end
diff --git a/spec/arel/unit/predicates/binary_spec.rb b/spec/arel/unit/predicates/binary_spec.rb
deleted file mode 100644
index 56fcf2d8ad..0000000000
--- a/spec/arel/unit/predicates/binary_spec.rb
+++ /dev/null
@@ -1,95 +0,0 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
-
-module Arel
- describe Binary do
- before do
- @relation = Table.new(:users)
- @attribute1 = @relation[:id]
- @attribute2 = @relation[:name]
- class ConcreteBinary < Binary
- def predicate_sql
- "<=>"
- end
- end
- end
-
- describe "with compound predicates" do
- before do
- @operand1 = ConcreteBinary.new(@attribute1, 1)
- @operand2 = ConcreteBinary.new(@attribute2, "name")
- end
-
- describe Or do
- describe "#to_sql" do
- it "manufactures sql with an OR operation" do
- Or.new(@operand1, @operand2).to_sql.should be_like("
- (`users`.`id` <=> 1 OR `users`.`name` <=> 'name')
- ")
- end
- end
- end
-
- describe And do
- describe "#to_sql" do
- it "manufactures sql with an AND operation" do
- And.new(@operand1, @operand2).to_sql.should be_like("
- (`users`.`id` <=> 1 AND `users`.`name` <=> 'name')
- ")
- end
- end
- end
- end
-
- describe '#to_sql' do
- describe 'when relating two attributes' do
- it 'manufactures sql with a binary operation' do
- ConcreteBinary.new(@attribute1, @attribute2).to_sql.should be_like("
- `users`.`id` <=> `users`.`name`
- ")
- end
- end
-
- describe 'when relating an attribute and a value' do
- before do
- @value = "1-asdf"
- end
-
- describe 'when relating to an integer attribute' do
- it 'formats values as integers' do
- ConcreteBinary.new(@attribute1, @value).to_sql.should be_like("
- `users`.`id` <=> 1
- ")
- end
- end
-
- describe 'when relating to a string attribute' do
- it 'formats values as strings' do
- ConcreteBinary.new(@attribute2, @value).to_sql.should be_like("
- `users`.`name` <=> '1-asdf'
- ")
- end
- end
- end
- end
-
- describe '#bind' do
- before do
- @another_relation = @relation.alias
- end
-
- describe 'when both operands are attributes' do
- it "manufactures an expression with the attributes bound to the relation" do
- ConcreteBinary.new(@attribute1, @attribute2).bind(@another_relation). \
- should == ConcreteBinary.new(@another_relation[@attribute1], @another_relation[@attribute2])
- end
- end
-
- describe 'when an operand is a value' do
- it "manufactures an expression with unmodified values" do
- ConcreteBinary.new(@attribute1, "asdf").bind(@another_relation). \
- should == ConcreteBinary.new(@attribute1.find_correlate_in(@another_relation), "asdf".find_correlate_in(@another_relation))
- end
- end
- end
- end
-end \ No newline at end of file
diff --git a/spec/arel/unit/predicates/in_spec.rb b/spec/arel/unit/predicates/in_spec.rb
deleted file mode 100644
index 797798a77f..0000000000
--- a/spec/arel/unit/predicates/in_spec.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
-
-module Arel
- describe In do
- before do
- @relation = Table.new(:users)
- @attribute = @relation[:id]
- end
-
- describe '#to_sql' do
- describe 'when relating to an array' do
- describe 'when the array\'s elements are the same type as the attribute' do
- before do
- @array = [1, 2, 3]
- end
-
- it 'manufactures sql with a comma separated list' do
- In.new(@attribute, @array).to_sql.should be_like("
- `users`.`id` IN (1, 2, 3)
- ")
- end
- end
-
- describe 'when the array\'s elements are not same type as the attribute' do
- before do
- @array = ['1-asdf', 2, 3]
- end
-
- it 'formats values in the array as the type of the attribute' do
- In.new(@attribute, @array).to_sql.should be_like("
- `users`.`id` IN (1, 2, 3)
- ")
- end
- end
- end
-
- describe 'when relating to a range' do
- before do
- @range = 1..2
- end
-
- it 'manufactures sql with a between' do
- In.new(@attribute, @range).to_sql.should be_like("
- `users`.`id` BETWEEN 1 AND 2
- ")
- end
- end
-
- describe 'when relating to a relation' do
- it 'manufactures sql with a subselect' do
- In.new(@attribute, @relation).to_sql.should be_like("
- `users`.`id` IN (SELECT `users`.`id`, `users`.`name` FROM `users`)
- ")
- end
- end
- end
- end
-end \ No newline at end of file
diff --git a/spec/arel/unit/predicates/predicates_spec.rb b/spec/arel/unit/predicates/predicates_spec.rb
deleted file mode 100644
index d11637cabe..0000000000
--- a/spec/arel/unit/predicates/predicates_spec.rb
+++ /dev/null
@@ -1,33 +0,0 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
-
-module Arel
- describe Predicate do
- before do
- @relation = Table.new(:users)
- @attribute1 = @relation[:id]
- @attribute2 = @relation[:name]
- @operand1 = Equality.new(@attribute1, 1)
- @operand2 = Equality.new(@attribute2, "name")
- end
-
- describe "when being combined with another predicate with AND logic" do
- describe "#to_sql" do
- it "manufactures sql with an AND operation" do
- @operand1.and(@operand2).to_sql.should be_like("
- (`users`.`id` = 1 AND `users`.`name` = 'name')
- ")
- end
- end
- end
-
- describe "when being combined with another predicate with OR logic" do
- describe "#to_sql" do
- it "manufactures sql with an OR operation" do
- @operand1.or(@operand2).to_sql.should be_like("
- (`users`.`id` = 1 OR `users`.`name` = 'name')
- ")
- end
- end
- end
- end
-end \ No newline at end of file
diff --git a/spec/arel/unit/relations/alias_spec.rb b/spec/arel/unit/relations/alias_spec.rb
deleted file mode 100644
index 460a0ed0df..0000000000
--- a/spec/arel/unit/relations/alias_spec.rb
+++ /dev/null
@@ -1,36 +0,0 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
-
-module Arel
- describe Alias do
- before do
- @relation = Table.new(:users)
- end
-
- describe '==' do
- it "obtains if the objects are the same" do
- Alias.new(@relation).should_not == Alias.new(@relation)
- (aliaz = Alias.new(@relation)).should == aliaz
- end
- end
-
- describe '#to_sql' do
- describe 'when there is no ambiguity' do
- it 'does not alias table names anywhere a table name can appear' do
- @relation \
- .where(@relation[:id].eq(1)) \
- .order(@relation[:id]) \
- .project(@relation[:id]) \
- .group(@relation[:id]) \
- .alias \
- .to_sql.should be_like("
- SELECT `users`.`id`
- FROM `users`
- WHERE `users`.`id` = 1
- GROUP BY `users`.`id`
- ORDER BY `users`.`id`
- ")
- end
- end
- end
- end
-end
diff --git a/spec/arel/unit/relations/delete_spec.rb b/spec/arel/unit/relations/delete_spec.rb
deleted file mode 100644
index fa887d20fd..0000000000
--- a/spec/arel/unit/relations/delete_spec.rb
+++ /dev/null
@@ -1,42 +0,0 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
-
-module Arel
- describe Deletion do
- before do
- @relation = Table.new(:users)
- end
-
- describe '#to_sql' do
- it 'manufactures sql deleting a table relation' do
- Deletion.new(@relation).to_sql.should be_like("
- DELETE
- FROM `users`
- ")
- end
-
- it 'manufactures sql deleting a where relation' do
- Deletion.new(@relation.where(@relation[:id].eq(1))).to_sql.should be_like("
- DELETE
- FROM `users`
- WHERE `users`.`id` = 1
- ")
- end
-
- it "manufactures sql deleting a ranged relation" do
- Deletion.new(@relation.take(1)).to_sql.should be_like("
- DELETE
- FROM `users`
- LIMIT 1
- ")
- end
- end
-
- describe '#call' do
- it 'executes a delete on the connection' do
- deletion = Deletion.new(@relation)
- mock(connection = Object.new).delete(deletion.to_sql)
- deletion.call(connection)
- end
- end
- end
-end
diff --git a/spec/arel/unit/relations/group_spec.rb b/spec/arel/unit/relations/group_spec.rb
deleted file mode 100644
index a0147b9416..0000000000
--- a/spec/arel/unit/relations/group_spec.rb
+++ /dev/null
@@ -1,32 +0,0 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
-
-module Arel
- describe Group do
- before do
- @relation = Table.new(:users)
- @attribute = @relation[:id]
- end
-
- describe '#to_sql' do
- describe 'when given a predicate' do
- it "manufactures sql with where clause conditions" do
- Group.new(@relation, @attribute).to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`
- FROM `users`
- GROUP BY `users`.`id`
- ")
- end
- end
-
- describe 'when given a string' do
- it "passes the string through to the where clause" do
- Group.new(@relation, 'asdf').to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`
- FROM `users`
- GROUP BY asdf
- ")
- end
- end
- end
- end
-end \ No newline at end of file
diff --git a/spec/arel/unit/relations/insert_spec.rb b/spec/arel/unit/relations/insert_spec.rb
deleted file mode 100644
index 441c97b290..0000000000
--- a/spec/arel/unit/relations/insert_spec.rb
+++ /dev/null
@@ -1,71 +0,0 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
-
-module Arel
- describe Insert do
- before do
- @relation = Table.new(:users)
- end
-
- describe '#to_sql' do
- it 'manufactures sql inserting data when given multiple rows' do
- pending 'it should insert multiple rows'
- @insertion = Insert.new(@relation, [@relation[:name] => "nick", @relation[:name] => "bryan"])
-
- @insertion.to_sql.should be_like("
- INSERT
- INTO `users`
- (`name`) VALUES ('nick'), ('bryan')
- ")
- end
-
- it 'manufactures sql inserting data when given multiple values' do
- @insertion = Insert.new(@relation, @relation[:id] => "1", @relation[:name] => "nick")
-
- @insertion.to_sql.should be_like("
- INSERT
- INTO `users`
- (`id`, `name`) VALUES (1, 'nick')
- ")
- end
-
- describe 'when given values whose types correspond to the types of the attributes' do
- before do
- @insertion = Insert.new(@relation, @relation[:name] => "nick")
- end
-
- it 'manufactures sql inserting data' do
- @insertion.to_sql.should be_like("
- INSERT
- INTO `users`
- (`name`) VALUES ('nick')
- ")
- end
- end
-
- describe 'when given values whose types differ from from the types of the attributes' do
- before do
- @insertion = Insert.new(@relation, @relation[:id] => '1-asdf')
- end
-
- it 'manufactures sql inserting data' do
- @insertion.to_sql.should be_like("
- INSERT
- INTO `users`
- (`id`) VALUES (1)
- ")
- end
- end
- end
-
- describe '#call' do
- before do
- @insertion = Insert.new(@relation, @relation[:name] => "nick")
- end
-
- it 'executes an insert on the connection' do
- mock(connection = Object.new).insert(@insertion.to_sql)
- @insertion.call(connection)
- end
- end
- end
-end
diff --git a/spec/arel/unit/relations/join_spec.rb b/spec/arel/unit/relations/join_spec.rb
deleted file mode 100644
index 1698bf9647..0000000000
--- a/spec/arel/unit/relations/join_spec.rb
+++ /dev/null
@@ -1,54 +0,0 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
-
-module Arel
- describe Join do
- before do
- @relation1 = Table.new(:users)
- @relation2 = Table.new(:photos)
- @predicate = @relation1[:id].eq(@relation2[:user_id])
- end
-
- describe 'hashing' do
- it 'implements hash equality' do
- Join.new("INNER JOIN", @relation1, @relation2, @predicate) \
- .should hash_the_same_as(Join.new("INNER JOIN", @relation1, @relation2, @predicate))
- end
- end
-
- describe '#engine' do
- it "delegates to a relation's engine" do
- Join.new("INNER JOIN", @relation1, @relation2, @predicate).engine.should == @relation1.engine
- end
- end
-
- describe '#attributes' do
- it 'combines the attributes of the two relations' do
- join = Join.new("INNER JOIN", @relation1, @relation2, @predicate)
- join.attributes.should ==
- (@relation1.attributes + @relation2.attributes).collect { |a| a.bind(join) }
- end
- end
-
- describe '#to_sql' do
- describe 'when joining with another relation' do
- it 'manufactures sql joining the two tables on the predicate' do
- Join.new("INNER JOIN", @relation1, @relation2, @predicate).to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`, `photos`.`id`, `photos`.`user_id`, `photos`.`camera_id`
- FROM `users`
- INNER JOIN `photos` ON `users`.`id` = `photos`.`user_id`
- ")
- end
- end
-
- describe 'when joining with a string' do
- it "passes the string through to the where clause" do
- Join.new("INNER JOIN asdf ON fdsa", @relation1).to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`
- FROM `users`
- INNER JOIN asdf ON fdsa
- ")
- end
- end
- end
- end
-end \ No newline at end of file
diff --git a/spec/arel/unit/relations/order_spec.rb b/spec/arel/unit/relations/order_spec.rb
deleted file mode 100644
index d373a8ba12..0000000000
--- a/spec/arel/unit/relations/order_spec.rb
+++ /dev/null
@@ -1,66 +0,0 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
-
-module Arel
- describe Order do
- before do
- @relation = Table.new(:users)
- @attribute = @relation[:id]
- end
-
- describe '#to_sql' do
- describe "when given an attribute" do
- it "manufactures sql with an order clause populated by the attribute" do
- Order.new(@relation, @attribute).to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`
- FROM `users`
- ORDER BY `users`.`id`
- ")
- end
- end
-
- describe "when given multiple attributes" do
- before do
- @another_attribute = @relation[:name]
- end
-
- it "manufactures sql with an order clause populated by comma-separated attributes" do
- Order.new(@relation, @attribute, @another_attribute).to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`
- FROM `users`
- ORDER BY `users`.`id`, `users`.`name`
- ")
- end
- end
-
- describe "when given a string" do
- before do
- @string = "asdf"
- end
-
- it "passes the string through to the order clause" do
- Order.new(@relation, @string).to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`
- FROM `users`
- ORDER BY asdf
- ")
- end
- end
-
- describe "when ordering an ordered relation" do
- before do
- @ordered_relation = Order.new(@relation, @attribute)
- @another_attribute = @relation[:name]
- end
-
- it "manufactures sql with the order clause of the last ordering preceding the first ordering" do
- Order.new(@ordered_relation, @another_attribute).to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`
- FROM `users`
- ORDER BY `users`.`name`, `users`.`id`
- ")
- end
- end
- end
- end
-end
- \ No newline at end of file
diff --git a/spec/arel/unit/relations/project_spec.rb b/spec/arel/unit/relations/project_spec.rb
deleted file mode 100644
index f389b18c54..0000000000
--- a/spec/arel/unit/relations/project_spec.rb
+++ /dev/null
@@ -1,81 +0,0 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
-
-module Arel
- describe Project do
- before do
- @relation = Table.new(:users)
- @attribute = @relation[:id]
- end
-
- describe '#attributes' do
- before do
- @projection = Project.new(@relation, @attribute)
- end
-
- it "manufactures attributes associated with the projection relation" do
- @projection.attributes.should == [@attribute].collect { |a| a.bind(@projection) }
- end
- end
-
- describe '#to_sql' do
- describe 'when given an attribute' do
- it "manufactures sql with a limited select clause" do
- Project.new(@relation, @attribute).to_sql.should be_like("
- SELECT `users`.`id`
- FROM `users`
- ")
- end
- end
-
- describe 'when given a relation' do
- before do
- @scalar_relation = Project.new(@relation, @relation[:name])
- end
-
- it "manufactures sql with scalar selects" do
- Project.new(@relation, @scalar_relation).to_sql.should be_like("
- SELECT (SELECT `users`.`name` FROM `users`) AS `users` FROM `users`
- ")
- end
- end
-
- describe 'when given a string' do
- it "passes the string through to the select clause" do
- Project.new(@relation, 'asdf').to_sql.should be_like("
- SELECT asdf FROM `users`
- ")
- end
- end
-
- describe 'when given an expression' do
- it 'manufactures sql with expressions' do
- @relation.project(@attribute.count).to_sql.should be_like("
- SELECT COUNT(`users`.`id`) AS count_id
- FROM `users`
- ")
- end
-
- it 'manufactures sql with distinct expressions' do
- @relation.project(@attribute.count(true)).to_sql.should be_like("
- SELECT COUNT(DISTINCT `users`.`id`) AS count_id
- FROM `users`
- ")
- end
- end
- end
-
- describe '#externalizable?' do
- describe 'when the projections are attributes' do
- it 'returns false' do
- Project.new(@relation, @attribute).should_not be_externalizable
- end
- end
-
- describe 'when the projections include an aggregation' do
- it "obtains" do
- Project.new(@relation, @attribute.sum).should be_externalizable
- end
- end
- end
- end
-end
diff --git a/spec/arel/unit/relations/skip_spec.rb b/spec/arel/unit/relations/skip_spec.rb
deleted file mode 100644
index d83c969aa8..0000000000
--- a/spec/arel/unit/relations/skip_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
-
-module Arel
- describe Skip do
- before do
- @relation = Table.new(:users)
- @skipped = 4
- end
-
- describe '#to_sql' do
- it "manufactures sql with limit and offset" do
- Skip.new(@relation, @skipped).to_s.should be_like("
- SELECT `users`.`id`, `users`.`name`
- FROM `users`
- OFFSET #{@skipped}
- ")
- end
- end
- end
-end \ No newline at end of file
diff --git a/spec/arel/unit/relations/table_spec.rb b/spec/arel/unit/relations/table_spec.rb
deleted file mode 100644
index 54520bf3b6..0000000000
--- a/spec/arel/unit/relations/table_spec.rb
+++ /dev/null
@@ -1,88 +0,0 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
-
-module Arel
- describe Table do
- before do
- @relation = Table.new(:users)
- end
-
- describe '[]' do
- describe 'when given a', Symbol do
- it "manufactures an attribute if the symbol names an attribute within the relation" do
- @relation[:id].should == Attribute.new(@relation, :id)
- @relation[:does_not_exist].should be_nil
- end
- end
-
- describe 'when given an', Attribute do
- it "returns the attribute if the attribute is within the relation" do
- @relation[@relation[:id]].should == @relation[:id]
- end
-
- it "returns nil if the attribtue is not within the relation" do
- another_relation = Table.new(:photos)
- @relation[another_relation[:id]].should be_nil
- end
- end
-
- describe 'when given an', Expression do
- before do
- @expression = @relation[:id].count
- end
-
- it "returns the Expression if the Expression is within the relation" do
- @relation[@expression].should be_nil
- end
- end
- end
-
- describe '#to_sql' do
- it "manufactures a simple select query" do
- @relation.to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`
- FROM `users`
- ")
- end
- end
-
- describe '#column_for' do
- it "returns the column corresponding to the attribute" do
- @relation.column_for(@relation[:id]).should == @relation.columns.detect { |c| c.name == 'id' }
- end
- end
-
- describe '#attributes' do
- it 'manufactures attributes corresponding to columns in the table' do
- @relation.attributes.should == [
- Attribute.new(@relation, :id),
- Attribute.new(@relation, :name)
- ]
- end
-
- describe '#reset' do
- it "reloads columns from the database" do
- lambda { stub(@relation.engine).columns { [] } }.should_not change { @relation.attributes }
- lambda { @relation.reset }.should change { @relation.attributes }
- end
- end
- end
-
- describe 'hashing' do
- it "implements hash equality" do
- Table.new(:users).should hash_the_same_as(Table.new(:users))
- Table.new(:users).should_not hash_the_same_as(Table.new(:photos))
- end
- end
-
- describe '#engine' do
- it "defaults to global engine" do
- Table.engine = engine = Engine.new
- Table.new(:users).engine.should == engine
- end
-
- it "can be specified" do
- Table.new(:users, engine = Engine.new).engine.should == engine
- end
- end
- end
-end \ No newline at end of file
diff --git a/spec/arel/unit/relations/take_spec.rb b/spec/arel/unit/relations/take_spec.rb
deleted file mode 100644
index dca7806057..0000000000
--- a/spec/arel/unit/relations/take_spec.rb
+++ /dev/null
@@ -1,20 +0,0 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
-
-module Arel
- describe Take do
- before do
- @relation = Table.new(:users)
- @taken = 4
- end
-
- describe '#to_sql' do
- it "manufactures sql with limit and offset" do
- Take.new(@relation, @taken).to_s.should be_like("
- SELECT `users`.`id`, `users`.`name`
- FROM `users`
- LIMIT #{@taken}
- ")
- end
- end
- end
-end \ No newline at end of file
diff --git a/spec/arel/unit/relations/update_spec.rb b/spec/arel/unit/relations/update_spec.rb
deleted file mode 100644
index b67369251f..0000000000
--- a/spec/arel/unit/relations/update_spec.rb
+++ /dev/null
@@ -1,81 +0,0 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
-
-module Arel
- describe Update do
- before do
- @relation = Table.new(:users)
- end
-
- describe '#to_sql' do
- it "manufactures sql updating attributes when given multiple attributes" do
- Update.new(@relation, @relation[:id] => 1, @relation[:name] => "nick").to_sql.should be_like("
- UPDATE `users`
- SET `id` = 1, `name` = 'nick'
- ")
- end
-
- it "manufactures sql updating attributes when given a ranged relation" do
- Update.new(@relation.take(1), @relation[:name] => "nick").to_sql.should be_like("
- UPDATE `users`
- SET `name` = 'nick'
- LIMIT 1
- ")
- end
-
- describe 'when given values whose types correspond to the types of the attributes' do
- before do
- @update = Update.new(@relation, @relation[:name] => "nick")
- end
-
- it 'manufactures sql updating attributes' do
- @update.to_sql.should be_like("
- UPDATE `users`
- SET `name` = 'nick'
- ")
- end
- end
-
- describe 'when given values whose types differ from from the types of the attributes' do
- before do
- @update = Update.new(@relation, @relation[:id] => '1-asdf')
- end
-
- it 'manufactures sql updating attributes' do
- @update.to_sql.should be_like("
- UPDATE `users`
- SET `id` = 1
- ")
- end
- end
-
- describe 'when the relation is a where' do
- before do
- @update = Update.new(
- @relation.where(@relation[:id].eq(1)),
- @relation[:name] => "nick"
- )
- end
-
- it 'manufactures sql updating a where relation' do
- @update.to_sql.should be_like("
- UPDATE `users`
- SET `name` = 'nick'
- WHERE `users`.`id` = 1
- ")
- end
- end
- end
-
- describe '#call' do
- before do
- @update = Update.new(@relation, @relation[:name] => "nick")
- end
-
- it 'executes an update on the connection' do
- mock(connection = Object.new).update(@update.to_sql)
- @update.call(connection)
- end
- end
-
- end
-end
diff --git a/spec/arel/unit/relations/where_spec.rb b/spec/arel/unit/relations/where_spec.rb
deleted file mode 100644
index 8ef4d54b63..0000000000
--- a/spec/arel/unit/relations/where_spec.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
-
-module Arel
- describe Where do
- before do
- @relation = Table.new(:users)
- @predicate = @relation[:id].eq(1)
- end
-
- describe '#initialize' do
- it "manufactures nested where relations if multiple predicates are provided" do
- another_predicate = @relation[:name].lt(2)
- Where.new(@relation, @predicate, another_predicate). \
- should == Where.new(Where.new(@relation, another_predicate), @predicate)
- end
- end
-
- describe '#to_sql' do
- describe 'when given a predicate' do
- it "manufactures sql with where clause conditions" do
- Where.new(@relation, @predicate).to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`
- FROM `users`
- WHERE `users`.`id` = 1
- ")
- end
- end
-
- describe 'when given a string' do
- it "passes the string through to the where clause" do
- Where.new(@relation, 'asdf').to_sql.should be_like("
- SELECT `users`.`id`, `users`.`name`
- FROM `users`
- WHERE asdf
- ")
- end
- end
- end
- end
-end
diff --git a/spec/connections/mysql_connection.rb b/spec/connections/mysql_connection.rb
new file mode 100644
index 0000000000..a58ddc35ef
--- /dev/null
+++ b/spec/connections/mysql_connection.rb
@@ -0,0 +1,15 @@
+require "activerecord"
+puts "Using native MySQL"
+
+ActiveRecord::Base.logger = Logger.new("debug.log")
+
+ActiveRecord::Base.configurations = {
+ 'unit' => {
+ :adapter => 'mysql',
+ :username => 'rails',
+ :encoding => 'utf8',
+ :database => 'arel_unit',
+ }
+}
+
+ActiveRecord::Base.establish_connection 'unit'
diff --git a/spec/connections/postgresql_connection.rb b/spec/connections/postgresql_connection.rb
new file mode 100644
index 0000000000..e376d33ec6
--- /dev/null
+++ b/spec/connections/postgresql_connection.rb
@@ -0,0 +1,14 @@
+require "activerecord"
+puts "Using native PostgreSQL"
+
+ActiveRecord::Base.logger = Logger.new("debug.log")
+
+ActiveRecord::Base.configurations = {
+ 'unit' => {
+ :adapter => 'postgresql',
+ :encoding => 'utf8',
+ :database => 'arel_unit',
+ }
+}
+
+ActiveRecord::Base.establish_connection 'unit'
diff --git a/spec/connections/sqlite3_connection.rb b/spec/connections/sqlite3_connection.rb
new file mode 100644
index 0000000000..9e9503e0ca
--- /dev/null
+++ b/spec/connections/sqlite3_connection.rb
@@ -0,0 +1,25 @@
+require "rubygems"
+require "activerecord"
+puts "Using native SQLite3"
+
+ActiveRecord::Base.logger = Logger.new("debug.log")
+
+db_file = "spec/fixtures/fixture_database.sqlite3"
+
+ActiveRecord::Base.configurations = {
+ "unit" => {
+ :adapter => 'sqlite3',
+ :database => db_file,
+ :timeout => 5000
+ }
+}
+
+unless File.exist?(db_file)
+ puts "SQLite3 database not found at #{db_file}. Rebuilding it."
+ FileUtils.mkdir_p(File.dirname(db_file))
+ sqlite_command = %Q{sqlite3 "#{db_file}" "create table a (a integer); drop table a;"}
+ puts "Executing '#{sqlite_command}'"
+ raise "Seems that there is no sqlite3 executable available" unless system(sqlite_command)
+end
+
+ActiveRecord::Base.establish_connection("unit")
diff --git a/spec/doubles/database.rb b/spec/doubles/database.rb
deleted file mode 100644
index f8a4b38e17..0000000000
--- a/spec/doubles/database.rb
+++ /dev/null
@@ -1,51 +0,0 @@
-module Fake
- class Engine
- def connection
- @conn ||= Connection.new
- end
- end
-
- class Connection
- include ActiveRecord::ConnectionAdapters::Quoting
-
- def columns(table_name, comment)
- { "users" =>
- [
- Column.new("id", :integer),
- Column.new("name", :string)
- ],
- "photos" =>
- [
- Column.new("id", :integer),
- Column.new("user_id", :integer),
- Column.new("camera_id", :integer)
- ]
- }[table_name]
- end
-
- def execute(*args)
- []
- end
-
- def quote_column_name(column_name)
- "`#{column_name}`"
- end
-
- def quote_table_name(table_name)
- "`#{table_name}`"
- end
-
- def supports_count_distinct?
- true
- end
- end
-
- class Column
- attr_reader :name, :type
-
- def initialize(name, type)
- @name = name
- @type = type
- end
- end
-end
diff --git a/spec/doubles/hash.rb b/spec/doubles/hash.rb
index 97d25742cb..32c5b98058 100644
--- a/spec/doubles/hash.rb
+++ b/spec/doubles/hash.rb
@@ -2,19 +2,19 @@ class Hash
def ordered_array
to_a.sort { |(key1, value1), (key2, value2)| key1.hash <=> key2.hash }
end
-
+
def keys
ordered_array.collect(&:first)
end
-
+
def values
ordered_array.collect { |_, v| v }
end
-
+
def each(&block)
ordered_array.each(&block)
end
-
+
def shift
returning to_a.first do |k, v|
delete(k)
diff --git a/spec/matchers/be_like.rb b/spec/matchers/be_like.rb
index 4ff5bc532f..c9d4d4b979 100644
--- a/spec/matchers/be_like.rb
+++ b/spec/matchers/be_like.rb
@@ -3,22 +3,22 @@ module BeLikeMatcher
def initialize(expected)
@expected = expected
end
-
+
def matches?(actual)
@actual = actual
@expected.gsub(/\s+/, ' ').strip == @actual.gsub(/\s+/, ' ').strip
end
-
+
def failure_message
"expected\n#{@actual}\nto be like\n#{@expected}"
end
-
+
def negative_failure_message
"expected\n#{@actual}\nto be unlike\n#{@expected}"
end
end
-
+
def be_like(expected)
BeLike.new(expected)
end
-end \ No newline at end of file
+end
diff --git a/spec/matchers/disambiguate_attributes.rb b/spec/matchers/disambiguate_attributes.rb
index bee7d22b0c..bc4a5215d4 100644
--- a/spec/matchers/disambiguate_attributes.rb
+++ b/spec/matchers/disambiguate_attributes.rb
@@ -3,7 +3,7 @@ module DisambiguateAttributesMatcher
def initialize(attributes)
@attributes = attributes
end
-
+
def matches?(actual)
@actual = actual
attribute1, attribute2 = @attributes
@@ -11,18 +11,18 @@ module DisambiguateAttributesMatcher
!@actual[attribute1].descends_from?(attribute2) &&
@actual[attribute2].descends_from?(attribute2)
end
-
+
def failure_message
""
# "expected #{@actual} to disambiguate its attributes"
end
-
+
def negative_failure_message
"expected #{@actual} to not disambiguate its attributes"
end
end
-
+
def disambiguate_attributes(*attributes)
DisambiguateAttributes.new(attributes)
end
-end \ No newline at end of file
+end
diff --git a/spec/matchers/hash_the_same_as.rb b/spec/matchers/hash_the_same_as.rb
index c1903b62b4..03e955a0cb 100644
--- a/spec/matchers/hash_the_same_as.rb
+++ b/spec/matchers/hash_the_same_as.rb
@@ -3,24 +3,24 @@ module HashTheSameAsMatcher
def initialize(expected)
@expected = expected
end
-
+
def matches?(actual)
@actual = actual
hash = {}
hash[@expected] = :some_arbitrary_value
hash[@actual] == :some_arbitrary_value
end
-
+
def failure_message
"expected #{@actual} to hash the same as #{@expected}; they must be `eql?` and have the same `#hash` value"
end
-
+
def negative_failure_message
"expected #{@actual} to hash differently than #{@expected}; they must not be `eql?` or have a differing `#hash` values"
end
end
-
+
def hash_the_same_as(expected)
HashTheSameAs.new(expected)
end
-end \ No newline at end of file
+end
diff --git a/spec/schemas/mysql_schema.rb b/spec/schemas/mysql_schema.rb
new file mode 100644
index 0000000000..dc2558fd6a
--- /dev/null
+++ b/spec/schemas/mysql_schema.rb
@@ -0,0 +1,18 @@
+sql = <<-SQL
+ DROP TABLE IF EXISTS users;
+ CREATE TABLE users (
+ id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ name VARCHAR(255) NOT NULL
+ );
+
+ DROP TABLE IF EXISTS photos;
+ CREATE TABLE photos (
+ id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ user_id INTEGER NOT NULL,
+ camera_id INTEGER NOT NULL
+ );
+SQL
+
+sql.split(/;/).select(&:present?).each do |sql_statement|
+ ActiveRecord::Base.connection.execute sql_statement
+end
diff --git a/spec/schemas/postgresql_schema.rb b/spec/schemas/postgresql_schema.rb
new file mode 100644
index 0000000000..30fa665902
--- /dev/null
+++ b/spec/schemas/postgresql_schema.rb
@@ -0,0 +1,18 @@
+sql = <<-SQL
+ DROP TABLE IF EXISTS users;
+ CREATE TABLE users (
+ id SERIAL PRIMARY KEY NOT NULL,
+ name VARCHAR(255) NOT NULL
+ );
+
+ DROP TABLE IF EXISTS photos;
+ CREATE TABLE photos (
+ id SERIAL PRIMARY KEY NOT NULL,
+ user_id INTEGER NOT NULL,
+ camera_id INTEGER NOT NULL
+ );
+SQL
+
+sql.split(/;/).select(&:present?).each do |sql_statement|
+ ActiveRecord::Base.connection.execute sql_statement
+end
diff --git a/spec/schemas/sqlite3_schema.rb b/spec/schemas/sqlite3_schema.rb
new file mode 100644
index 0000000000..94d224520e
--- /dev/null
+++ b/spec/schemas/sqlite3_schema.rb
@@ -0,0 +1,18 @@
+sql = <<-SQL
+ DROP TABLE IF EXISTS users;
+ CREATE TABLE users (
+ id INTEGER NOT NULL PRIMARY KEY,
+ name VARCHAR(255) NOT NULL
+ );
+
+ DROP TABLE IF EXISTS photos;
+ CREATE TABLE photos (
+ id INTEGER NOT NULL PRIMARY KEY,
+ user_id INTEGER NOT NULL,
+ camera_id INTEGER NOT NULL
+ );
+SQL
+
+sql.split(/;/).select(&:present?).each do |sql_statement|
+ ActiveRecord::Base.connection.execute sql_statement
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index ce539b6ffa..beb634fbd3 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -11,10 +11,37 @@ require 'arel'
Dir["#{dir}/#{helper}/*"].each { |m| require "#{dir}/#{helper}/#{File.basename(m)}" }
end
+module AdapterGuards
+ def adapter_is(name)
+ verify_adapter_name(name)
+ yield if name.to_s == adapter_name
+ end
+
+ def adapter_is_not(name)
+ verify_adapter_name(name)
+ yield if name.to_s != adapter_name
+ end
+
+ def adapter_name
+ name = ActiveRecord::Base.configurations["unit"][:adapter]
+ verify_adapter_name(name)
+ name
+ end
+
+ def verify_adapter_name(name)
+ raise "Invalid adapter name: #{name}" unless valid_adapters.include?(name.to_s)
+ end
+
+ def valid_adapters
+ %w[mysql postgresql sqlite3]
+ end
+end
+
Spec::Runner.configure do |config|
- config.include(BeLikeMatcher, HashTheSameAsMatcher, DisambiguateAttributesMatcher)
+ config.include BeLikeMatcher, HashTheSameAsMatcher, DisambiguateAttributesMatcher
+ config.include AdapterGuards
config.mock_with :rr
config.before do
- Arel::Table.engine = Arel::Engine.new(Fake::Engine.new)
+ Arel::Table.engine = Arel::Sql::Engine.new(ActiveRecord::Base)
end
end