mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Add a bunch of specs for attribute type casting.
This commit is contained in:
parent
83c27c0b5e
commit
61916e408d
9 changed files with 435 additions and 63 deletions
|
@ -160,16 +160,13 @@ module Arel
|
||||||
def type_cast_to_numeric(value, method)
|
def type_cast_to_numeric(value, method)
|
||||||
return unless value
|
return unless value
|
||||||
if value.respond_to?(:to_str)
|
if value.respond_to?(:to_str)
|
||||||
if value.to_str =~ /\A(-?(?:0|[1-9]\d*)(?:\.\d+)?|(?:\.\d+))\z/
|
str = value.to_str.strip
|
||||||
$1.send(method)
|
return if str.empty?
|
||||||
else
|
return $1.send(method) if str =~ /\A(-?(?:0|[1-9]\d*)(?:\.\d+)?|(?:\.\d+))\z/
|
||||||
value
|
|
||||||
end
|
|
||||||
elsif value.respond_to?(method)
|
elsif value.respond_to?(method)
|
||||||
value.send(method)
|
return value.send(method)
|
||||||
else
|
|
||||||
raise typecast_error(value)
|
|
||||||
end
|
end
|
||||||
|
raise typecast_error(value)
|
||||||
end
|
end
|
||||||
|
|
||||||
def typecast_error(value)
|
def typecast_error(value)
|
||||||
|
|
|
@ -3,11 +3,12 @@ module Arel
|
||||||
class Boolean < Attribute
|
class Boolean < Attribute
|
||||||
def type_cast(value)
|
def type_cast(value)
|
||||||
case value
|
case value
|
||||||
when true, false then value
|
when true, false then value
|
||||||
when nil then options[:allow_nil] ? nil : false
|
# when nil then options[:allow_nil] ? nil : false
|
||||||
when 1 then true
|
when nil then false
|
||||||
when 0 then false
|
when 1 then true
|
||||||
else
|
when 0 then false
|
||||||
|
else
|
||||||
case value.to_s.downcase.strip
|
case value.to_s.downcase.strip
|
||||||
when 'true' then true
|
when 'true' then true
|
||||||
when 'false' then false
|
when 'false' then false
|
||||||
|
|
|
@ -1,50 +1,5 @@
|
||||||
require 'spec_helper'
|
require 'spec_helper'
|
||||||
|
|
||||||
module Arel
|
|
||||||
module Testing
|
|
||||||
class Engine
|
|
||||||
attr_reader :rows
|
|
||||||
|
|
||||||
def initialize
|
|
||||||
@rows = []
|
|
||||||
end
|
|
||||||
|
|
||||||
def supports(operation)
|
|
||||||
false
|
|
||||||
end
|
|
||||||
|
|
||||||
def read(relation)
|
|
||||||
@rows.dup.map { |r| Row.new(relation, r) }
|
|
||||||
end
|
|
||||||
|
|
||||||
def create(insert)
|
|
||||||
@rows << insert.record.tuple
|
|
||||||
insert
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Thing < Arel::Relation
|
|
||||||
attr_reader :engine, :attributes
|
|
||||||
|
|
||||||
def initialize(engine, attributes)
|
|
||||||
@engine, @attributes = engine, []
|
|
||||||
attributes.each do |name, type|
|
|
||||||
@attributes << type.new(self, name)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def format(attribute, value)
|
|
||||||
value
|
|
||||||
end
|
|
||||||
|
|
||||||
def insert(row)
|
|
||||||
insert = super Arel::Row.new(self, row)
|
|
||||||
insert.record
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def have_rows(expected)
|
def have_rows(expected)
|
||||||
simple_matcher "have rows" do |given, matcher|
|
simple_matcher "have rows" do |given, matcher|
|
||||||
found, got, expected = [], [], expected.map { |r| r.tuple }
|
found, got, expected = [], [], expected.map { |r| r.tuple }
|
||||||
|
@ -101,11 +56,14 @@ module Arel
|
||||||
describe "Relation" do
|
describe "Relation" do
|
||||||
|
|
||||||
before :all do
|
before :all do
|
||||||
@engine = Testing::Engine.new
|
@engine = Testing::Engine.new
|
||||||
@relation = Thing.new(@engine,
|
@relation = Model.build do |r|
|
||||||
:id => Attributes::Integer,
|
r.engine @engine
|
||||||
:name => Attributes::String,
|
|
||||||
:age => Attributes::Integer)
|
r.attribute :id, Attributes::Integer
|
||||||
|
r.attribute :name, Attributes::String
|
||||||
|
r.attribute :age, Attributes::Integer
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
describe "..." do
|
describe "..." do
|
||||||
|
|
57
spec/attributes/boolean_spec.rb
Normal file
57
spec/attributes/boolean_spec.rb
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
|
||||||
|
module Arel
|
||||||
|
describe "Attributes::Boolean" do
|
||||||
|
|
||||||
|
before :all do
|
||||||
|
@relation = Model.build do |r|
|
||||||
|
r.engine Testing::Engine.new
|
||||||
|
r.attribute :awesome, Attributes::Boolean
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def type_cast(val)
|
||||||
|
@relation[:awesome].type_cast(val)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#type_cast" do
|
||||||
|
it "returns same value if passed a boolean" do
|
||||||
|
val = true
|
||||||
|
type_cast(val).should eql(val)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns boolean representation (false) of nil" do
|
||||||
|
type_cast(nil).should eql(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns boolean representation of 'true', 'false'" do
|
||||||
|
type_cast('true').should eql(true)
|
||||||
|
type_cast('false').should eql(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns boolean representation of :true, :false" do
|
||||||
|
type_cast(:true).should eql(true)
|
||||||
|
type_cast(:false).should eql(false)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns boolean representation of 0, 1" do
|
||||||
|
type_cast(1).should == true
|
||||||
|
type_cast(0).should == false
|
||||||
|
end
|
||||||
|
|
||||||
|
it "calls #to_s on arbitrary objects" do
|
||||||
|
obj = Object.new
|
||||||
|
obj.extend Module.new { def to_s ; 'true' ; end }
|
||||||
|
type_cast(obj).should == true
|
||||||
|
end
|
||||||
|
|
||||||
|
[ Object.new, 'string', '00.0', 5 ].each do |value|
|
||||||
|
it "raises exception when attempting type_cast of non-boolean value #{value.inspect}" do
|
||||||
|
lambda do
|
||||||
|
type_cast(value)
|
||||||
|
end.should raise_error(TypecastError, /could not typecast/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
119
spec/attributes/float_spec.rb
Normal file
119
spec/attributes/float_spec.rb
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
require 'bigdecimal'
|
||||||
|
|
||||||
|
module Arel
|
||||||
|
describe "Attributes::Float" do
|
||||||
|
|
||||||
|
before :all do
|
||||||
|
@relation = Model.build do |r|
|
||||||
|
r.engine Testing::Engine.new
|
||||||
|
r.attribute :percentage, Attributes::Float
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def type_cast(val)
|
||||||
|
@relation[:percentage].type_cast(val)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#type_cast" do
|
||||||
|
it "returns same value if an float" do
|
||||||
|
type_cast(24.01).should eql(24.01)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns nil if passed nil" do
|
||||||
|
type_cast(nil).should be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns nil if passed empty string" do
|
||||||
|
type_cast('').should be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns float representation of a zero string float" do
|
||||||
|
type_cast('0').should eql(0.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns float representation of a positive string integer" do
|
||||||
|
type_cast('24').should eql(24.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns float representation of a positive string integer with spaces" do
|
||||||
|
type_cast(' 24').should eql(24.0)
|
||||||
|
type_cast('24 ').should eql(24.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns float representation of a negative string float" do
|
||||||
|
type_cast('-24.23').should eql(-24.23)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns float representation of a negative string integer with spaces" do
|
||||||
|
type_cast('-24 ').should eql(-24.0)
|
||||||
|
type_cast(' -24').should eql(-24.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a zero string float" do
|
||||||
|
type_cast('0.0').should eql(0.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a positive string float" do
|
||||||
|
type_cast('24.35').should eql(24.35)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a positive string float with spaces" do
|
||||||
|
type_cast(' 24.35').should eql(24.35)
|
||||||
|
type_cast('24.35 ').should eql(24.35)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a negative string float" do
|
||||||
|
type_cast('-24.35').should eql(-24.35)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a negative string float with spaces" do
|
||||||
|
type_cast(' -24.35 ').should eql(-24.35)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a zero string float, with no leading digits" do
|
||||||
|
type_cast('.0').should eql(0.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a zero string float, with no leading digits with spaces" do
|
||||||
|
type_cast(' .0').should eql(0.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a positive string float, with no leading digits" do
|
||||||
|
type_cast('.41').should eql(0.41)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a zero float" do
|
||||||
|
type_cast(0.0).should eql(0.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a positive float" do
|
||||||
|
type_cast(24.35).should eql(24.35)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a negative float" do
|
||||||
|
type_cast(-24.35).should eql(-24.35)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a zero decimal" do
|
||||||
|
type_cast(BigDecimal('0.0')).should eql(0.0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a positive decimal" do
|
||||||
|
type_cast(BigDecimal('24.35')).should eql(24.35)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a negative decimal" do
|
||||||
|
type_cast(BigDecimal('-24.35')).should eql(-24.35)
|
||||||
|
end
|
||||||
|
|
||||||
|
[ Object.new, true, '00.0', '0.', 'string' ].each do |value|
|
||||||
|
it "raises exception when attempting type_cast of non-numeric value #{value.inspect}" do
|
||||||
|
lambda do
|
||||||
|
type_cast(value)
|
||||||
|
end.should raise_error(TypecastError, /could not typecast/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
119
spec/attributes/integer_spec.rb
Normal file
119
spec/attributes/integer_spec.rb
Normal file
|
@ -0,0 +1,119 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
require 'bigdecimal'
|
||||||
|
|
||||||
|
module Arel
|
||||||
|
describe "Attributes::Integer" do
|
||||||
|
|
||||||
|
before :all do
|
||||||
|
@relation = Model.build do |r|
|
||||||
|
r.engine Testing::Engine.new
|
||||||
|
r.attribute :age, Attributes::Integer
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def type_cast(val)
|
||||||
|
@relation[:age].type_cast(val)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#type_cast" do
|
||||||
|
it "returns same value if an integer" do
|
||||||
|
type_cast(24).should eql(24)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns nil if passed nil" do
|
||||||
|
type_cast(nil).should be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns nil if passed empty string" do
|
||||||
|
type_cast('').should be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a zero string integer" do
|
||||||
|
type_cast('0').should eql(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a positive string integer" do
|
||||||
|
type_cast('24').should eql(24)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a positive string integer with spaces" do
|
||||||
|
type_cast(' 24').should eql(24)
|
||||||
|
type_cast('24 ').should eql(24)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a negative string integer" do
|
||||||
|
type_cast('-24').should eql(-24)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a negative string integer with spaces" do
|
||||||
|
type_cast('-24 ').should eql(-24)
|
||||||
|
type_cast(' -24').should eql(-24)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a zero string float" do
|
||||||
|
type_cast('0.0').should eql(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a positive string float" do
|
||||||
|
type_cast('24.35').should eql(24)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a positive string float with spaces" do
|
||||||
|
type_cast(' 24.35').should eql(24)
|
||||||
|
type_cast('24.35 ').should eql(24)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a negative string float" do
|
||||||
|
type_cast('-24.35').should eql(-24)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a negative string float with spaces" do
|
||||||
|
type_cast(' -24.35 ').should eql(-24)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a zero string float, with no leading digits" do
|
||||||
|
type_cast('.0').should eql(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a zero string float, with no leading digits with spaces" do
|
||||||
|
type_cast(' .0').should eql(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a positive string float, with no leading digits" do
|
||||||
|
type_cast('.41').should eql(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a zero float" do
|
||||||
|
type_cast(0.0).should eql(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a positive float" do
|
||||||
|
type_cast(24.35).should eql(24)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a negative float" do
|
||||||
|
type_cast(-24.35).should eql(-24)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a zero decimal" do
|
||||||
|
type_cast(BigDecimal('0.0')).should eql(0)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a positive decimal" do
|
||||||
|
type_cast(BigDecimal('24.35')).should eql(24)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns integer representation of a negative decimal" do
|
||||||
|
type_cast(BigDecimal('-24.35')).should eql(-24)
|
||||||
|
end
|
||||||
|
|
||||||
|
[ Object.new, true, '00.0', '0.', 'string' ].each do |value|
|
||||||
|
it "raises exception when attempting type_cast of non-numeric value #{value.inspect}" do
|
||||||
|
lambda do
|
||||||
|
type_cast(value)
|
||||||
|
end.should raise_error(TypecastError, /could not typecast/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
43
spec/attributes/string_spec.rb
Normal file
43
spec/attributes/string_spec.rb
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
require 'bigdecimal'
|
||||||
|
|
||||||
|
module Arel
|
||||||
|
describe "Attributes::String" do
|
||||||
|
|
||||||
|
before :all do
|
||||||
|
@relation = Model.build do |r|
|
||||||
|
r.engine Testing::Engine.new
|
||||||
|
r.attribute :name, Attributes::String
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def type_cast(val)
|
||||||
|
@relation[:name].type_cast(val)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#type_cast" do
|
||||||
|
it "returns same value if passed a String" do
|
||||||
|
val = "hell"
|
||||||
|
type_cast(val).should eql(val)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns nil if passed nil" do
|
||||||
|
type_cast(nil).should be_nil
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns String representation of Symbol" do
|
||||||
|
type_cast(:hello).should == "hello"
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns string representation of Integer" do
|
||||||
|
type_cast(1).should == '1'
|
||||||
|
end
|
||||||
|
|
||||||
|
it "calls #to_s on arbitrary objects" do
|
||||||
|
obj = Object.new
|
||||||
|
obj.extend Module.new { def to_s ; 'hello' ; end }
|
||||||
|
type_cast(obj).should == 'hello'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
22
spec/attributes/time_spec.rb
Normal file
22
spec/attributes/time_spec.rb
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
require 'spec_helper'
|
||||||
|
require 'bigdecimal'
|
||||||
|
|
||||||
|
module Arel
|
||||||
|
describe "Attributes::Time" do
|
||||||
|
|
||||||
|
before :all do
|
||||||
|
@relation = Model.build do |r|
|
||||||
|
r.engine Testing::Engine.new
|
||||||
|
r.attribute :created_at, Attributes::Time
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def type_cast(val)
|
||||||
|
@relation[:created_at].type_cast(val)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "#type_cast" do
|
||||||
|
it "works"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
56
spec/support/model.rb
Normal file
56
spec/support/model.rb
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
module Arel
|
||||||
|
module Testing
|
||||||
|
class Engine
|
||||||
|
attr_reader :rows
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@rows = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def supports(operation)
|
||||||
|
false
|
||||||
|
end
|
||||||
|
|
||||||
|
def read(relation)
|
||||||
|
@rows.dup.map { |r| Row.new(relation, r) }
|
||||||
|
end
|
||||||
|
|
||||||
|
def create(insert)
|
||||||
|
@rows << insert.record.tuple
|
||||||
|
insert
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
class Model < Relation
|
||||||
|
attr_reader :engine, :attributes
|
||||||
|
|
||||||
|
def self.build
|
||||||
|
relation = new
|
||||||
|
yield relation
|
||||||
|
relation
|
||||||
|
end
|
||||||
|
|
||||||
|
def initialize
|
||||||
|
@attributes = []
|
||||||
|
end
|
||||||
|
|
||||||
|
def engine(engine = nil)
|
||||||
|
@engine = engine if engine
|
||||||
|
@engine
|
||||||
|
end
|
||||||
|
|
||||||
|
def attribute(name, type)
|
||||||
|
@attributes << type.new(self, name)
|
||||||
|
end
|
||||||
|
|
||||||
|
def format(attribute, value)
|
||||||
|
value
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert(row)
|
||||||
|
insert = super Arel::Row.new(self, row)
|
||||||
|
insert.record
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Loading…
Reference in a new issue