attributor 2.1.0
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.
- checksums.yaml +7 -0
- data/.gitignore +15 -0
- data/.rspec +2 -0
- data/.travis.yml +4 -0
- data/CHANGELOG.md +52 -0
- data/Gemfile +3 -0
- data/Guardfile +12 -0
- data/LICENSE +22 -0
- data/README.md +62 -0
- data/Rakefile +28 -0
- data/attributor.gemspec +40 -0
- data/lib/attributor.rb +89 -0
- data/lib/attributor/attribute.rb +271 -0
- data/lib/attributor/attribute_resolver.rb +116 -0
- data/lib/attributor/dsl_compiler.rb +106 -0
- data/lib/attributor/exceptions.rb +38 -0
- data/lib/attributor/extensions/randexp.rb +10 -0
- data/lib/attributor/type.rb +117 -0
- data/lib/attributor/types/boolean.rb +26 -0
- data/lib/attributor/types/collection.rb +135 -0
- data/lib/attributor/types/container.rb +42 -0
- data/lib/attributor/types/csv.rb +10 -0
- data/lib/attributor/types/date_time.rb +36 -0
- data/lib/attributor/types/file_upload.rb +11 -0
- data/lib/attributor/types/float.rb +27 -0
- data/lib/attributor/types/hash.rb +337 -0
- data/lib/attributor/types/ids.rb +26 -0
- data/lib/attributor/types/integer.rb +63 -0
- data/lib/attributor/types/model.rb +316 -0
- data/lib/attributor/types/object.rb +19 -0
- data/lib/attributor/types/string.rb +25 -0
- data/lib/attributor/types/struct.rb +50 -0
- data/lib/attributor/types/tempfile.rb +36 -0
- data/lib/attributor/version.rb +3 -0
- data/spec/attribute_resolver_spec.rb +227 -0
- data/spec/attribute_spec.rb +597 -0
- data/spec/attributor_spec.rb +25 -0
- data/spec/dsl_compiler_spec.rb +130 -0
- data/spec/spec_helper.rb +30 -0
- data/spec/support/models.rb +81 -0
- data/spec/support/types.rb +21 -0
- data/spec/type_spec.rb +134 -0
- data/spec/types/boolean_spec.rb +85 -0
- data/spec/types/collection_spec.rb +286 -0
- data/spec/types/container_spec.rb +49 -0
- data/spec/types/csv_spec.rb +17 -0
- data/spec/types/date_time_spec.rb +90 -0
- data/spec/types/file_upload_spec.rb +6 -0
- data/spec/types/float_spec.rb +78 -0
- data/spec/types/hash_spec.rb +372 -0
- data/spec/types/ids_spec.rb +32 -0
- data/spec/types/integer_spec.rb +151 -0
- data/spec/types/model_spec.rb +401 -0
- data/spec/types/string_spec.rb +55 -0
- data/spec/types/struct_spec.rb +189 -0
- data/spec/types/tempfile_spec.rb +6 -0
- metadata +348 -0
@@ -0,0 +1,25 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
2
|
+
|
3
|
+
|
4
|
+
describe Attributor do
|
5
|
+
context '.resolve_type' do
|
6
|
+
context 'given valid types' do
|
7
|
+
{
|
8
|
+
::Integer => Attributor::Integer,
|
9
|
+
Integer => Attributor::Integer,
|
10
|
+
Attributor::Integer => Attributor::Integer,
|
11
|
+
::Attributor::Integer => Attributor::Integer,
|
12
|
+
::Attributor::DateTime => Attributor::DateTime,
|
13
|
+
# FIXME: Boolean doesn't exist in Ruby, thus this causes and error
|
14
|
+
# https://github.com/rightscale/attributor/issues/25
|
15
|
+
#Boolean => Attributor::Boolean,
|
16
|
+
Attributor::Boolean => Attributor::Boolean,
|
17
|
+
Attributor::Struct => Attributor::Struct
|
18
|
+
}.each do |type, expected_type|
|
19
|
+
it "resolves #{type} as #{expected_type}" do
|
20
|
+
Attributor.resolve_type(type).should == expected_type
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
2
|
+
|
3
|
+
|
4
|
+
describe Attributor::DSLCompiler do
|
5
|
+
|
6
|
+
let(:target) { double("model", attributes: {}) }
|
7
|
+
|
8
|
+
let(:dsl_compiler_options) { {} }
|
9
|
+
subject(:dsl_compiler) { Attributor::DSLCompiler.new(target, dsl_compiler_options) }
|
10
|
+
|
11
|
+
let(:attribute_name) { :name }
|
12
|
+
let(:type) { Attributor::String }
|
13
|
+
|
14
|
+
let!(:reference_attributes) { Turducken.attributes }
|
15
|
+
let(:reference_type) { reference_attribute.type }
|
16
|
+
let(:reference_attribute_options) { reference_attribute.options }
|
17
|
+
let(:reference_attribute) {reference_attributes[attribute_name] }
|
18
|
+
|
19
|
+
context '#attribute' do
|
20
|
+
let(:attribute_options) { {} }
|
21
|
+
|
22
|
+
let(:expected_options) { attribute_options }
|
23
|
+
let(:expected_type) { type }
|
24
|
+
|
25
|
+
context 'when not not given a block for a sub-definition' do
|
26
|
+
context 'without a reference' do
|
27
|
+
it 'raises an error for a missing type' do
|
28
|
+
expect {
|
29
|
+
dsl_compiler.attribute(attribute_name)
|
30
|
+
}.to raise_error(/type for attribute/)
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'creates an attribute given a name and type' do
|
35
|
+
Attributor::Attribute.should_receive(:new).with(expected_type, expected_options)
|
36
|
+
dsl_compiler.attribute(attribute_name, type)
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
it 'creates an attribute given a name, type, and options' do
|
41
|
+
Attributor::Attribute.should_receive(:new).with(expected_type, expected_options)
|
42
|
+
dsl_compiler.attribute(attribute_name, type, attribute_options)
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
context 'with a reference' do
|
49
|
+
let(:dsl_compiler_options) { {:reference => Turducken} }
|
50
|
+
|
51
|
+
context 'with no options' do
|
52
|
+
let(:expected_options) { reference_attribute_options }
|
53
|
+
|
54
|
+
it 'creates an attribute with the inherited type' do
|
55
|
+
Attributor::Attribute.should_receive(:new).with(expected_type, expected_options)
|
56
|
+
dsl_compiler.attribute(attribute_name)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'with options' do
|
61
|
+
let(:attribute_options) { {:description => "some new description", :required => true} }
|
62
|
+
let(:expected_options) { reference_attribute_options.merge(attribute_options) }
|
63
|
+
|
64
|
+
before do
|
65
|
+
Attributor::Attribute.should_receive(:new).with(expected_type, expected_options)
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'creates an attribute with the inherited type and merged options' do
|
69
|
+
dsl_compiler.attribute(attribute_name, attribute_options)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'accepts explicit nil type' do
|
73
|
+
dsl_compiler.attribute(attribute_name, nil, attribute_options)
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'for a referenced Model attribute' do
|
79
|
+
let(:attribute_name) { :turkey }
|
80
|
+
let(:expected_type) { Turkey }
|
81
|
+
let(:expected_options) { reference_attribute_options.merge(attribute_options) }
|
82
|
+
|
83
|
+
it 'creates an attribute with the inherited type' do
|
84
|
+
Attributor::Attribute.should_receive(:new).with(expected_type, expected_options)
|
85
|
+
dsl_compiler.attribute(attribute_name)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
|
94
|
+
context 'when given a block for sub-attributes' do
|
95
|
+
let(:attribute_block) { Proc.new { } }
|
96
|
+
let(:attribute_name) { :turkey }
|
97
|
+
let(:type) { Attributor::Struct }
|
98
|
+
let(:expected_type) { Attributor::Struct }
|
99
|
+
|
100
|
+
context 'without a reference' do
|
101
|
+
it 'defaults type to Struct' do
|
102
|
+
Attributor::Attribute.should_receive(:new).with(expected_type, expected_options)
|
103
|
+
dsl_compiler.attribute(attribute_name, &attribute_block)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context 'with a reference' do
|
108
|
+
let(:dsl_compiler_options) { {:reference => Turducken} }
|
109
|
+
let(:expected_options) do
|
110
|
+
attribute_options.merge(:reference => reference_type)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'is unhappy from somewhere else if you do not specify a type' do
|
114
|
+
expect {
|
115
|
+
dsl_compiler.attribute(attribute_name, attribute_options, &attribute_block)
|
116
|
+
}.to raise_error(/does not support anonymous generation/)
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'passes the correct reference to the created attribute' do
|
120
|
+
Attributor::Attribute.should_receive(:new).with(expected_type, expected_options)
|
121
|
+
dsl_compiler.attribute(attribute_name, type, attribute_options, &attribute_block)
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
Encoding.default_external = Encoding::UTF_8
|
2
|
+
|
3
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
|
+
|
6
|
+
# Configure simplecov gem (must be here at top of file)
|
7
|
+
require 'simplecov'
|
8
|
+
SimpleCov.start do
|
9
|
+
add_filter 'spec' # Don't include RSpec stuff
|
10
|
+
add_group 'Types', 'lib/attributor/types'
|
11
|
+
end
|
12
|
+
|
13
|
+
require 'rspec'
|
14
|
+
require 'attributor'
|
15
|
+
|
16
|
+
require 'pry'
|
17
|
+
|
18
|
+
# Requires supporting files with custom matchers and macros, etc,
|
19
|
+
# in ./support/ and its subdirectories.
|
20
|
+
Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
|
21
|
+
|
22
|
+
RSpec.configure do |config|
|
23
|
+
|
24
|
+
config.around(:each) do |example|
|
25
|
+
Attributor::AttributeResolver.current = Attributor::AttributeResolver.new
|
26
|
+
example.run
|
27
|
+
Attributor::AttributeResolver.current = nil
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
class Chicken < Attributor::Model
|
2
|
+
attributes(:identity => :email) do
|
3
|
+
attribute :name, Attributor::String, example: /[:first_name:]/
|
4
|
+
attribute :age, Attributor::Integer, :default => 1, :min => 0, :max => 120, :description => "The age of the chicken"
|
5
|
+
attribute :email, Attributor::String, :example => /[:email:]/, :regexp => /@/, :description => "The email address of the chicken"
|
6
|
+
attribute :angry, Attributor::Boolean, :example => "true", :description => "Angry bird?"
|
7
|
+
attribute :weight, Attributor::Float, :example => /\d{1,2}\.\d/, :description => "The weight of the chicken"
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
|
12
|
+
class Duck < Attributor::Model
|
13
|
+
attributes do
|
14
|
+
attribute :age, Attributor::Integer, :required_if => {"name" => "Daffy" }
|
15
|
+
attribute :name, Attributor::String
|
16
|
+
attribute :email, Attributor::String, :required_if => "name"
|
17
|
+
attribute :angry, Attributor::Boolean, :default => true, :example => /true|false/, :description => "Angry bird?"
|
18
|
+
attribute :weight, Attributor::Float, :example => /\d{1,2}\.\d/, :description => "The weight of the duck"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
class Turkey < Attributor::Model
|
24
|
+
attributes do
|
25
|
+
attribute :age, Integer, :default => 1, :min => 0, :max => 120, :description => "The age of the turkey"
|
26
|
+
attribute :name, String , :description => "name of the turkey", :example => /[:name:]/ #, :default => "Providencia Zboncak"
|
27
|
+
attribute :email, String, :example => /[:email:]/, :regexp => /@/, :description => "The email address of the turkey"
|
28
|
+
attribute :weight, Attributor::Float, :example => /\d{1,2}\.\d/, :max => 86.7, :description => "The weight of the turkey"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
|
34
|
+
class Turducken < Attributor::Model
|
35
|
+
attributes do
|
36
|
+
attribute :name, String, :description => "Turducken name", :example => /[:name:]/
|
37
|
+
attribute :chicken, Chicken
|
38
|
+
attribute :duck, Duck
|
39
|
+
attribute :turkey, Turkey, :description => "The turkey"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
# http://en.wikipedia.org/wiki/Cormorant
|
45
|
+
class Cormorant < Attributor::Model
|
46
|
+
attributes do
|
47
|
+
# This will be a collection of arbitrary Ruby Objects
|
48
|
+
attribute :fish, Attributor::Collection, :description => "All kinds of fish for feeding the babies"
|
49
|
+
|
50
|
+
# This will be a collection of Cormorants (note, this relationship is circular)
|
51
|
+
attribute :neighbors, Attributor::Collection.of(Cormorant), :description => "Neighbor cormorants"
|
52
|
+
|
53
|
+
# This will be a collection of instances of an anonymous Struct class, each having two well-defined attributes
|
54
|
+
|
55
|
+
attribute :babies, Attributor::Collection.of(Attributor::Struct), :description => "All the babies", :member_options => {:identity => 'name'} do
|
56
|
+
attribute :name, Attributor::String, :example => /[:name]/, :description => "The name of the baby cormorant"
|
57
|
+
attribute :months, Attributor::Integer, :default => 0, :min => 0, :description => "The age in months of the baby cormorant"
|
58
|
+
attribute :weight, Attributor::Float, :example => /\d{1,2}\.\d{3}/, :description => "The weight in kg of the baby cormorant"
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
class Person < Attributor::Model
|
66
|
+
attributes do
|
67
|
+
attribute :name, String, example: /[:first_name:]/
|
68
|
+
attribute :title, String, values: %w{Mr Mrs Ms Dr}
|
69
|
+
attribute :okay, Attributor::Boolean, values: [true]
|
70
|
+
attribute :address, Address, example: proc { |person, context| Address.example(context, person: person) }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
class Address < Attributor::Model
|
76
|
+
attributes do
|
77
|
+
attribute :name, String, example: /\w+/
|
78
|
+
attribute :state, String, values: %w{OR CA}
|
79
|
+
attribute :person, Person, example: proc { |address, context| Person.example(context, address: address) }
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class AttributeType
|
2
|
+
include Attributor::Type
|
3
|
+
def self.native_type
|
4
|
+
::String
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
class IntegerAttributeType
|
10
|
+
include Attributor::Type
|
11
|
+
def self.native_type
|
12
|
+
::Integer
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.load(value,context=Attributor::DEFAULT_ROOT_CONTEXT, **options)
|
16
|
+
value.to_i
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
|
data/spec/type_spec.rb
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'spec_helper.rb')
|
2
|
+
|
3
|
+
|
4
|
+
describe Attributor::Type do
|
5
|
+
|
6
|
+
|
7
|
+
subject(:test_type) { AttributeType }
|
8
|
+
|
9
|
+
let(:attribute_options) { Hash.new }
|
10
|
+
let(:attribute_attributes) { Hash.new }
|
11
|
+
|
12
|
+
let(:attribute) do
|
13
|
+
double "attribute",
|
14
|
+
:options => attribute_options,
|
15
|
+
:attributes => attribute_attributes
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
its(:native_type) { should be(::String) }
|
20
|
+
|
21
|
+
|
22
|
+
context 'load' do
|
23
|
+
let(:value) { nil }
|
24
|
+
let(:context) { nil }
|
25
|
+
|
26
|
+
context "when given a nil value" do
|
27
|
+
it 'always successfully returns it (i.e., you can always load nil)' do
|
28
|
+
test_type.load(value).should be(value)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context "when given a value that is of native_type" do
|
33
|
+
let(:value) { "one" }
|
34
|
+
it 'returns the value' do
|
35
|
+
test_type.load(value).should be(value)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "when given a value that is not of native_type" do
|
40
|
+
let(:value) { 1 }
|
41
|
+
let(:context) { ['top','sub'] }
|
42
|
+
|
43
|
+
it 'raises an exception' do
|
44
|
+
expect { test_type.load(value,context) }.to raise_error( Attributor::IncompatibleTypeError, /AttributeType cannot load values of type Fixnum.*while loading top.sub/)
|
45
|
+
end
|
46
|
+
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
context 'validate' do
|
54
|
+
let(:context) { ['some_attribute'] }
|
55
|
+
|
56
|
+
let(:attribute_options) { {} }
|
57
|
+
|
58
|
+
let(:attribute) { double("some_attribute", :options => attribute_options)}
|
59
|
+
subject(:errors) { test_type.validate(value, context, attribute) }
|
60
|
+
|
61
|
+
context 'min and max' do
|
62
|
+
let(:min) { 10 }
|
63
|
+
let(:max) { 100}
|
64
|
+
|
65
|
+
let(:attribute_options) { {:min => min, :max => max} }
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
context "with a value <= min" do
|
70
|
+
let(:value) { 1 }
|
71
|
+
|
72
|
+
it { should_not be_empty }
|
73
|
+
it 'returns the correct error message' do
|
74
|
+
errors.first.should =~ /value \(#{value}\) is smaller than the allowed min/
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context "with a value >= max" do
|
79
|
+
let(:value) { 1000 }
|
80
|
+
it { should_not be_empty }
|
81
|
+
it 'returns the correct error message' do
|
82
|
+
errors.first.should =~ /value \(#{value}\) is larger than the allowed max/
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'with a value within the range' do
|
88
|
+
let(:value) { 50 }
|
89
|
+
it { should be_empty }
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
end
|
94
|
+
|
95
|
+
|
96
|
+
|
97
|
+
context 'regexp' do
|
98
|
+
let(:regexp) { /dog/ }
|
99
|
+
let(:attribute_options) { {:regexp => regexp} }
|
100
|
+
|
101
|
+
context 'with a value that matches' do
|
102
|
+
let(:value) { 'bulldog' }
|
103
|
+
|
104
|
+
it { should be_empty }
|
105
|
+
end
|
106
|
+
|
107
|
+
|
108
|
+
context 'with a value that does not match' do
|
109
|
+
let(:value) { 'chicken' }
|
110
|
+
it { should_not be_empty}
|
111
|
+
it 'returns the correct error message' do
|
112
|
+
errors.first.should =~ /value \(#{value}\) does not match regexp/
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
end
|
120
|
+
|
121
|
+
|
122
|
+
context 'example' do
|
123
|
+
|
124
|
+
end
|
125
|
+
|
126
|
+
context 'describe' do
|
127
|
+
subject(:description) { test_type.describe }
|
128
|
+
it 'outputs the type name' do
|
129
|
+
description[:name].should == test_type.name
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'spec_helper.rb')
|
2
|
+
|
3
|
+
describe Attributor::Boolean do
|
4
|
+
|
5
|
+
subject(:type) { Attributor::Boolean }
|
6
|
+
|
7
|
+
context '.valid_type?' do
|
8
|
+
|
9
|
+
context 'for incoming Boolean values' do
|
10
|
+
|
11
|
+
[false, true].each do |value|
|
12
|
+
|
13
|
+
it "returns true for #{value.inspect}" do
|
14
|
+
type.valid_type?(value).should be_true
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
context 'for incoming non-Boolean values' do
|
22
|
+
|
23
|
+
['false', 2, 1.0, Class, Object.new].each do |value|
|
24
|
+
|
25
|
+
it "returns false for #{value.inspect}" do
|
26
|
+
type.valid_type?(value).should be_false
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
context '.example' do
|
36
|
+
it "should return a valid Boolean" do
|
37
|
+
[true, false].should include type.example
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context '.load' do
|
42
|
+
|
43
|
+
context 'for incoming Boolean false values' do
|
44
|
+
|
45
|
+
[false, 'false', 'FALSE', '0', 0, 'f', 'F'].each do |value|
|
46
|
+
|
47
|
+
it "returns false for #{value.inspect}" do
|
48
|
+
type.load(value).should be(false)
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
context 'for incoming Boolean false values' do
|
56
|
+
|
57
|
+
[true, 'true', 'TRUE', '1', 1, 't', 'T'].each do |value|
|
58
|
+
|
59
|
+
it "returns true for #{value.inspect}" do
|
60
|
+
type.load(value).should be(true)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'that are not valid Booleans' do
|
68
|
+
let(:context){ ['root','subattr'] }
|
69
|
+
['string', 2, 1.0, Class, Object.new].each do |value|
|
70
|
+
|
71
|
+
it "raises Attributor::CoercionError for #{value.inspect}" do
|
72
|
+
expect {
|
73
|
+
type.load(value,context)
|
74
|
+
}.to raise_error(Attributor::CoercionError, /Error coercing from .+ to Attributor::Boolean.* #{context.join('.')}/)
|
75
|
+
end
|
76
|
+
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
|
85
|
+
|