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:
commit
8aa5d7a393
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).
|
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:
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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
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
|
|
@ -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'
|
||||||
|
|
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
|
|
@ -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
|
||||||
|
|
|
@ -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 = {
|
||||||
|
|
|
@ -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]
|
||||||
|
|
Loading…
Reference in a new issue