aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/TODO8
-rw-r--r--lib/arel/algebra/extensions/array.rb7
-rw-r--r--lib/arel/algebra/extensions/hash.rb7
-rw-r--r--lib/arel/algebra/extensions/object.rb5
-rw-r--r--lib/arel/algebra/primitives.rb1
-rw-r--r--lib/arel/algebra/primitives/attribute.rb17
-rw-r--r--lib/arel/algebra/primitives/ordering.rb18
-rw-r--r--lib/arel/algebra/primitives/value.rb8
-rw-r--r--lib/arel/algebra/relations/operations/order.rb3
-rw-r--r--lib/arel/engines/memory/primitives.rb14
-rw-r--r--lib/arel/engines/memory/relations.rb1
-rw-r--r--lib/arel/engines/memory/relations/compound.rb5
-rw-r--r--lib/arel/engines/memory/relations/operations.rb33
-rw-r--r--lib/arel/engines/sql/formatters.rb6
-rw-r--r--lib/arel/engines/sql/primitives.rb14
-rw-r--r--lib/arel/engines/sql/relations/utilities/compound.rb2
-rw-r--r--spec/arel/unit/relations/alias_spec.rb4
-rw-r--r--spec/arel/unit/relations/array_spec.rb75
-rw-r--r--spec/arel/unit/relations/order_spec.rb12
19 files changed, 208 insertions, 32 deletions
diff --git a/doc/TODO b/doc/TODO
index ebc469ad70..dfbf09de9d 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -1,6 +1,9 @@
todo:
-- expressions should be class-based, and joins too, anything _sql should be renamed
+- projection is by definition distincting
+- union/intersection
- refactor adapter pattern
+- result sets to attr correlation too
+ - result sets should be array relations
- clean up block_given stuff
- reorganize sql tests
- audit unit coverage of algebra
@@ -10,6 +13,7 @@ todo:
- blocks for joins
- implement in memory adapter
- implement mnesia adapter
+- test ordering
- joins become subselects in writes:
users.delete().where(
addresses.c.user_id==
@@ -18,7 +22,6 @@ users.delete().where(
)
- rename externalize to derived.
- and/or w/ predicates
-- result sets to attr correlation too
- cache expiry on write
- transactions
- scoped writes
@@ -89,6 +92,7 @@ done:
- 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
icebox:
- #bind in Attribute and Expression should be doing a descend?
diff --git a/lib/arel/algebra/extensions/array.rb b/lib/arel/algebra/extensions/array.rb
index 5b6d6d6abd..935569a07b 100644
--- a/lib/arel/algebra/extensions/array.rb
+++ b/lib/arel/algebra/extensions/array.rb
@@ -2,4 +2,11 @@ class Array
def to_hash
Hash[*flatten]
end
+
+ def group_by
+ inject({}) do |groups, element|
+ (groups[yield(element)] ||= []) << element
+ groups
+ end
+ end
end \ No newline at end of file
diff --git a/lib/arel/algebra/extensions/hash.rb b/lib/arel/algebra/extensions/hash.rb
index 7472b5aa73..bc97785e62 100644
--- a/lib/arel/algebra/extensions/hash.rb
+++ b/lib/arel/algebra/extensions/hash.rb
@@ -4,4 +4,11 @@ class Hash
bound.merge(key.bind(relation) => value.bind(relation))
end
end
+
+ def slice(*attributes)
+ inject({}) do |cheese, (key, value)|
+ cheese[key] = value if attributes.include?(key)
+ cheese
+ end
+ end
end \ No newline at end of file
diff --git a/lib/arel/algebra/extensions/object.rb b/lib/arel/algebra/extensions/object.rb
index d626407dcb..efdbbf525f 100644
--- a/lib/arel/algebra/extensions/object.rb
+++ b/lib/arel/algebra/extensions/object.rb
@@ -12,4 +12,9 @@ class Object
self
end
end
+
+ def let
+ yield(self)
+ end
end
+
diff --git a/lib/arel/algebra/primitives.rb b/lib/arel/algebra/primitives.rb
index a4c3169e0b..df8d16a5d5 100644
--- a/lib/arel/algebra/primitives.rb
+++ b/lib/arel/algebra/primitives.rb
@@ -1,4 +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
index 943c5b030e..7a4411e248 100644
--- a/lib/arel/algebra/primitives/attribute.rb
+++ b/lib/arel/algebra/primitives/attribute.rb
@@ -17,6 +17,10 @@ module Arel
def aggregation?
false
end
+
+ def inspect
+ "<Attribute #{name}>"
+ end
module Transformations
def self.included(klass)
@@ -129,5 +133,18 @@ module Arel
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/ordering.rb b/lib/arel/algebra/primitives/ordering.rb
new file mode 100644
index 0000000000..e8d8f97188
--- /dev/null
+++ b/lib/arel/algebra/primitives/ordering.rb
@@ -0,0 +1,18 @@
+module Arel
+ class Ordering
+ attributes :attribute
+ deriving :initialize, :==
+ delegate :relation, :to => :attribute
+
+ def bind(relation)
+ self.class.new(attribute.bind(relation))
+ end
+
+ def to_ordering
+ self
+ end
+ end
+
+ class Ascending < Ordering; end
+ class Descending < Ordering; end
+end \ No newline at end of file
diff --git a/lib/arel/algebra/primitives/value.rb b/lib/arel/algebra/primitives/value.rb
index 76c82890d0..e363805140 100644
--- a/lib/arel/algebra/primitives/value.rb
+++ b/lib/arel/algebra/primitives/value.rb
@@ -7,12 +7,8 @@ module Arel
Value.new(value, relation)
end
- def aggregation?
- false
- end
-
- def to_attribute
- value
+ def to_ordering
+ self
end
end
end
diff --git a/lib/arel/algebra/relations/operations/order.rb b/lib/arel/algebra/relations/operations/order.rb
index 05af3fde4d..eccd8bcda0 100644
--- a/lib/arel/algebra/relations/operations/order.rb
+++ b/lib/arel/algebra/relations/operations/order.rb
@@ -10,7 +10,8 @@ module Arel
# TESTME
def orders
- (orderings + relation.orders).collect { |o| o.bind(self) }
+ # QUESTION - do we still need relation.orders ?
+ (orderings + relation.orders).collect { |o| o.bind(self) }.collect { |o| o.to_ordering }
end
end
end \ No newline at end of file
diff --git a/lib/arel/engines/memory/primitives.rb b/lib/arel/engines/memory/primitives.rb
index 77d4c1a52c..f8bbcedb55 100644
--- a/lib/arel/engines/memory/primitives.rb
+++ b/lib/arel/engines/memory/primitives.rb
@@ -10,4 +10,18 @@ module Arel
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 \ No newline at end of file
diff --git a/lib/arel/engines/memory/relations.rb b/lib/arel/engines/memory/relations.rb
index 9f8264c439..1b009537b9 100644
--- a/lib/arel/engines/memory/relations.rb
+++ b/lib/arel/engines/memory/relations.rb
@@ -1,3 +1,4 @@
require 'arel/engines/memory/relations/array'
require 'arel/engines/memory/relations/operations'
+require 'arel/engines/memory/relations/compound'
diff --git a/lib/arel/engines/memory/relations/compound.rb b/lib/arel/engines/memory/relations/compound.rb
new file mode 100644
index 0000000000..b029082d57
--- /dev/null
+++ b/lib/arel/engines/memory/relations/compound.rb
@@ -0,0 +1,5 @@
+module Arel
+ class Compound < Relation
+ delegate :array, :to => :relation
+ end
+end
diff --git a/lib/arel/engines/memory/relations/operations.rb b/lib/arel/engines/memory/relations/operations.rb
index eb11fb55fd..115df054df 100644
--- a/lib/arel/engines/memory/relations/operations.rb
+++ b/lib/arel/engines/memory/relations/operations.rb
@@ -4,4 +4,37 @@ module Arel
relation.eval.select { |row| predicate.eval(row) }
end
end
+
+ class Order < Compound
+ def eval
+ relation.eval.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
+ relation.eval.collect { |r| r.slice(*projections) }
+ end
+ end
+
+ class Take < Compound
+ def eval
+ relation.eval[0, taken]
+ end
+ end
+
+ class Skip < Compound
+ def eval
+ relation.eval[skipped..-1]
+ end
+ end
+
+ class Group < Compound
+ def eval
+ raise NotImplementedError
+ end
+ end
end \ No newline at end of file
diff --git a/lib/arel/engines/sql/formatters.rb b/lib/arel/engines/sql/formatters.rb
index f82ddf631f..bc5f0f7c64 100644
--- a/lib/arel/engines/sql/formatters.rb
+++ b/lib/arel/engines/sql/formatters.rb
@@ -47,9 +47,9 @@ module Arel
class WhereClause < PassThrough
end
- class OrderClause < PassThrough
- def attribute(attribute)
- "#{quote_table_name(name_for(attribute.original_relation))}.#{quote_column_name(attribute.name)}"
+ 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
diff --git a/lib/arel/engines/sql/primitives.rb b/lib/arel/engines/sql/primitives.rb
index 6f89723afe..9e9143ac0f 100644
--- a/lib/arel/engines/sql/primitives.rb
+++ b/lib/arel/engines/sql/primitives.rb
@@ -24,6 +24,20 @@ module Arel
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))
diff --git a/lib/arel/engines/sql/relations/utilities/compound.rb b/lib/arel/engines/sql/relations/utilities/compound.rb
index 502bf8b01e..61df196d6e 100644
--- a/lib/arel/engines/sql/relations/utilities/compound.rb
+++ b/lib/arel/engines/sql/relations/utilities/compound.rb
@@ -1,6 +1,6 @@
module Arel
class Compound < Relation
- delegate :table, :table_sql, :array, :to => :relation
+ delegate :table, :table_sql, :to => :relation
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
index 570f315892..63c15cfeff 100644
--- a/spec/arel/unit/relations/alias_spec.rb
+++ b/spec/arel/unit/relations/alias_spec.rb
@@ -30,7 +30,7 @@ module Arel
FROM `users`
WHERE `users`.`id` = 1
GROUP BY `users`.`id`
- ORDER BY `users`.`id`
+ ORDER BY `users`.`id` ASC
})
end
@@ -40,7 +40,7 @@ module Arel
FROM "users"
WHERE "users"."id" = 1
GROUP BY "users"."id"
- ORDER BY "users"."id"
+ ORDER BY "users"."id" ASC
})
end
end
diff --git a/spec/arel/unit/relations/array_spec.rb b/spec/arel/unit/relations/array_spec.rb
index c90843cd7d..d1c65c60a9 100644
--- a/spec/arel/unit/relations/array_spec.rb
+++ b/spec/arel/unit/relations/array_spec.rb
@@ -3,13 +3,18 @@ require File.join(File.dirname(__FILE__), '..', '..', '..', 'spec_helper')
module Arel
describe Array do
before do
- @relation = Array.new([[1], [2], [3]], [:id])
+ @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, :id),
+ Attribute.new(@relation, :name)
]
end
end
@@ -17,17 +22,65 @@ module Arel
describe '#call' do
it "manufactures an array of hashes of attributes to values" do
@relation.call.should == [
- { @relation[:id] => 1 },
- { @relation[:id] => 2 },
- { @relation[:id] => 3 }
+ { @relation[:id] => 1, @relation[:name] => 'duck' },
+ { @relation[:id] => 2, @relation[:name] => 'duck' },
+ { @relation[:id] => 3, @relation[:name] => 'goose' }
]
end
-
- it '' do
- @relation.where(@relation[:id].lt(3)).call.should == [
- { @relation[:id] => 1 },
- { @relation[:id] => 2 }
- ]
+
+ describe 'where' do
+ it 'filters the relation with the provided predicate' do
+ @relation.where(@relation[:id].lt(3)).call.should == [
+ { @relation[:id] => 1, @relation[:name] => 'duck' },
+ { @relation[:id] => 2, @relation[:name] => 'duck' }
+ ]
+ end
+ end
+
+ describe 'group' do
+ it 'sorts the relation with the provided ordering' do
+ end
+ end
+
+ describe 'order' do
+ it 'sorts the relation with the provided ordering' do
+ @relation.order(@relation[:id].desc).call.should == [
+ { @relation[:id] => 3, @relation[:name] => 'goose' },
+ { @relation[:id] => 2, @relation[:name] => 'duck' },
+ { @relation[:id] => 1, @relation[:name] => 'duck' }
+ ]
+ end
+ end
+
+ describe 'project' do
+ it 'projects' do
+ @relation.project(@relation[:id]).call.should == [
+ { @relation[:id] => 1 },
+ { @relation[:id] => 2 },
+ { @relation[:id] => 3 }
+ ]
+ end
+ end
+
+ describe 'skip' do
+ it 'slices' do
+ @relation.skip(1).call.should == [
+ { @relation[:id] => 2, @relation[:name] => 'duck' },
+ { @relation[:id] => 3, @relation[:name] => 'goose' }
+ ]
+ end
+ end
+
+ describe 'take' do
+ it 'dices' do
+ @relation.take(2).call.should == [
+ { @relation[:id] => 1, @relation[:name] => 'duck' },
+ { @relation[:id] => 2, @relation[:name] => 'duck' }
+ ]
+ end
+ end
+
+ describe 'join' do
end
end
end
diff --git a/spec/arel/unit/relations/order_spec.rb b/spec/arel/unit/relations/order_spec.rb
index 31014ddd39..cb0f1de84c 100644
--- a/spec/arel/unit/relations/order_spec.rb
+++ b/spec/arel/unit/relations/order_spec.rb
@@ -16,7 +16,7 @@ module Arel
sql.should be_like(%Q{
SELECT `users`.`id`, `users`.`name`
FROM `users`
- ORDER BY `users`.`id`
+ ORDER BY `users`.`id` ASC
})
end
@@ -24,7 +24,7 @@ module Arel
sql.should be_like(%Q{
SELECT "users"."id", "users"."name"
FROM "users"
- ORDER BY "users"."id"
+ ORDER BY "users"."id" ASC
})
end
end
@@ -42,7 +42,7 @@ module Arel
sql.should be_like(%Q{
SELECT `users`.`id`, `users`.`name`
FROM `users`
- ORDER BY `users`.`id`, `users`.`name`
+ ORDER BY `users`.`id` ASC, `users`.`name` ASC
})
end
@@ -50,7 +50,7 @@ module Arel
sql.should be_like(%Q{
SELECT "users"."id", "users"."name"
FROM "users"
- ORDER BY "users"."id", "users"."name"
+ ORDER BY "users"."id" ASC, "users"."name" ASC
})
end
end
@@ -95,7 +95,7 @@ module Arel
sql.should be_like(%Q{
SELECT `users`.`id`, `users`.`name`
FROM `users`
- ORDER BY `users`.`name`, `users`.`id`
+ ORDER BY `users`.`name` ASC, `users`.`id` ASC
})
end
@@ -103,7 +103,7 @@ module Arel
sql.should be_like(%Q{
SELECT "users"."id", "users"."name"
FROM "users"
- ORDER BY "users"."name", "users"."id"
+ ORDER BY "users"."name" ASC, "users"."id" ASC
})
end
end