ward 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.gitignore +28 -0
- data/LICENSE +19 -0
- data/README.markdown +99 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/features/acceptance_matcher.feature +78 -0
- data/features/attribute_keyword.feature +13 -0
- data/features/close_to_matcher.feature +130 -0
- data/features/context_arguments.feature +47 -0
- data/features/equal_to_matcher.feature +25 -0
- data/features/error_messages.feature +69 -0
- data/features/external_validation.feature +15 -0
- data/features/has_matcher.feature +72 -0
- data/features/has_matcher_initialized_with_expectation.feature +94 -0
- data/features/has_matcher_relativities.feature +171 -0
- data/features/include_matcher.feature +28 -0
- data/features/is_keyword.feature +42 -0
- data/features/is_not_keyword.feature +62 -0
- data/features/match_matcher.feature +49 -0
- data/features/multiple_validators.feature +29 -0
- data/features/nil_matcher.feature +25 -0
- data/features/predicate_matcher.feature +23 -0
- data/features/present_matcher.feature +59 -0
- data/features/satisfy_matcher.feature +80 -0
- data/features/scenario_validation.feature +81 -0
- data/features/step_definitions/external_validation_steps.rb +69 -0
- data/features/step_definitions/generic_validation_steps.rb +33 -0
- data/features/step_definitions/object_definition_steps.rb +43 -0
- data/features/support/env.rb +12 -0
- data/features/support/object_builder.rb +33 -0
- data/features/support/struct.rb +38 -0
- data/lang/en.yml +56 -0
- data/lib/ward.rb +26 -0
- data/lib/ward/context.rb +70 -0
- data/lib/ward/context_chain.rb +87 -0
- data/lib/ward/dsl.rb +7 -0
- data/lib/ward/dsl/validation_block.rb +73 -0
- data/lib/ward/dsl/validation_builder.rb +190 -0
- data/lib/ward/errors.rb +213 -0
- data/lib/ward/matchers.rb +97 -0
- data/lib/ward/matchers/acceptance.rb +43 -0
- data/lib/ward/matchers/close_to.rb +60 -0
- data/lib/ward/matchers/equal_to.rb +33 -0
- data/lib/ward/matchers/has.rb +283 -0
- data/lib/ward/matchers/include.rb +54 -0
- data/lib/ward/matchers/match.rb +29 -0
- data/lib/ward/matchers/matcher.rb +68 -0
- data/lib/ward/matchers/nil.rb +30 -0
- data/lib/ward/matchers/predicate.rb +31 -0
- data/lib/ward/matchers/present.rb +56 -0
- data/lib/ward/matchers/satisfy.rb +65 -0
- data/lib/ward/spec.rb +17 -0
- data/lib/ward/spec/matcher_matcher.rb +114 -0
- data/lib/ward/support.rb +7 -0
- data/lib/ward/support/basic_object.rb +55 -0
- data/lib/ward/support/result.rb +49 -0
- data/lib/ward/validator.rb +147 -0
- data/lib/ward/validator_set.rb +115 -0
- data/lib/ward/version.rb +3 -0
- data/spec/lib/has_matcher_relativity_examples.rb +15 -0
- data/spec/lib/have_public_method_defined.rb +22 -0
- data/spec/rcov.opts +8 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/ward/context_chain_spec.rb +178 -0
- data/spec/ward/context_spec.rb +57 -0
- data/spec/ward/dsl/validation_block_spec.rb +27 -0
- data/spec/ward/dsl/validation_builder_spec.rb +212 -0
- data/spec/ward/errors_spec.rb +149 -0
- data/spec/ward/matchers/acceptance_spec.rb +16 -0
- data/spec/ward/matchers/close_to_spec.rb +57 -0
- data/spec/ward/matchers/equal_to_spec.rb +16 -0
- data/spec/ward/matchers/has_spec.rb +175 -0
- data/spec/ward/matchers/include_spec.rb +41 -0
- data/spec/ward/matchers/match_spec.rb +21 -0
- data/spec/ward/matchers/matcher_spec.rb +54 -0
- data/spec/ward/matchers/nil_spec.rb +16 -0
- data/spec/ward/matchers/predicate_spec.rb +19 -0
- data/spec/ward/matchers/present_spec.rb +16 -0
- data/spec/ward/matchers/satisfy_spec.rb +68 -0
- data/spec/ward/matchers_spec.rb +51 -0
- data/spec/ward/spec/have_public_method_defined_spec.rb +31 -0
- data/spec/ward/spec/matcher_matcher_spec.rb +217 -0
- data/spec/ward/validator_set_spec.rb +178 -0
- data/spec/ward/validator_spec.rb +264 -0
- data/tasks/features.rake +15 -0
- data/tasks/rcov.rake +24 -0
- data/tasks/spec.rake +18 -0
- data/tasks/yard.rake +9 -0
- data/ward.gemspec +176 -0
- metadata +239 -0
@@ -0,0 +1,178 @@
|
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Ward::ContextChain do
|
4
|
+
subject { Ward::ContextChain }
|
5
|
+
|
6
|
+
#
|
7
|
+
# attribute
|
8
|
+
#
|
9
|
+
|
10
|
+
it { should have_public_method_defined(:attribute) }
|
11
|
+
|
12
|
+
describe '#attribute' do
|
13
|
+
before(:all) do
|
14
|
+
@chain = Ward::ContextChain.new
|
15
|
+
@chain.push(Ward::Context.new(:name))
|
16
|
+
@chain.push(Ward::Context.new(:length))
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'should return a symbol' do
|
20
|
+
@chain.attribute.should be_a(Symbol)
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should return the first Context's attribute" do
|
24
|
+
@chain.attribute.should == :name
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should return :base when there are no contexts contained' do
|
28
|
+
Ward::ContextChain.new.attribute.should == :base
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
#
|
33
|
+
# to_a
|
34
|
+
#
|
35
|
+
|
36
|
+
it { should have_public_method_defined(:to_a) }
|
37
|
+
|
38
|
+
#
|
39
|
+
# push
|
40
|
+
#
|
41
|
+
|
42
|
+
it { should have_public_method_defined(:push) }
|
43
|
+
it { should have_public_method_defined(:<<) }
|
44
|
+
|
45
|
+
describe '#push' do
|
46
|
+
it 'should add a context to the end of the chain' do
|
47
|
+
chain = Ward::ContextChain.new
|
48
|
+
new_context = Ward::Context.new(:length)
|
49
|
+
chain.to_a.should be_empty
|
50
|
+
chain.push(new_context)
|
51
|
+
chain.to_a.should eql([new_context])
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
#
|
56
|
+
# natural name
|
57
|
+
#
|
58
|
+
|
59
|
+
it { should have_public_method_defined(:natural_name) }
|
60
|
+
|
61
|
+
describe '#natural_name' do
|
62
|
+
describe 'when the chain contains no contexts' do
|
63
|
+
it 'should return a blank string' do
|
64
|
+
Ward::ContextChain.new.natural_name.should eql('')
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe 'when the chain contains a single context' do
|
69
|
+
before(:all) do
|
70
|
+
@chain = Ward::ContextChain.new
|
71
|
+
@chain << Ward::Context.new(:length)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should return the context natural name' do
|
75
|
+
@chain.natural_name.should eql('length')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'when the chain contains a two (or more) contexts' do
|
80
|
+
before(:all) do
|
81
|
+
@chain = Ward::ContextChain.new
|
82
|
+
@chain << Ward::Context.new(:post)
|
83
|
+
@chain << Ward::Context.new(:name)
|
84
|
+
end
|
85
|
+
|
86
|
+
it 'should join the natural names of each context' do
|
87
|
+
@chain.natural_name.should eql('post name')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# value
|
94
|
+
#
|
95
|
+
|
96
|
+
it { should have_public_method_defined(:value) }
|
97
|
+
|
98
|
+
describe '#value' do
|
99
|
+
describe 'when the chain contains no context' do
|
100
|
+
before(:all) do
|
101
|
+
@chain = Ward::ContextChain.new
|
102
|
+
end
|
103
|
+
|
104
|
+
it 'should return the given object' do
|
105
|
+
@chain.value('this').should == 'this'
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe 'when the chain contains a single context' do
|
110
|
+
before(:all) do
|
111
|
+
@chain = Ward::ContextChain.new
|
112
|
+
@chain << Ward::Context.new(:length)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'should retrieve the value when set' do
|
116
|
+
@chain.value('abc').should eql(3)
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'should retrieve a nil value without an error' do
|
120
|
+
@chain.value(Struct.new(:length).new(nil)).should be_nil
|
121
|
+
end
|
122
|
+
end # when the chain contains a single context
|
123
|
+
|
124
|
+
describe 'when the chain contains two contexts' do
|
125
|
+
before(:all) do
|
126
|
+
@chain = Ward::ContextChain.new
|
127
|
+
@chain << Ward::Context.new(:post)
|
128
|
+
@chain << Ward::Context.new(:name)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'should retrieve the value when set' do
|
132
|
+
target = Struct.new(:post).new(Struct.new(:name).new('Hello!'))
|
133
|
+
@chain.value(target).should eql('Hello!')
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'should raise an error if the first context value is nil' do
|
137
|
+
lambda { @chain.value(nil) }.should raise_error
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'should not raise an error if the second context value is nil' do
|
141
|
+
target = Struct.new(:post).new(Struct.new(:name).new(nil))
|
142
|
+
@chain.value(target).should be_nil
|
143
|
+
end
|
144
|
+
end # when the chain contains two contexts
|
145
|
+
|
146
|
+
describe 'when the chain contains three (or more) contexts' do
|
147
|
+
before(:all) do
|
148
|
+
@chain = Ward::ContextChain.new
|
149
|
+
@chain << Ward::Context.new(:post)
|
150
|
+
@chain << Ward::Context.new(:name)
|
151
|
+
@chain << Ward::Context.new(:length)
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'should retrieve the value when set' do
|
155
|
+
target = Struct.new(:post).new(Struct.new(:name).new('abc'))
|
156
|
+
@chain.value(target).should eql(3)
|
157
|
+
end
|
158
|
+
|
159
|
+
it 'should raise an error if the first context value is nil' do
|
160
|
+
lambda { @chain.value(nil) }.should raise_error
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'should raise an error if the second context value is nil' do
|
164
|
+
target = Struct.new(:post).new(Struct.new(:name).new(nil))
|
165
|
+
lambda { @chain.value(target) }.should raise_error
|
166
|
+
end
|
167
|
+
|
168
|
+
it 'should not raise an error if the second context value is nil' do
|
169
|
+
target = Struct.new(:post).new(
|
170
|
+
Struct.new(:name).new(Struct.new(:length).new(nil))
|
171
|
+
)
|
172
|
+
|
173
|
+
@chain.value(target).should be_nil
|
174
|
+
end
|
175
|
+
end # when the chain contains three (or more) contexts
|
176
|
+
end # value
|
177
|
+
|
178
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Ward::Context do
|
4
|
+
subject { Ward::Context }
|
5
|
+
|
6
|
+
#
|
7
|
+
# attribute
|
8
|
+
#
|
9
|
+
|
10
|
+
it { should have_public_method_defined(:attribute) }
|
11
|
+
|
12
|
+
describe '#attribute' do
|
13
|
+
subject do
|
14
|
+
@attribute = Ward::Context.new('full_name').attribute
|
15
|
+
end
|
16
|
+
|
17
|
+
it { should be_a(Symbol) }
|
18
|
+
it { should eql(:full_name) }
|
19
|
+
end
|
20
|
+
|
21
|
+
#
|
22
|
+
# natural name
|
23
|
+
#
|
24
|
+
|
25
|
+
it { should have_public_method_defined(:natural_name) }
|
26
|
+
|
27
|
+
describe '#natural_name' do
|
28
|
+
subject do
|
29
|
+
@attribute = Ward::Context.new('full_name').natural_name
|
30
|
+
end
|
31
|
+
|
32
|
+
it { should be_a(String) }
|
33
|
+
it { should eql('full name') }
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
# value
|
38
|
+
#
|
39
|
+
|
40
|
+
it { should have_public_method_defined(:value) }
|
41
|
+
|
42
|
+
describe '#value' do
|
43
|
+
before(:all) do
|
44
|
+
@matcher = Ward::Context.new(:length)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should retrieve the target value' do
|
48
|
+
@matcher.value('abc').should == 3
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'should raise an error when the target does not respond to the ' \
|
52
|
+
'target method' do
|
53
|
+
lambda { @matcher.value(nil) }.should raise_exception(NoMethodError)
|
54
|
+
end
|
55
|
+
end # value
|
56
|
+
|
57
|
+
end # Ward::Context
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Ward::DSL::ValidationBlock do
|
4
|
+
|
5
|
+
#
|
6
|
+
# to_validator_set
|
7
|
+
#
|
8
|
+
|
9
|
+
describe '#to_validator_set' do
|
10
|
+
it 'should return a Ward::ValidatorSet' do
|
11
|
+
set = Ward::DSL::ValidationBlock.new.to_validator_set
|
12
|
+
set.should be_a(Ward::ValidatorSet)
|
13
|
+
end
|
14
|
+
end # to_validator_set
|
15
|
+
|
16
|
+
#
|
17
|
+
# method_missing
|
18
|
+
#
|
19
|
+
|
20
|
+
describe '#method_missing' do
|
21
|
+
it 'should raise an exception when used outside of a run block' do
|
22
|
+
running = lambda { Ward::DSL::ValidationBlock.new.name }
|
23
|
+
running.should raise_error(RuntimeError, /a block/)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,212 @@
|
|
1
|
+
require File.expand_path('../../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe Ward::DSL::ValidationBuilder do
|
4
|
+
subject { Ward::DSL::ValidationBuilder }
|
5
|
+
|
6
|
+
#
|
7
|
+
# when setting a matcher
|
8
|
+
#
|
9
|
+
|
10
|
+
describe 'when setting a matcher' do
|
11
|
+
before(:each) do
|
12
|
+
@builder = Ward::DSL::ValidationBuilder.new
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should set an Acceptance matcher when calling #accepted" do
|
16
|
+
validator = @builder.is.accepted.to_validator
|
17
|
+
validator.matcher.should be_a(Ward::Matchers::Acceptance)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should set a CloseTo matcher when calling #close_to" do
|
21
|
+
validator = @builder.is.close_to(1, 1).to_validator
|
22
|
+
validator.matcher.should be_a(Ward::Matchers::CloseTo)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should set an EqualTo matcher when calling #equal_to" do
|
26
|
+
validator = @builder.is.equal_to(1).to_validator
|
27
|
+
validator.matcher.should be_a(Ward::Matchers::EqualTo)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should set a Has matcher when calling #has" do
|
31
|
+
validator = @builder.has.to_validator
|
32
|
+
validator.matcher.should be_a(Ward::Matchers::Has)
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should set a Has matcher when calling #have" do
|
36
|
+
validator = @builder.have(1, 1).to_validator
|
37
|
+
validator.matcher.should be_a(Ward::Matchers::Has)
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should set an Include matcher when calling #included_in" do
|
41
|
+
validator = @builder.is.included_in([]).to_validator
|
42
|
+
validator.matcher.should be_a(Ward::Matchers::Include)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should set a Match matcher when calling #matches" do
|
46
|
+
validator = @builder.matches(//).to_validator
|
47
|
+
validator.matcher.should be_a(Ward::Matchers::Match)
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should set a Match matcher when calling #match" do
|
51
|
+
validator = @builder.match(//).to_validator
|
52
|
+
validator.matcher.should be_a(Ward::Matchers::Match)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should set a Nil matcher when calling #nil" do
|
56
|
+
validator = @builder.is.nil.to_validator
|
57
|
+
validator.matcher.should be_a(Ward::Matchers::Nil)
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should set a Present matcher when calling #present" do
|
61
|
+
validator = @builder.is.present.to_validator
|
62
|
+
validator.matcher.should be_a(Ward::Matchers::Present)
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should set a Satisfy matcher when calling #satisfies" do
|
66
|
+
validator = @builder.satisfies.to_validator
|
67
|
+
validator.matcher.should be_a(Ward::Matchers::Satisfy)
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should set a Satisfy matcher when calling #satisfy" do
|
71
|
+
validator = @builder.satisfy.to_validator
|
72
|
+
validator.matcher.should be_a(Ward::Matchers::Satisfy)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
#
|
77
|
+
# customising a matcher
|
78
|
+
#
|
79
|
+
|
80
|
+
describe 'calling a missing method when a matcher is set' do
|
81
|
+
it 'should call methods on the matcher' do
|
82
|
+
builder = Ward::DSL::ValidationBuilder.new.has
|
83
|
+
builder.to_validator.matcher.expected.should == 1 # default
|
84
|
+
builder.at_least(5)
|
85
|
+
builder.to_validator.matcher.expected.should == 5
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
#
|
90
|
+
# when setting a context name
|
91
|
+
#
|
92
|
+
|
93
|
+
describe '#attribute' do
|
94
|
+
before(:each) do
|
95
|
+
@builder = Ward::DSL::ValidationBuilder.new
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should return the builder' do
|
99
|
+
@builder.attribute(:name).should be(@builder)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
#
|
104
|
+
# when setting a message
|
105
|
+
#
|
106
|
+
|
107
|
+
describe '#message' do
|
108
|
+
before(:each) do
|
109
|
+
@builder = Ward::DSL::ValidationBuilder.new.is.present
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should return the builder' do
|
113
|
+
@builder.message('A validation error message').should be(@builder)
|
114
|
+
end
|
115
|
+
|
116
|
+
it 'should set the validation message' do
|
117
|
+
validator = @builder.message('A validation error message').to_validator
|
118
|
+
validator.message.should == 'A validation error message'
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe 'with no message' do
|
123
|
+
before(:each) do
|
124
|
+
@builder = Ward::DSL::ValidationBuilder.new.is.present
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'should set the validation message' do
|
128
|
+
validator = @builder.to_validator
|
129
|
+
validator.message.should be_nil
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
#
|
134
|
+
# when setting a scenario
|
135
|
+
#
|
136
|
+
|
137
|
+
describe '#scenario' do
|
138
|
+
before(:each) do
|
139
|
+
@builder = Ward::DSL::ValidationBuilder.new.is.present
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'should return the builder' do
|
143
|
+
@builder.scenario(:scenario).should be(@builder)
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'should set the validation scenario' do
|
147
|
+
validator = @builder.scenario(:scenario).to_validator
|
148
|
+
validator.scenarios.should == [:scenario]
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
describe '#scenarios' do
|
153
|
+
before(:each) do
|
154
|
+
@builder = Ward::DSL::ValidationBuilder.new.is.present
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should return the builder' do
|
158
|
+
@builder.scenarios([:scenario]).should be(@builder)
|
159
|
+
end
|
160
|
+
|
161
|
+
it 'should set multiple scenarios when given an array' do
|
162
|
+
validator = @builder.scenarios([:one, :two]).to_validator
|
163
|
+
validator.scenarios.should == [:one, :two]
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'should set multiple scenarios when given a multiple arguments' do
|
167
|
+
validator = @builder.scenarios(:one, :two).to_validator
|
168
|
+
validator.scenarios.should == [:one, :two]
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
#
|
173
|
+
# to_validator
|
174
|
+
#
|
175
|
+
|
176
|
+
it { should have_public_method_defined(:to_validator) }
|
177
|
+
|
178
|
+
describe '#to_validator' do
|
179
|
+
describe 'when no matcher is defined' do
|
180
|
+
it 'should raise an IncompleteValidator exception' do
|
181
|
+
running = lambda { Ward::DSL::ValidationBuilder.new.to_validator }
|
182
|
+
running.should raise_exception(Ward::IncompleteValidator)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe 'when a matcher is defined' do
|
187
|
+
before(:all) do
|
188
|
+
builder = Ward::DSL::ValidationBuilder.new.author.name
|
189
|
+
builder.is.equal_to(1)
|
190
|
+
@validator = builder.to_validator
|
191
|
+
end
|
192
|
+
|
193
|
+
it 'should return a Ward::Validator' do
|
194
|
+
@validator.should be_a(Ward::Validator)
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'should set the matcher' do
|
198
|
+
@validator.matcher.should be_a(Ward::Matchers::Matcher)
|
199
|
+
end
|
200
|
+
|
201
|
+
it 'should set the matcher expectation' do
|
202
|
+
@validator.matcher.expected.should == 1
|
203
|
+
end
|
204
|
+
|
205
|
+
it 'should set the context' do
|
206
|
+
attributes = @validator.context.to_a.map { |c| c.attribute }
|
207
|
+
attributes.should == [:author, :name]
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
end
|