aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAaron Patterson <aaron.patterson@gmail.com>2011-03-07 08:30:09 -0800
committerAaron Patterson <aaron.patterson@gmail.com>2011-03-07 08:30:09 -0800
commit8aa5d7a393e0bbef8cd3ae9ecf64c2358b802b5f (patch)
tree65bf0d13754591494117283ac9b8c076cd5799ea
parent2644bcec7dbe3a65277b3a6a141853484171535a (diff)
parent2158d592c074813471baa8fa20044b683bb156e6 (diff)
downloadrails-8aa5d7a393e0bbef8cd3ae9ecf64c2358b802b5f.tar.gz
rails-8aa5d7a393e0bbef8cd3ae9ecf64c2358b802b5f.tar.bz2
rails-8aa5d7a393e0bbef8cd3ae9ecf64c2358b802b5f.zip
Merge remote branch 'stiff/master' into omg
* stiff/master: implemented support for math operations in numeric attributes
-rw-r--r--README.markdown17
-rw-r--r--lib/arel.rb1
-rw-r--r--lib/arel/attributes/attribute.rb10
-rw-r--r--lib/arel/math.rb21
-rw-r--r--lib/arel/nodes.rb1
-rw-r--r--lib/arel/nodes/math_operation.rb15
-rw-r--r--lib/arel/visitors/to_sql.rb16
-rw-r--r--test/support/fake_record.rb8
-rw-r--r--test/visitors/test_to_sql.rb22
9 files changed, 106 insertions, 5 deletions
diff --git a/README.markdown b/README.markdown
index b71879e4e3..4952ec6191 100644
--- a/README.markdown
+++ b/README.markdown
@@ -74,6 +74,23 @@ The `AND` operator behaves similarly.
The examples above are fairly simple and other libraries match or come close to matching the expressiveness of Arel (e.g., `Sequel` in Ruby).
+#### Inline math operations
+
+Suppose we have a table `products` with prices in different currencies. And we have a table currency_rates, of constantly changing currency rates. In Arel:
+
+ products = Arel::Table.new(:products)
+ products.columns # => [products[:id], products[:name], products[:price], products[:currency_id]]
+
+ currency_rates = Arel::Table.new(:currency_rates)
+ currency_rates.columns # => [currency_rates[:from_id], currency_rates[:to_id], currency_rates[:date], currency_rates[:rate]]
+
+Now, to order products by price in user preferred currency simply call:
+
+ products.
+ join(:currency_rates).on(products[:currency_id].eq(currency_rates[:from_id])).
+ where(currency_rates[:to_id].eq(user_preferred_currency), currency_rates[:date].eq(Date.today)).
+ order(products[:price] * currency_rates[:rate])
+
#### Complex Joins
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:
diff --git a/lib/arel.rb b/lib/arel.rb
index 32fc8d9bc0..de429f532e 100644
--- a/lib/arel.rb
+++ b/lib/arel.rb
@@ -3,6 +3,7 @@ require 'arel/factory_methods'
require 'arel/expressions'
require 'arel/predications'
+require 'arel/math'
require 'arel/table'
require 'arel/attributes'
require 'arel/compatibility/wheres'
diff --git a/lib/arel/attributes/attribute.rb b/lib/arel/attributes/attribute.rb
index 9a42e5a4da..bd3f4b58f1 100644
--- a/lib/arel/attributes/attribute.rb
+++ b/lib/arel/attributes/attribute.rb
@@ -5,12 +5,16 @@ module Arel
include Arel::Predications
end
+ class NumericAttribute < Attribute
+ include Arel::Math
+ end
+
class String < Attribute; end
class Time < Attribute; end
class Boolean < Attribute; end
- class Decimal < Attribute; end
- class Float < Attribute; end
- class Integer < Attribute; end
+ class Decimal < NumericAttribute; end
+ class Float < NumericAttribute; end
+ class Integer < NumericAttribute; end
class Undefined < Attribute; end
end
diff --git a/lib/arel/math.rb b/lib/arel/math.rb
new file mode 100644
index 0000000000..551b1f1010
--- /dev/null
+++ b/lib/arel/math.rb
@@ -0,0 +1,21 @@
+module Arel
+ module Math
+
+ def *(other)
+ Arel::Nodes::Multiplication.new(self, other)
+ end
+
+ def +(other)
+ Arel::Nodes::Addition.new(self, other)
+ end
+
+ def -(other)
+ Arel::Nodes::Subtraction.new(self, other)
+ end
+
+ def /(other)
+ Arel::Nodes::Division.new(self, other)
+ end
+
+ end
+end \ No newline at end of file
diff --git a/lib/arel/nodes.rb b/lib/arel/nodes.rb
index 442b313593..4b97e28668 100644
--- a/lib/arel/nodes.rb
+++ b/lib/arel/nodes.rb
@@ -18,6 +18,7 @@ require 'arel/nodes/join_source'
require 'arel/nodes/ordering'
require 'arel/nodes/delete_statement'
require 'arel/nodes/table_alias'
+require 'arel/nodes/math_operation'
# nary
require 'arel/nodes/and'
diff --git a/lib/arel/nodes/math_operation.rb b/lib/arel/nodes/math_operation.rb
new file mode 100644
index 0000000000..d9820f1ece
--- /dev/null
+++ b/lib/arel/nodes/math_operation.rb
@@ -0,0 +1,15 @@
+module Arel
+ module Nodes
+ class MathOperation < Binary
+ include Arel::Expressions
+ include Arel::Predications
+ include Arel::Math
+ end
+
+ class Multiplication < MathOperation; end
+ class Division < MathOperation; end
+ class Addition < MathOperation; end
+ class Subtraction < MathOperation; end
+
+ end
+end \ No newline at end of file
diff --git a/lib/arel/visitors/to_sql.rb b/lib/arel/visitors/to_sql.rb
index 70e10a5e0f..7eaaff8e13 100644
--- a/lib/arel/visitors/to_sql.rb
+++ b/lib/arel/visitors/to_sql.rb
@@ -373,6 +373,22 @@ key on UpdateManager using UpdateManager#key=
alias :visit_Time :quoted
alias :visit_TrueClass :quoted
+ def visit_Arel_Nodes_Multiplication o
+ "#{visit o.left} * #{visit o.right}"
+ end
+
+ def visit_Arel_Nodes_Division o
+ "#{visit o.left} / #{visit o.right}"
+ end
+
+ def visit_Arel_Nodes_Addition o
+ "(#{visit o.left} + #{visit o.right})"
+ end
+
+ def visit_Arel_Nodes_Subtraction o
+ "(#{visit o.left} - #{visit o.right})"
+ end
+
def visit_Array o
o.empty? ? 'NULL' : o.map { |x| visit x }.join(', ')
end
diff --git a/test/support/fake_record.rb b/test/support/fake_record.rb
index 54f73489c9..a0fa79d519 100644
--- a/test/support/fake_record.rb
+++ b/test/support/fake_record.rb
@@ -6,13 +6,17 @@ module FakeRecord
attr_reader :tables, :columns_hash
def initialize
- @tables = %w{ users photos developers }
+ @tables = %w{ users photos developers products}
@columns = {
'users' => [
Column.new('id', :integer),
Column.new('name', :string),
Column.new('bool', :boolean),
- Column.new('created_at', :date),
+ Column.new('created_at', :date)
+ ],
+ 'products' => [
+ Column.new('id', :integer),
+ Column.new('price', :decimal)
]
}
@columns_hash = {
diff --git a/test/visitors/test_to_sql.rb b/test/visitors/test_to_sql.rb
index c8ad40e242..3af316037a 100644
--- a/test/visitors/test_to_sql.rb
+++ b/test/visitors/test_to_sql.rb
@@ -194,6 +194,28 @@ module Arel
end
end
+ describe "Nodes::MathOperation" do
+ it "should handle Multiplication" do
+ node = Arel::Attributes::Decimal.new(Table.new(:products), :price) * Arel::Attributes::Decimal.new(Table.new(:currency_rates), :rate)
+ @visitor.accept(node).must_equal %("products"."price" * "currency_rates"."rate")
+ end
+
+ it "should handle Division" do
+ node = Arel::Attributes::Decimal.new(Table.new(:products), :price) / 5
+ @visitor.accept(node).must_equal %("products"."price" / 5)
+ end
+
+ it "should handle Addition" do
+ node = Arel::Attributes::Decimal.new(Table.new(:products), :price) + 6
+ @visitor.accept(node).must_equal %(("products"."price" + 6))
+ end
+
+ it "should handle Subtraction" do
+ node = Arel::Attributes::Decimal.new(Table.new(:products), :price) - 7
+ @visitor.accept(node).must_equal %(("products"."price" - 7))
+ end
+ end
+
describe "Nodes::NotIn" do
it "should know how to visit" do
node = @attr.not_in [1, 2, 3]