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

Merge remote branch 'stiff/master' into omg

* stiff/master:
  implemented support for math operations in numeric attributes
This commit is contained in:
Aaron Patterson 2011-03-07 08:30:09 -08:00
commit 8aa5d7a393
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). 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 #### 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: 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/expressions'
require 'arel/predications' require 'arel/predications'
require 'arel/math'
require 'arel/table' require 'arel/table'
require 'arel/attributes' require 'arel/attributes'
require 'arel/compatibility/wheres' require 'arel/compatibility/wheres'

View file

@ -5,12 +5,16 @@ module Arel
include Arel::Predications include Arel::Predications
end end
class NumericAttribute < Attribute
include Arel::Math
end
class String < Attribute; end class String < Attribute; end
class Time < Attribute; end class Time < Attribute; end
class Boolean < Attribute; end class Boolean < Attribute; end
class Decimal < Attribute; end class Decimal < NumericAttribute; end
class Float < Attribute; end class Float < NumericAttribute; end
class Integer < Attribute; end class Integer < NumericAttribute; end
class Undefined < Attribute; end class Undefined < Attribute; end
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

@ -18,6 +18,7 @@ require 'arel/nodes/join_source'
require 'arel/nodes/ordering' require 'arel/nodes/ordering'
require 'arel/nodes/delete_statement' require 'arel/nodes/delete_statement'
require 'arel/nodes/table_alias' require 'arel/nodes/table_alias'
require 'arel/nodes/math_operation'
# nary # nary
require 'arel/nodes/and' 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

@ -373,6 +373,22 @@ key on UpdateManager using UpdateManager#key=
alias :visit_Time :quoted alias :visit_Time :quoted
alias :visit_TrueClass :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 def visit_Array o
o.empty? ? 'NULL' : o.map { |x| visit x }.join(', ') o.empty? ? 'NULL' : o.map { |x| visit x }.join(', ')
end end

View file

@ -6,13 +6,17 @@ module FakeRecord
attr_reader :tables, :columns_hash attr_reader :tables, :columns_hash
def initialize def initialize
@tables = %w{ users photos developers } @tables = %w{ users photos developers products}
@columns = { @columns = {
'users' => [ 'users' => [
Column.new('id', :integer), Column.new('id', :integer),
Column.new('name', :string), Column.new('name', :string),
Column.new('bool', :boolean), 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 = { @columns_hash = {

View file

@ -194,6 +194,28 @@ module Arel
end end
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 describe "Nodes::NotIn" do
it "should know how to visit" do it "should know how to visit" do
node = @attr.not_in [1, 2, 3] node = @attr.not_in [1, 2, 3]