aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--History.txt2
-rw-r--r--README.markdown17
-rw-r--r--arel.gemspec6
-rw-r--r--lib/arel.rb1
-rw-r--r--lib/arel/attributes/attribute.rb1
-rw-r--r--lib/arel/math.rb19
-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.rb14
-rw-r--r--test/visitors/test_to_sql.rb22
11 files changed, 107 insertions, 7 deletions
diff --git a/History.txt b/History.txt
index fac98381a6..a5f630b021 100644
--- a/History.txt
+++ b/History.txt
@@ -9,6 +9,8 @@
* Add Arel::SelectManager#limit=
* Add Arel::SelectManager#offset
* Add Arel::SelectManager#offset=
+ * Math operations have been added to attributes, thanks to
+ Vladimir Meremyanin.
* Bug fixes
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/arel.gemspec b/arel.gemspec
index 16ceb11c05..58cb8f61fb 100644
--- a/arel.gemspec
+++ b/arel.gemspec
@@ -2,15 +2,15 @@
Gem::Specification.new do |s|
s.name = %q{arel}
- s.version = "2.0.7.beta.20110224095102"
+ s.version = "2.0.7.beta.20110228092631"
s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
s.authors = ["Aaron Patterson", "Bryan Halmkamp", "Emilio Tagua", "Nick Kallen"]
- s.date = %q{2011-02-24}
+ s.date = %q{2011-02-28}
s.description = %q{Arel is a Relational Algebra for Ruby. It 1) simplifies the generation complex of SQL queries and it 2) adapts to various RDBMS systems. It is intended to be a framework framework; that is, you can build your own ORM with it, focusing on innovative object and collection modeling as opposed to database compatibility and query generation.}
s.email = ["aaron@tenderlovemaking.com", "bryan@brynary.com", "miloops@gmail.com", "nick@example.org"]
s.extra_rdoc_files = ["History.txt", "MIT-LICENSE.txt", "Manifest.txt", "README.markdown"]
- s.files = [".autotest", "History.txt", "MIT-LICENSE.txt", "Manifest.txt", "README.markdown", "Rakefile", "arel.gemspec", "lib/arel.rb", "lib/arel/attributes.rb", "lib/arel/attributes/attribute.rb", "lib/arel/compatibility/wheres.rb", "lib/arel/crud.rb", "lib/arel/delete_manager.rb", "lib/arel/deprecated.rb", "lib/arel/expression.rb", "lib/arel/expressions.rb", "lib/arel/factory_methods.rb", "lib/arel/insert_manager.rb", "lib/arel/nodes.rb", "lib/arel/nodes/and.rb", "lib/arel/nodes/binary.rb", "lib/arel/nodes/count.rb", "lib/arel/nodes/delete_statement.rb", "lib/arel/nodes/equality.rb", "lib/arel/nodes/function.rb", "lib/arel/nodes/in.rb", "lib/arel/nodes/inner_join.rb", "lib/arel/nodes/insert_statement.rb", "lib/arel/nodes/join_source.rb", "lib/arel/nodes/named_function.rb", "lib/arel/nodes/node.rb", "lib/arel/nodes/ordering.rb", "lib/arel/nodes/outer_join.rb", "lib/arel/nodes/select_core.rb", "lib/arel/nodes/select_statement.rb", "lib/arel/nodes/sql_literal.rb", "lib/arel/nodes/string_join.rb", "lib/arel/nodes/table_alias.rb", "lib/arel/nodes/unary.rb", "lib/arel/nodes/unqualified_column.rb", "lib/arel/nodes/update_statement.rb", "lib/arel/nodes/values.rb", "lib/arel/nodes/with.rb", "lib/arel/predications.rb", "lib/arel/relation.rb", "lib/arel/select_manager.rb", "lib/arel/sql/engine.rb", "lib/arel/sql_literal.rb", "lib/arel/table.rb", "lib/arel/tree_manager.rb", "lib/arel/update_manager.rb", "lib/arel/visitors.rb", "lib/arel/visitors/depth_first.rb", "lib/arel/visitors/dot.rb", "lib/arel/visitors/join_sql.rb", "lib/arel/visitors/mssql.rb", "lib/arel/visitors/mysql.rb", "lib/arel/visitors/oracle.rb", "lib/arel/visitors/order_clauses.rb", "lib/arel/visitors/postgresql.rb", "lib/arel/visitors/sqlite.rb", "lib/arel/visitors/to_sql.rb", "lib/arel/visitors/visitor.rb", "lib/arel/visitors/where_sql.rb", "test/attributes/test_attribute.rb", "test/helper.rb", "test/nodes/test_as.rb", "test/nodes/test_count.rb", "test/nodes/test_delete_statement.rb", "test/nodes/test_equality.rb", "test/nodes/test_insert_statement.rb", "test/nodes/test_named_function.rb", "test/nodes/test_node.rb", "test/nodes/test_not.rb", "test/nodes/test_or.rb", "test/nodes/test_select_core.rb", "test/nodes/test_select_statement.rb", "test/nodes/test_sql_literal.rb", "test/nodes/test_sum.rb", "test/nodes/test_update_statement.rb", "test/support/fake_record.rb", "test/test_activerecord_compat.rb", "test/test_attributes.rb", "test/test_crud.rb", "test/test_delete_manager.rb", "test/test_factory_methods.rb", "test/test_insert_manager.rb", "test/test_select_manager.rb", "test/test_table.rb", "test/test_update_manager.rb", "test/visitors/test_depth_first.rb", "test/visitors/test_dot.rb", "test/visitors/test_join_sql.rb", "test/visitors/test_mssql.rb", "test/visitors/test_mysql.rb", "test/visitors/test_oracle.rb", "test/visitors/test_postgres.rb", "test/visitors/test_sqlite.rb", "test/visitors/test_to_sql.rb", ".gemtest"]
+ s.files = [".autotest", "History.txt", "MIT-LICENSE.txt", "Manifest.txt", "README.markdown", "Rakefile", "arel.gemspec", "lib/arel.rb", "lib/arel/attributes.rb", "lib/arel/attributes/attribute.rb", "lib/arel/compatibility/wheres.rb", "lib/arel/crud.rb", "lib/arel/delete_manager.rb", "lib/arel/deprecated.rb", "lib/arel/expression.rb", "lib/arel/expressions.rb", "lib/arel/factory_methods.rb", "lib/arel/insert_manager.rb", "lib/arel/nodes.rb", "lib/arel/nodes/and.rb", "lib/arel/nodes/binary.rb", "lib/arel/nodes/count.rb", "lib/arel/nodes/delete_statement.rb", "lib/arel/nodes/equality.rb", "lib/arel/nodes/function.rb", "lib/arel/nodes/in.rb", "lib/arel/nodes/inner_join.rb", "lib/arel/nodes/insert_statement.rb", "lib/arel/nodes/join_source.rb", "lib/arel/nodes/named_function.rb", "lib/arel/nodes/node.rb", "lib/arel/nodes/ordering.rb", "lib/arel/nodes/outer_join.rb", "lib/arel/nodes/select_core.rb", "lib/arel/nodes/select_statement.rb", "lib/arel/nodes/sql_literal.rb", "lib/arel/nodes/string_join.rb", "lib/arel/nodes/table_alias.rb", "lib/arel/nodes/unary.rb", "lib/arel/nodes/unqualified_column.rb", "lib/arel/nodes/update_statement.rb", "lib/arel/nodes/values.rb", "lib/arel/nodes/with.rb", "lib/arel/predications.rb", "lib/arel/relation.rb", "lib/arel/select_manager.rb", "lib/arel/sql/engine.rb", "lib/arel/sql_literal.rb", "lib/arel/table.rb", "lib/arel/tree_manager.rb", "lib/arel/update_manager.rb", "lib/arel/visitors.rb", "lib/arel/visitors/depth_first.rb", "lib/arel/visitors/dot.rb", "lib/arel/visitors/join_sql.rb", "lib/arel/visitors/mssql.rb", "lib/arel/visitors/mysql.rb", "lib/arel/visitors/oracle.rb", "lib/arel/visitors/order_clauses.rb", "lib/arel/visitors/postgresql.rb", "lib/arel/visitors/sqlite.rb", "lib/arel/visitors/to_sql.rb", "lib/arel/visitors/visitor.rb", "lib/arel/visitors/where_sql.rb", "test/attributes/test_attribute.rb", "test/helper.rb", "test/nodes/test_as.rb", "test/nodes/test_count.rb", "test/nodes/test_delete_statement.rb", "test/nodes/test_equality.rb", "test/nodes/test_insert_statement.rb", "test/nodes/test_named_function.rb", "test/nodes/test_node.rb", "test/nodes/test_not.rb", "test/nodes/test_or.rb", "test/nodes/test_select_core.rb", "test/nodes/test_select_statement.rb", "test/nodes/test_sql_literal.rb", "test/nodes/test_sum.rb", "test/nodes/test_update_statement.rb", "test/support/fake_record.rb", "test/test_activerecord_compat.rb", "test/test_attributes.rb", "test/test_crud.rb", "test/test_delete_manager.rb", "test/test_factory_methods.rb", "test/test_insert_manager.rb", "test/test_select_manager.rb", "test/test_table.rb", "test/test_update_manager.rb", "test/visitors/test_depth_first.rb", "test/visitors/test_dot.rb", "test/visitors/test_join_sql.rb", "test/visitors/test_mssql.rb", "test/visitors/test_mysql.rb", "test/visitors/test_oracle.rb", "test/visitors/test_postgres.rb", "test/visitors/test_sqlite.rb", "test/visitors/test_to_sql.rb"]
s.homepage = %q{http://github.com/rails/arel}
s.rdoc_options = ["--main", "README.markdown"]
s.require_paths = ["lib"]
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..5aea87ac43 100644
--- a/lib/arel/attributes/attribute.rb
+++ b/lib/arel/attributes/attribute.rb
@@ -3,6 +3,7 @@ module Arel
class Attribute < Struct.new :relation, :name
include Arel::Expressions
include Arel::Predications
+ include Arel::Math
end
class String < Attribute; end
diff --git a/lib/arel/math.rb b/lib/arel/math.rb
new file mode 100644
index 0000000000..b7c2419233
--- /dev/null
+++ b/lib/arel/math.rb
@@ -0,0 +1,19 @@
+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
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 f76c1491ee..f30557e509 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..babf5fa25b 100644
--- a/test/support/fake_record.rb
+++ b/test/support/fake_record.rb
@@ -6,20 +6,26 @@ 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 = {
- 'users' => Hash[@columns['users'].map { |x| [x.name, x] }]
+ 'users' => Hash[@columns['users'].map { |x| [x.name, x] }],
+ 'products' => Hash[@columns['products'].map { |x| [x.name, x] }]
}
@primary_keys = {
- 'users' => 'id'
+ 'users' => 'id',
+ 'products' => 'id'
}
end
diff --git a/test/visitors/test_to_sql.rb b/test/visitors/test_to_sql.rb
index 2d5549ca43..c47fd57a28 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]