1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

implemented support for math operations in numeric attributes

This commit is contained in:
Vladimir Meremyanin 2011-01-29 14:40:39 +08:00
parent 7b445c6dd5
commit 2158d592c0
9 changed files with 106 additions and 5 deletions

View file

@ -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:

View file

@ -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'

View file

@ -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

21
lib/arel/math.rb Normal file
View file

@ -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

View file

@ -19,6 +19,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'

View file

@ -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

View file

@ -395,6 +395,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

View file

@ -6,13 +6,17 @@ module FakeRecord
attr_reader :tables
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)
]
}
@primary_keys = {

View file

@ -188,6 +188,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]