virtus 0.5.3 → 0.5.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog.md +10 -1
- data/Gemfile.devtools +2 -2
- data/lib/virtus/attribute.rb +3 -1
- data/lib/virtus/attribute/hash.rb +6 -10
- data/lib/virtus/coercion/float.rb +15 -0
- data/lib/virtus/coercion/integer.rb +23 -0
- data/lib/virtus/coercion/string.rb +1 -1
- data/lib/virtus/version.rb +1 -1
- data/spec/unit/virtus/attribute/class_methods/determine_type_spec.rb +42 -0
- data/spec/unit/virtus/attribute/date_time/coerce_spec.rb +12 -0
- data/spec/unit/virtus/attribute/decimal/coerce_spec.rb +7 -1
- data/spec/unit/virtus/attribute/float/coerce_spec.rb +7 -1
- data/spec/unit/virtus/attribute/hash/coerce_spec.rb +8 -0
- data/spec/unit/virtus/attribute/integer/coerce_spec.rb +7 -1
- data/virtus.gemspec +0 -3
- metadata +2 -34
data/Changelog.md
CHANGED
@@ -1,4 +1,13 @@
|
|
1
|
-
# v0.5.
|
1
|
+
# v0.5.4 2012-12-20
|
2
|
+
|
3
|
+
* [feature] Allow *any* enumerable to be a collection attribute (aptinio)
|
4
|
+
* [feature] Add Integer.to_datetime and Float.to_datetime coercion (brutuscat)
|
5
|
+
* [fixed] Fixed a regression with Hash attribute introduced by key/member coercion (shingara)
|
6
|
+
* [fixed] Change leading non-significant digit type coercion to be coerced (maskact)
|
7
|
+
|
8
|
+
[Compare v0.5.3..v0.5.4](https://github.com/solnic/virtus/compare/v0.5.3...v0.5.4)
|
9
|
+
|
10
|
+
# v0.5.3 2012-12-13
|
2
11
|
|
3
12
|
* [feature] Added Hash member type coercion [example](https://github.com/solnic/virtus#hash-attributes-coercion) (greyblake)
|
4
13
|
* [fixed] Fixed issues with String=>Integer coercion and e-notation (greyblake)
|
data/Gemfile.devtools
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
group :development do
|
2
|
-
gem 'rake', '~> 0
|
2
|
+
gem 'rake', '~> 10.0'
|
3
3
|
gem 'rspec', '~> 2.12.0'
|
4
4
|
gem 'yard', '~> 0.8.3'
|
5
5
|
end
|
@@ -27,7 +27,7 @@ group :metrics do
|
|
27
27
|
gem 'reek', '~> 1.2.8', :git => 'https://github.com/dkubb/reek.git'
|
28
28
|
gem 'roodi', '~> 2.1.0'
|
29
29
|
gem 'yardstick', '~> 0.8.0'
|
30
|
-
gem 'mutant', '~> 0.2.
|
30
|
+
gem 'mutant', '~> 0.2.6'
|
31
31
|
|
32
32
|
platforms :ruby_18, :ruby_19 do
|
33
33
|
# this indirectly depends on ffi which does not build on ruby-head
|
data/lib/virtus/attribute.rb
CHANGED
@@ -75,12 +75,10 @@ module Virtus
|
|
75
75
|
# @api private
|
76
76
|
def initialize(*)
|
77
77
|
super
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
@value_type_instance = Attribute.build(@name, @value_type)
|
83
|
-
end
|
78
|
+
@key_type = @options[:key_type] || Object
|
79
|
+
@value_type = @options[:value_type] || Object
|
80
|
+
@key_type_instance = Attribute.build(@name, @key_type)
|
81
|
+
@value_type_instance = Attribute.build(@name, @value_type)
|
84
82
|
end
|
85
83
|
|
86
84
|
# Coerce a hash with keys and values
|
@@ -93,10 +91,8 @@ module Virtus
|
|
93
91
|
def coerce(value)
|
94
92
|
coerced = super
|
95
93
|
return coerced unless coerced.respond_to?(:each_with_object)
|
96
|
-
coerced.each_with_object({}) do |
|
97
|
-
key
|
98
|
-
value = @value_type_instance.coerce(key_and_value[1])
|
99
|
-
hash[key] = value
|
94
|
+
coerced.each_with_object({}) do |(key, value), hash|
|
95
|
+
hash[@key_type_instance.coerce(key)] = @value_type_instance.coerce(value)
|
100
96
|
end
|
101
97
|
end
|
102
98
|
|
@@ -19,6 +19,21 @@ module Virtus
|
|
19
19
|
value
|
20
20
|
end
|
21
21
|
|
22
|
+
# Coerce given value to a DateTime
|
23
|
+
#
|
24
|
+
# @example
|
25
|
+
# datetime = Virtus::Coercion::Float.to_datetime(1000000000.999) # => Sun, 09 Sep 2001 01:46:40 +0000
|
26
|
+
# datetime.to_f # => 1000000000.999
|
27
|
+
#
|
28
|
+
# @param [Float] value
|
29
|
+
#
|
30
|
+
# @return [DateTime]
|
31
|
+
#
|
32
|
+
# @api public
|
33
|
+
def self.to_datetime(value)
|
34
|
+
::DateTime.strptime((value * 10**3).to_s, "%Q")
|
35
|
+
end
|
36
|
+
|
22
37
|
end # class Float
|
23
38
|
end # class Coercion
|
24
39
|
end # module Virtus
|
@@ -55,6 +55,29 @@ module Virtus
|
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
|
+
# Coerce given value to a DateTime
|
59
|
+
#
|
60
|
+
# @example
|
61
|
+
# Virtus::Coercion::Fixnum.to_datetime(0) # => Thu, 01 Jan 1970 00:00:00 +0000
|
62
|
+
#
|
63
|
+
# @param [Integer] value
|
64
|
+
#
|
65
|
+
# @return [DateTime]
|
66
|
+
#
|
67
|
+
# @api public
|
68
|
+
def self.to_datetime(value)
|
69
|
+
# FIXME: Remove after Rubinius 2.0 is released
|
70
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'rbx'
|
71
|
+
datetime_format = '%Q'
|
72
|
+
value = "#{value * 10**3}"
|
73
|
+
else
|
74
|
+
datetime_format = '%s'
|
75
|
+
value = "#{value}"
|
76
|
+
end
|
77
|
+
|
78
|
+
::DateTime.strptime(value, datetime_format)
|
79
|
+
end
|
80
|
+
|
58
81
|
end # class Fixnum
|
59
82
|
end # class Coercion
|
60
83
|
end # module Virtus
|
@@ -9,7 +9,7 @@ module Virtus
|
|
9
9
|
FALSE_VALUES = %w[ 0 off f false n no ].freeze
|
10
10
|
BOOLEAN_MAP = ::Hash[ TRUE_VALUES.product([ true ]) + FALSE_VALUES.product([ false ]) ].freeze
|
11
11
|
|
12
|
-
INTEGER_REGEXP = /[-+]?(?:0
|
12
|
+
INTEGER_REGEXP = /[-+]?(?:[0-9]\d*)/.freeze
|
13
13
|
EXPONENT_REGEXP = /(?:[eE][-+]?\d+)/.freeze
|
14
14
|
FRACTIONAL_REGEXP = /(?:\.\d+)/.freeze
|
15
15
|
|
data/lib/virtus/version.rb
CHANGED
@@ -72,4 +72,46 @@ describe Virtus::Attribute, '.determine_type' do
|
|
72
72
|
|
73
73
|
it { should equal(Virtus::Attribute::Set) }
|
74
74
|
end
|
75
|
+
|
76
|
+
context 'with an instance of an enumerable' do
|
77
|
+
before do
|
78
|
+
module Examples
|
79
|
+
class EnumerableClass
|
80
|
+
include Enumerable
|
81
|
+
|
82
|
+
def self.[](*args)
|
83
|
+
new
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class EnumerableAttribute < Virtus::Attribute::Collection
|
88
|
+
primitive EnumerableClass
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
subject { object.determine_type(primitive) }
|
94
|
+
|
95
|
+
let(:primitive) { Examples::EnumerableClass[String] }
|
96
|
+
|
97
|
+
it { should equal(Examples::EnumerableAttribute) }
|
98
|
+
end
|
99
|
+
|
100
|
+
context 'with an instance of an array subclass' do
|
101
|
+
before do
|
102
|
+
module Examples
|
103
|
+
ArraySubclass = Class.new(Array)
|
104
|
+
|
105
|
+
class ArraySubclassAttribute < Virtus::Attribute::Collection
|
106
|
+
primitive ArraySubclass
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
subject { object.determine_type(primitive) }
|
112
|
+
|
113
|
+
let(:primitive) { Examples::ArraySubclass[String] }
|
114
|
+
|
115
|
+
it { should equal(Examples::ArraySubclassAttribute) }
|
116
|
+
end
|
75
117
|
end
|
@@ -60,6 +60,18 @@ describe Virtus::Attribute::DateTime, '#coerce' do
|
|
60
60
|
end
|
61
61
|
end
|
62
62
|
|
63
|
+
context 'with an integer with the seconds since the Unix Epoch' do
|
64
|
+
let(:value) { 1302139609 }
|
65
|
+
|
66
|
+
it_should_behave_like 'a correct date time'
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'with a float with the seconds and milliseconds since the Unix Epoch' do
|
70
|
+
let(:value) { 1302139609.664 }
|
71
|
+
|
72
|
+
it_should_behave_like 'a correct date time'
|
73
|
+
end
|
74
|
+
|
63
75
|
context 'with a on-date value' do
|
64
76
|
let(:value) { 'non-date' }
|
65
77
|
|
@@ -59,6 +59,12 @@ describe Virtus::Attribute::Decimal, '#coerce' do
|
|
59
59
|
it { should eql(BigDecimal('0.41')) }
|
60
60
|
end
|
61
61
|
|
62
|
+
context 'with a positive string float and leading non-significant digits' do
|
63
|
+
let (:value) { '00.0' }
|
64
|
+
|
65
|
+
it { should eql(BigDecimal('0.0')) }
|
66
|
+
end
|
67
|
+
|
62
68
|
context 'with a zero integer' do
|
63
69
|
let(:value) { 0 }
|
64
70
|
|
@@ -107,7 +113,7 @@ describe Virtus::Attribute::Decimal, '#coerce' do
|
|
107
113
|
it { should eql(BigDecimal('-24.35')) }
|
108
114
|
end
|
109
115
|
|
110
|
-
[ Object.new, true, '
|
116
|
+
[ Object.new, true, '0.', '-.0', 'string' ].each do |non_num_value|
|
111
117
|
context 'with a non-numeric value = #{non_num_value.inspect}' do
|
112
118
|
let(:value) { non_num_value }
|
113
119
|
|
@@ -59,6 +59,12 @@ describe Virtus::Attribute::Float, '#coerce' do
|
|
59
59
|
it { should eql(0.41) }
|
60
60
|
end
|
61
61
|
|
62
|
+
context 'with a positive string float and leading non-significant digits' do
|
63
|
+
let (:value) { '00.0' }
|
64
|
+
|
65
|
+
it { should eql(0.0) }
|
66
|
+
end
|
67
|
+
|
62
68
|
context 'with a zero integer' do
|
63
69
|
let(:value) { 0 }
|
64
70
|
|
@@ -107,7 +113,7 @@ describe Virtus::Attribute::Float, '#coerce' do
|
|
107
113
|
it { should eql(-24.35) }
|
108
114
|
end
|
109
115
|
|
110
|
-
[ Object.new, true, '
|
116
|
+
[ Object.new, true, '0.', '-.0', 'string' ].each do |non_num_value|
|
111
117
|
context 'does not coerce non-numeric value #{non_num_value.inspect}' do
|
112
118
|
let(:value) { non_num_value }
|
113
119
|
|
@@ -17,4 +17,12 @@ describe Virtus::Attribute::Hash, '#coerce' do
|
|
17
17
|
|
18
18
|
it { should be(:symbol) }
|
19
19
|
end
|
20
|
+
|
21
|
+
context "without Hash Coerce type define" do
|
22
|
+
let(:options) { { } }
|
23
|
+
let(:input_value) { { :one => '1', 'two' => 2 } }
|
24
|
+
|
25
|
+
it { should eq(:one => '1', 'two' => 2) }
|
26
|
+
|
27
|
+
end
|
20
28
|
end
|
@@ -59,6 +59,12 @@ describe Virtus::Attribute::Integer, '#coerce' do
|
|
59
59
|
it { should eql(0) }
|
60
60
|
end
|
61
61
|
|
62
|
+
context 'with a positive string float and leading non-significant digits' do
|
63
|
+
let (:value) { '00.0' }
|
64
|
+
|
65
|
+
it { should eql(0) }
|
66
|
+
end
|
67
|
+
|
62
68
|
context 'with a zero float' do
|
63
69
|
let(:value) { 0.0 }
|
64
70
|
|
@@ -101,7 +107,7 @@ describe Virtus::Attribute::Integer, '#coerce' do
|
|
101
107
|
it { should eql(value.to_i) }
|
102
108
|
end
|
103
109
|
|
104
|
-
[ Object.new, true, false, '
|
110
|
+
[ Object.new, true, false, '0.', '-.0', 'string' ].each do |non_num_value|
|
105
111
|
context 'does not coerce non-numeric value #{non_num_value.inspect}' do
|
106
112
|
let(:value) { non_num_value }
|
107
113
|
|
data/virtus.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: virtus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.5.
|
4
|
+
version: 0.5.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-12-
|
12
|
+
date: 2012-12-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: backports
|
@@ -43,38 +43,6 @@ dependencies:
|
|
43
43
|
- - ~>
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: 0.0.1
|
46
|
-
- !ruby/object:Gem::Dependency
|
47
|
-
name: rake
|
48
|
-
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
|
-
requirements:
|
51
|
-
- - ~>
|
52
|
-
- !ruby/object:Gem::Version
|
53
|
-
version: 0.9.2
|
54
|
-
type: :development
|
55
|
-
prerelease: false
|
56
|
-
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
|
-
requirements:
|
59
|
-
- - ~>
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: 0.9.2
|
62
|
-
- !ruby/object:Gem::Dependency
|
63
|
-
name: rspec
|
64
|
-
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
|
-
requirements:
|
67
|
-
- - ~>
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
version: '2.12'
|
70
|
-
type: :development
|
71
|
-
prerelease: false
|
72
|
-
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
|
-
requirements:
|
75
|
-
- - ~>
|
76
|
-
- !ruby/object:Gem::Version
|
77
|
-
version: '2.12'
|
78
46
|
description: Attributes on Steroids for Plain Old Ruby Objects
|
79
47
|
email:
|
80
48
|
- piotr.solnica@gmail.com
|