active_attr 0.4.1 → 0.5.0.alpha1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of active_attr might be problematic. Click here for more details.
- data/.travis.yml +7 -0
- data/CHANGELOG.md +14 -0
- data/README.md +57 -25
- data/Rakefile +2 -0
- data/active_attr.gemspec +3 -2
- data/gemfiles/rails_3_1.gemfile +6 -0
- data/lib/active_attr.rb +3 -2
- data/lib/active_attr/attribute_defaults.rb +120 -0
- data/lib/active_attr/attribute_definition.rb +25 -2
- data/lib/active_attr/attributes.rb +44 -33
- data/lib/active_attr/logger.rb +8 -8
- data/lib/active_attr/mass_assignment.rb +2 -1
- data/lib/active_attr/matchers/have_attribute_matcher.rb +28 -10
- data/lib/active_attr/model.rb +2 -0
- data/lib/active_attr/query_attributes.rb +2 -5
- data/lib/active_attr/typecasted_attributes.rb +77 -0
- data/lib/active_attr/typecasting.rb +60 -0
- data/lib/active_attr/typecasting/boolean.rb +6 -0
- data/lib/active_attr/typecasting/boolean_typecaster.rb +21 -0
- data/lib/active_attr/typecasting/date_time_typecaster.rb +14 -0
- data/lib/active_attr/typecasting/date_typecaster.rb +14 -0
- data/lib/active_attr/typecasting/float_typecaster.rb +11 -0
- data/lib/active_attr/typecasting/integer_typecaster.rb +12 -0
- data/lib/active_attr/typecasting/object_typecaster.rb +9 -0
- data/lib/active_attr/typecasting/string_typecaster.rb +11 -0
- data/lib/active_attr/version.rb +1 -1
- data/spec/functional/active_attr/attribute_defaults_spec.rb +136 -0
- data/spec/functional/active_attr/attributes_spec.rb +19 -5
- data/spec/functional/active_attr/model_spec.rb +34 -4
- data/spec/functional/active_attr/typecasted_attributes_spec.rb +116 -0
- data/spec/support/mass_assignment_shared_examples.rb +1 -1
- data/spec/unit/active_attr/attribute_defaults_spec.rb +43 -0
- data/spec/unit/active_attr/attribute_definition_spec.rb +12 -8
- data/spec/unit/active_attr/attributes_spec.rb +22 -10
- data/spec/unit/active_attr/mass_assignment_spec.rb +1 -0
- data/spec/unit/active_attr/matchers/have_attribute_matcher_spec.rb +94 -15
- data/spec/unit/active_attr/query_attributes_spec.rb +1 -1
- data/spec/unit/active_attr/typecasted_attributes_spec.rb +76 -0
- data/spec/unit/active_attr/typecasting/boolean_spec.rb +10 -0
- data/spec/unit/active_attr/typecasting/boolean_typecaster_spec.rb +147 -0
- data/spec/unit/active_attr/typecasting/date_time_typecaster_spec.rb +67 -0
- data/spec/unit/active_attr/typecasting/date_typecaster_spec.rb +55 -0
- data/spec/unit/active_attr/typecasting/float_typecaster_spec.rb +27 -0
- data/spec/unit/active_attr/typecasting/integer_typecaster_spec.rb +35 -0
- data/spec/unit/active_attr/typecasting/object_typecaster_spec.rb +15 -0
- data/spec/unit/active_attr/typecasting/string_typecaster_spec.rb +24 -0
- data/spec/unit/active_attr/typecasting_spec.rb +87 -0
- data/spec/unit/active_attr/version_spec.rb +11 -2
- metadata +75 -29
- data/lib/active_attr/strict_mass_assignment.rb +0 -44
- data/lib/active_attr/unknown_attributes_error.rb +0 -18
- data/spec/unit/active_attr/strict_mass_assignment_spec.rb +0 -38
- data/spec/unit/active_attr/unknown_attributes_error_spec.rb +0 -9
@@ -0,0 +1,76 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "active_attr/typecasted_attributes"
|
3
|
+
|
4
|
+
module ActiveAttr
|
5
|
+
describe TypecastedAttributes do
|
6
|
+
subject { model_class.new }
|
7
|
+
|
8
|
+
let :model_class do
|
9
|
+
Class.new do
|
10
|
+
include TypecastedAttributes
|
11
|
+
|
12
|
+
attribute :amount, :type => String
|
13
|
+
attribute :first_name
|
14
|
+
attribute :last_name
|
15
|
+
|
16
|
+
def last_name_before_type_cast
|
17
|
+
super
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.name
|
21
|
+
"Foo"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
let :attributeless do
|
27
|
+
Class.new do
|
28
|
+
include TypecastedAttributes
|
29
|
+
|
30
|
+
def self.name
|
31
|
+
"Foo"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe ".attribute" do
|
37
|
+
it "defines an attribute pre-typecasting reader that calls #attribute_before_type_cast" do
|
38
|
+
subject.should_receive(:attribute_before_type_cast).with("first_name")
|
39
|
+
subject.first_name_before_type_cast
|
40
|
+
end
|
41
|
+
|
42
|
+
it "defines an attribute reader that can be called via super" do
|
43
|
+
subject.should_receive(:attribute_before_type_cast).with("last_name")
|
44
|
+
subject.last_name_before_type_cast
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe ".inspect" do
|
49
|
+
it "renders the class name" do
|
50
|
+
model_class.inspect.should match(/^Foo\(.*\)$/)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "renders the attribute names and types in alphabetical order, using Object for undeclared types" do
|
54
|
+
model_class.inspect.should match "(amount: String, first_name: Object, last_name: Object)"
|
55
|
+
end
|
56
|
+
|
57
|
+
it "doesn't format the inspection string for attributes if the model does not have any" do
|
58
|
+
attributeless.inspect.should == "Foo"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#attribute_before_type_cast" do
|
63
|
+
it "returns the assigned attribute value, without typecasting, when given an attribute name as a Symbol" do
|
64
|
+
value = :value
|
65
|
+
subject.amount = value
|
66
|
+
subject.attribute_before_type_cast(:amount).should equal value
|
67
|
+
end
|
68
|
+
|
69
|
+
it "returns the assigned attribute value, without typecasting, when given an attribute name as a String" do
|
70
|
+
value = :value
|
71
|
+
subject.amount = value
|
72
|
+
subject.attribute_before_type_cast('amount').should equal value
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "active_attr/typecasting/boolean_typecaster"
|
3
|
+
require "bigdecimal"
|
4
|
+
|
5
|
+
module ActiveAttr
|
6
|
+
module Typecasting
|
7
|
+
describe BooleanTypecaster do
|
8
|
+
describe "#call" do
|
9
|
+
it "returns true for true" do
|
10
|
+
subject.call(true).should equal true
|
11
|
+
end
|
12
|
+
|
13
|
+
it "returns false for false" do
|
14
|
+
subject.call(false).should equal false
|
15
|
+
end
|
16
|
+
|
17
|
+
it "casts nil to false" do
|
18
|
+
subject.call(nil).should equal false
|
19
|
+
end
|
20
|
+
|
21
|
+
it "casts an Object to true" do
|
22
|
+
subject.call(Object.new).should equal true
|
23
|
+
end
|
24
|
+
|
25
|
+
context "when the value is a String" do
|
26
|
+
it "casts an empty String to false" do
|
27
|
+
subject.call("").should equal false
|
28
|
+
end
|
29
|
+
|
30
|
+
it "casts a non-empty String to true" do
|
31
|
+
subject.call("abc").should equal true
|
32
|
+
end
|
33
|
+
|
34
|
+
{
|
35
|
+
"t" => true,
|
36
|
+
"f" => false,
|
37
|
+
"T" => true,
|
38
|
+
"F" => false,
|
39
|
+
# http://yaml.org/type/bool.html
|
40
|
+
"y" => true,
|
41
|
+
"Y" => true,
|
42
|
+
"yes" => true,
|
43
|
+
"Yes" => true,
|
44
|
+
"YES" => true,
|
45
|
+
"n" => false,
|
46
|
+
"N" => false,
|
47
|
+
"no" => false,
|
48
|
+
"No" => false,
|
49
|
+
"NO" => false,
|
50
|
+
"true" => true,
|
51
|
+
"True" => true,
|
52
|
+
"TRUE" => true,
|
53
|
+
"false" => false,
|
54
|
+
"False" => false,
|
55
|
+
"FALSE" => false,
|
56
|
+
"on" => true,
|
57
|
+
"On" => true,
|
58
|
+
"ON" => true,
|
59
|
+
"off" => false,
|
60
|
+
"Off" => false,
|
61
|
+
"OFF" => false,
|
62
|
+
}.each_pair do |value, result|
|
63
|
+
it "casts #{value.inspect} to #{result.inspect}" do
|
64
|
+
subject.call(value).should equal result
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "when the value is Numeric" do
|
70
|
+
it "casts 0 to false" do
|
71
|
+
subject.call(0).should equal false
|
72
|
+
end
|
73
|
+
|
74
|
+
it "casts 1 to true" do
|
75
|
+
subject.call(1).should equal true
|
76
|
+
end
|
77
|
+
|
78
|
+
it "casts 0.0 to false" do
|
79
|
+
subject.call(0.0).should equal false
|
80
|
+
end
|
81
|
+
|
82
|
+
it "casts 0.1 to true" do
|
83
|
+
subject.call(0.1).should equal true
|
84
|
+
end
|
85
|
+
|
86
|
+
it "casts a zero BigDecimal to false" do
|
87
|
+
subject.call(BigDecimal.new("0.0")).should equal false
|
88
|
+
end
|
89
|
+
|
90
|
+
it "casts a non-zero BigDecimal to true" do
|
91
|
+
subject.call(BigDecimal.new("0.1")).should equal true
|
92
|
+
end
|
93
|
+
|
94
|
+
it "casts -1 to true" do
|
95
|
+
subject.call(-1).should equal true
|
96
|
+
end
|
97
|
+
|
98
|
+
it "casts -0.0 to false" do
|
99
|
+
subject.call(-0.0).should equal false
|
100
|
+
end
|
101
|
+
|
102
|
+
it "casts -0.1 to true" do
|
103
|
+
subject.call(-0.1).should equal true
|
104
|
+
end
|
105
|
+
|
106
|
+
it "casts a negative zero BigDecimal to false" do
|
107
|
+
subject.call(BigDecimal.new("-0.0")).should equal false
|
108
|
+
end
|
109
|
+
|
110
|
+
it "casts a negative BigDecimal to true" do
|
111
|
+
subject.call(BigDecimal.new("-0.1")).should equal true
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
context "when the value is the String version of a Numeric" do
|
116
|
+
it "casts '0' to false" do
|
117
|
+
subject.call("0").should equal false
|
118
|
+
end
|
119
|
+
|
120
|
+
it "casts '1' to true" do
|
121
|
+
subject.call("1").should equal true
|
122
|
+
end
|
123
|
+
|
124
|
+
it "casts '0.0' to false" do
|
125
|
+
subject.call("0.0").should equal false
|
126
|
+
end
|
127
|
+
|
128
|
+
it "casts '0.1' to true" do
|
129
|
+
subject.call("0.1").should equal true
|
130
|
+
end
|
131
|
+
|
132
|
+
it "casts '-1' to true" do
|
133
|
+
subject.call("-1").should equal true
|
134
|
+
end
|
135
|
+
|
136
|
+
it "casts '-0.0' to false" do
|
137
|
+
subject.call("-0.0").should equal false
|
138
|
+
end
|
139
|
+
|
140
|
+
it "casts '-0.1' to true" do
|
141
|
+
subject.call("-0.1").should equal true
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "active_attr/typecasting/date_time_typecaster"
|
3
|
+
|
4
|
+
module ActiveAttr
|
5
|
+
module Typecasting
|
6
|
+
describe DateTimeTypecaster do
|
7
|
+
describe "#call" do
|
8
|
+
it "returns the original DateTime for a DateTime" do
|
9
|
+
value = DateTime.now
|
10
|
+
subject.call(value).should equal value
|
11
|
+
end
|
12
|
+
|
13
|
+
it "returns nil for nil" do
|
14
|
+
subject.call(nil).should equal nil
|
15
|
+
end
|
16
|
+
|
17
|
+
it "casts a Date to a DateTime at the beginning of the day with no offset" do
|
18
|
+
result = subject.call(Date.new(2012, 1, 1))
|
19
|
+
result.should eql DateTime.new(2012, 1, 1)
|
20
|
+
result.should be_instance_of DateTime
|
21
|
+
end
|
22
|
+
|
23
|
+
it "casts a UTC Time to a DateTime with no offset" do
|
24
|
+
result = subject.call(Time.utc(2012, 1, 1, 12, 0, 0))
|
25
|
+
result.should eql DateTime.new(2012, 1, 1, 12, 0, 0, 0)
|
26
|
+
result.should be_instance_of DateTime
|
27
|
+
end
|
28
|
+
|
29
|
+
it "casts a local Time to a DateTime with a matching offset" do
|
30
|
+
result = subject.call(Time.local(2012, 1, 1, 12, 0, 0))
|
31
|
+
result.should eql DateTime.new(2012, 1, 1, 12, 0, 0, Rational(Time.now.utc_offset, 86400))
|
32
|
+
result.should be_instance_of DateTime
|
33
|
+
end
|
34
|
+
|
35
|
+
it "casts an ISO8601 date String to a Date" do
|
36
|
+
result = subject.call("2012-01-01")
|
37
|
+
result.should eql DateTime.new(2012, 1, 1)
|
38
|
+
result.should be_instance_of DateTime
|
39
|
+
end
|
40
|
+
|
41
|
+
it "casts an RFC2616 date and GMT time String to a DateTime" do
|
42
|
+
result = subject.call("Sat, 03 Feb 2001 00:00:00 GMT")
|
43
|
+
result.should eql DateTime.new(2001, 2, 3)
|
44
|
+
result.should be_instance_of DateTime
|
45
|
+
end
|
46
|
+
|
47
|
+
it "casts an RFC3339 date and offset time String to a DateTime" do
|
48
|
+
result = subject.call("2001-02-03T04:05:06+07:00")
|
49
|
+
result.should eql DateTime.new(2001, 2, 3, 4, 5, 6, "+07:00")
|
50
|
+
result.should be_instance_of DateTime
|
51
|
+
end
|
52
|
+
|
53
|
+
it "casts a UTC ActiveSupport::TimeWithZone to a DateTime" do
|
54
|
+
result = subject.call(Time.utc(2012, 1, 1, 1, 1, 1).in_time_zone("UTC"))
|
55
|
+
result.should eql DateTime.new(2012, 1, 1, 1, 1, 1, 0)
|
56
|
+
result.should be_instance_of DateTime
|
57
|
+
end
|
58
|
+
|
59
|
+
it "casts an Alaska ActiveSupport::TimeWithZone to a Date representing the date portion" do
|
60
|
+
result = subject.call(Time.utc(2012, 1, 1, 0, 0, 0).in_time_zone("Alaska"))
|
61
|
+
result.should eql DateTime.new(2011, 12, 31, 15, 0, 0, "-09:00")
|
62
|
+
result.should be_instance_of DateTime
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "active_attr/typecasting/date_typecaster"
|
3
|
+
|
4
|
+
module ActiveAttr
|
5
|
+
module Typecasting
|
6
|
+
describe DateTypecaster do
|
7
|
+
describe "#call" do
|
8
|
+
it "returns the original date for a Date" do
|
9
|
+
value = Date.today
|
10
|
+
subject.call(value).should equal value
|
11
|
+
end
|
12
|
+
|
13
|
+
it "returns nil for nil" do
|
14
|
+
subject.call(nil).should equal nil
|
15
|
+
end
|
16
|
+
|
17
|
+
it "casts a UTC Time to a Date representing the date portion" do
|
18
|
+
subject.call(Time.utc(2012, 1, 1, 0, 0, 0)).should eql Date.new(2012, 1, 1)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "casts a local Time to a Date representing the date portion" do
|
22
|
+
subject.call(Time.local(2012, 1, 1, 0, 0, 0)).should eql Date.new(2012, 1, 1)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "casts a UTC DateTime to a Date representing the date portion" do
|
26
|
+
subject.call(DateTime.new(2012, 1, 1, 0, 0, 0)).should eql Date.new(2012, 1, 1)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "casts an offset DateTime to a Date representing the date portion" do
|
30
|
+
subject.call(DateTime.new(2012, 1, 1, 0, 0, 0, "-12:00")).should eql Date.new(2012, 1, 1)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "casts an ISO8601 date String to a Date" do
|
34
|
+
subject.call("2012-01-01").should eql Date.new(2012, 1, 1)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "casts an RFC2616 date and GMT time String to a Date representing the date portion" do
|
38
|
+
subject.call("Sat, 03 Feb 2001 00:00:00 GMT").should eql Date.new(2001, 2, 3)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "casts an RFC3339 date and offset time String to a Date representing the date portion" do
|
42
|
+
subject.call("2001-02-03T04:05:06+07:00").should eql Date.new(2001, 2, 3)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "casts a UTC ActiveSupport::TimeWithZone to a Date representing the date portion" do
|
46
|
+
subject.call(Time.utc(2012, 1, 1, 0, 0, 0).in_time_zone("UTC")).should eql Date.new(2012, 1, 1)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "casts an Alaska ActiveSupport::TimeWithZone to a Date representing the date portion" do
|
50
|
+
subject.call(Time.utc(2012, 1, 1, 0, 0, 0).in_time_zone("Alaska")).should eql Date.new(2011, 12, 31)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "active_attr/typecasting/float_typecaster"
|
3
|
+
|
4
|
+
module ActiveAttr
|
5
|
+
module Typecasting
|
6
|
+
describe FloatTypecaster do
|
7
|
+
describe "#call" do
|
8
|
+
it "returns the original float for a Float" do
|
9
|
+
value = 2.0
|
10
|
+
subject.call(value).should equal value
|
11
|
+
end
|
12
|
+
|
13
|
+
it "casts nil to 0.0" do
|
14
|
+
subject.call(nil).should eql 0.0
|
15
|
+
end
|
16
|
+
|
17
|
+
it "returns the float version of a String" do
|
18
|
+
subject.call("2").should eql 2.0
|
19
|
+
end
|
20
|
+
|
21
|
+
it "returns nil for an object that does not respond to #to_f" do
|
22
|
+
subject.call(Object.new).should be_nil
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "active_attr/typecasting/integer_typecaster"
|
3
|
+
|
4
|
+
module ActiveAttr
|
5
|
+
module Typecasting
|
6
|
+
describe IntegerTypecaster do
|
7
|
+
describe "#call" do
|
8
|
+
it "returns the original integer for a FixNum" do
|
9
|
+
value = 2
|
10
|
+
subject.call(value).should equal value
|
11
|
+
end
|
12
|
+
|
13
|
+
it "casts nil to 0" do
|
14
|
+
subject.call(nil).should eql 0
|
15
|
+
end
|
16
|
+
|
17
|
+
it "returns the integer version of a String" do
|
18
|
+
subject.call("2").should eql 2
|
19
|
+
end
|
20
|
+
|
21
|
+
it "returns nil for an object that does not respond to #to_i" do
|
22
|
+
subject.call(Object.new).should be_nil
|
23
|
+
end
|
24
|
+
|
25
|
+
it "returns nil for Float::INFINITY" do
|
26
|
+
subject.call(1.0 / 0.0).should be_nil
|
27
|
+
end
|
28
|
+
|
29
|
+
it "returns nil for Float::NAN" do
|
30
|
+
subject.call(0.0 / 0.0).should be_nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
require "active_attr/typecasting/object_typecaster"
|
3
|
+
|
4
|
+
module ActiveAttr
|
5
|
+
module Typecasting
|
6
|
+
describe ObjectTypecaster do
|
7
|
+
describe "#call" do
|
8
|
+
it "returns the original object for any object" do
|
9
|
+
value = mock
|
10
|
+
subject.call(value).should equal value
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|