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:
parent
7b445c6dd5
commit
2158d592c0
9 changed files with 106 additions and 5 deletions
|
@ -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:
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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
21
lib/arel/math.rb
Normal 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
|
|
@ -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'
|
||||
|
|
15
lib/arel/nodes/math_operation.rb
Normal file
15
lib/arel/nodes/math_operation.rb
Normal 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
|
|
@ -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
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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]
|
||||
|
|
Loading…
Reference in a new issue