dm-validations 0.9.9 → 0.9.10

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.
Files changed (57) hide show
  1. data/History.txt +15 -0
  2. data/Manifest.txt +21 -2
  3. data/lib/dm-validations.rb +14 -14
  4. data/lib/dm-validations/absent_field_validator.rb +3 -4
  5. data/lib/dm-validations/acceptance_validator.rb +6 -10
  6. data/lib/dm-validations/confirmation_validator.rb +5 -5
  7. data/lib/dm-validations/contextual_validators.rb +1 -1
  8. data/lib/dm-validations/custom_validator.rb +1 -1
  9. data/lib/dm-validations/format_validator.rb +6 -5
  10. data/lib/dm-validations/generic_validator.rb +7 -10
  11. data/lib/dm-validations/length_validator.rb +7 -7
  12. data/lib/dm-validations/method_validator.rb +2 -2
  13. data/lib/dm-validations/numeric_validator.rb +4 -4
  14. data/lib/dm-validations/primitive_validator.rb +4 -4
  15. data/lib/dm-validations/required_field_validator.rb +5 -5
  16. data/lib/dm-validations/uniqueness_validator.rb +2 -2
  17. data/lib/dm-validations/validation_errors.rb +34 -2
  18. data/lib/dm-validations/version.rb +1 -1
  19. data/lib/dm-validations/within_validator.rb +15 -13
  20. data/spec/integration/absent_field_validator_spec.rb +4 -2
  21. data/spec/integration/acceptance_validator_spec.rb +3 -3
  22. data/spec/integration/auto_validate_spec.rb +16 -9
  23. data/spec/integration/block_validator_spec.rb +2 -8
  24. data/spec/integration/confirmation_validator_spec.rb +11 -8
  25. data/spec/integration/contextual_validators_spec.rb +2 -1
  26. data/spec/integration/format_validator_spec.rb +1 -1
  27. data/spec/integration/length_validator/error_message_spec.rb +23 -0
  28. data/spec/integration/length_validator/maximum_spec.rb +31 -0
  29. data/spec/integration/length_validator/minimum_spec.rb +31 -0
  30. data/spec/integration/length_validator/range_spec.rb +95 -0
  31. data/spec/integration/length_validator/spec_helper.rb +12 -0
  32. data/spec/integration/length_validator/valid_objects_spec.rb +13 -0
  33. data/spec/integration/method_validator_spec.rb +3 -3
  34. data/spec/integration/numeric_validator/float_type_spec.rb +102 -0
  35. data/spec/integration/numeric_validator/integer_only_true_spec.rb +92 -0
  36. data/spec/integration/numeric_validator/integer_type_spec.rb +100 -0
  37. data/spec/integration/numeric_validator/spec_helper.rb +77 -0
  38. data/spec/integration/numeric_validator_spec.rb +19 -6
  39. data/spec/integration/primitive_validator_spec.rb +2 -1
  40. data/spec/integration/required_field_validator/association_spec.rb +98 -0
  41. data/spec/integration/required_field_validator/boolean_type_value_spec.rb +149 -0
  42. data/spec/integration/required_field_validator/date_type_value_spec.rb +126 -0
  43. data/spec/integration/required_field_validator/datetime_type_value_spec.rb +126 -0
  44. data/spec/integration/required_field_validator/float_type_value_spec.rb +130 -0
  45. data/spec/integration/required_field_validator/integer_type_value_spec.rb +98 -0
  46. data/spec/integration/required_field_validator/plain_old_ruby_object_spec.rb +36 -0
  47. data/spec/integration/required_field_validator/shared_examples.rb +24 -0
  48. data/spec/integration/required_field_validator/spec_helper.rb +68 -0
  49. data/spec/integration/required_field_validator/string_type_value_spec.rb +164 -0
  50. data/spec/integration/required_field_validator/text_type_value_spec.rb +46 -0
  51. data/spec/integration/uniqueness_validator_spec.rb +10 -8
  52. data/spec/integration/validation_spec.rb +25 -25
  53. data/spec/integration/within_validator_spec.rb +36 -11
  54. data/tasks/spec.rb +1 -1
  55. metadata +24 -5
  56. data/spec/integration/length_validator_spec.rb +0 -115
  57. data/spec/integration/required_field_validator_spec.rb +0 -93
@@ -43,8 +43,8 @@ module DataMapper
43
43
  # is target and found resource identic? same instance... but not ==
44
44
  return true if !target.new_record? && resource.repository.name == repository_name && resource.model == target.model && resource.key == target.key
45
45
 
46
- error_message = @options[:message] || "%s is already taken".t(Extlib::Inflection.humanize(field_name))
47
- add_error(target, error_message , field_name)
46
+ error_message = @options[:message] || ValidationErrors.default_error_message(:taken, field_name)
47
+ add_error(target, error_message, field_name)
48
48
 
49
49
  return false
50
50
  end
@@ -9,6 +9,38 @@ module DataMapper
9
9
 
10
10
  include Enumerable
11
11
 
12
+ @@default_error_messages = {
13
+ :absent => '%s must be absent',
14
+ :inclusion => '%s must be one of [%s]',
15
+ :invalid => '%s has an invalid format',
16
+ :confirmation => '%s does not match the confirmation',
17
+ :accepted => "%s is not accepted",
18
+ :nil => '%s must not be nil',
19
+ :blank => '%s must not be blank',
20
+ :length_between => '%s must be between %s and %s characters long',
21
+ :too_long => '%s must be less than %s characters long',
22
+ :too_short => '%s must be more than %s characters long',
23
+ :wrong_length => '%s must be %s characters long',
24
+ :taken => '%s is already taken',
25
+ :not_a_number => '%s must be a number',
26
+ :not_an_integer => '%s must be an integer',
27
+ :greater_than => '%s must be greater than %s',
28
+ :greater_than_or_equal_to => "%s must be greater than or equal to %s",
29
+ :equal_to => "%s must be equal to %s",
30
+ :less_than => '%s must be less than %s',
31
+ :less_than_or_equal_to => "%s must be less than or equal to %s",
32
+ :value_between => '%s must be between %s and %s',
33
+ :primitive => '%s must be of type %s'
34
+ }
35
+
36
+ # Holds a hash with all the default error messages that can be replaced by your own copy or localizations.
37
+ cattr_writer :default_error_messages
38
+
39
+ def self.default_error_message(key, field, *values)
40
+ field = Extlib::Inflection.humanize(field)
41
+ @@default_error_messages[key] % [field, *values].flatten
42
+ end
43
+
12
44
  # Clear existing validation errors.
13
45
  def clear!
14
46
  errors.clear
@@ -25,7 +57,7 @@ module DataMapper
25
57
 
26
58
  # Collect all errors into a single list.
27
59
  def full_messages
28
- errors.inject([]) do |list,pair|
60
+ errors.inject([]) do |list, pair|
29
61
  list += pair.last
30
62
  end
31
63
  end
@@ -39,7 +71,7 @@ module DataMapper
39
71
  end
40
72
 
41
73
  def each
42
- errors.map.each do |k,v|
74
+ errors.map.each do |k, v|
43
75
  next if v.blank?
44
76
  yield(v)
45
77
  end
@@ -1,5 +1,5 @@
1
1
  module DataMapper
2
2
  module Validations
3
- VERSION = '0.9.9'
3
+ VERSION = '0.9.10'
4
4
  end
5
5
  end
@@ -14,23 +14,25 @@ module DataMapper
14
14
  end
15
15
 
16
16
  def call(target)
17
- includes = @options[:set].include?(target.send(field_name))
18
- return true if includes
19
-
20
- field_name = Extlib::Inflection.humanize(@field_name)
21
- if @options[:set].is_a?(Range)
22
- if @options[:set].first != -n && @options[:set].last != n
23
- error_message = @options[:message] || "%s must be between %s and %s".t(field_name, @options[:set].first, @options[:set].last)
24
- elsif @options[:set].first == -n
25
- error_message = @options[:message] || "%s must be less than %s".t(field_name, @options[:set].last)
26
- elsif @options[:set].last == n
27
- error_message = @options[:message] || "%s must be greater than %s".t(field_name, @options[:set].first)
17
+ value = target.send(field_name)
18
+ return true if @options[:allow_nil] && value.nil?
19
+ return true if @options[:set].include?(value)
20
+
21
+ set = @options[:set]
22
+ msg = @options[:message]
23
+ if set.is_a?(Range)
24
+ if set.first != -n && set.last != n
25
+ error_message = msg || ValidationErrors.default_error_message(:value_between, field_name, set.first, set.last)
26
+ elsif set.first == -n
27
+ error_message = msg || ValidationErrors.default_error_message(:less_than_or_equal_to, field_name, set.last)
28
+ elsif set.last == n
29
+ error_message = msg || ValidationErrors.default_error_message(:greater_than_or_equal_to, field_name, set.first)
28
30
  end
29
31
  else
30
- error_message = "%s must be one of [%s]".t(field_name, @options[:set].join(', '))
32
+ error_message = ValidationErrors.default_error_message(:inclusion, field_name, set.join(', '))
31
33
  end
32
34
 
33
- add_error(target, error_message , @field_name)
35
+ add_error(target, error_message, field_name)
34
36
  return false
35
37
  end
36
38
 
@@ -3,14 +3,14 @@ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
3
 
4
4
  describe DataMapper::Validate::AbsentFieldValidator do
5
5
  before(:all) do
6
- class Kayak
6
+ class ::Kayak
7
7
  include DataMapper::Resource
8
8
  property :id, Integer, :key => true
9
9
  property :salesman, String, :auto_validation => false
10
10
  validates_absent :salesman, :when => :sold
11
11
  end
12
12
 
13
- class Pirogue
13
+ class ::Pirogue
14
14
  include DataMapper::Resource
15
15
  property :id, Integer, :key => true
16
16
  property :salesman, String, :default => 'Layfayette'
@@ -24,11 +24,13 @@ describe DataMapper::Validate::AbsentFieldValidator do
24
24
 
25
25
  kayak.salesman = 'Joe'
26
26
  kayak.valid_for_sold?.should_not == true
27
+ kayak.errors.on(:salesman).should include('Salesman must be absent')
27
28
  end
28
29
 
29
30
  it "should validate the absence of a value and ensure defaults" do
30
31
  pirogue = Pirogue.new
31
32
  pirogue.should_not be_valid_for_sold
33
+ pirogue.errors.on(:salesman).should include('Salesman must be absent')
32
34
  end
33
35
 
34
36
  end
@@ -4,7 +4,7 @@ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
4
4
  describe DataMapper::Validate::AcceptanceValidator do
5
5
  describe "with standard options" do
6
6
  before(:all) do
7
- class SkimBat
7
+ class ::SkimBat
8
8
  include DataMapper::Resource
9
9
  property :id, Integer, :serial => true
10
10
  property :sailyness, Boolean
@@ -31,7 +31,7 @@ describe DataMapper::Validate::AcceptanceValidator do
31
31
  it "should add the default message when invalid" do
32
32
  @s.sailyness = "0"
33
33
  @s.valid?.should == false
34
- @s.errors.full_messages.join(" ").should =~ /#{DataMapper::Validate::AcceptanceValidator.default_message_for_field("sailyness")}/
34
+ @s.errors.on(:sailyness).should include('Sailyness is not accepted')
35
35
  end
36
36
  end
37
37
  describe "with :allow_nil => false" do
@@ -81,7 +81,7 @@ describe DataMapper::Validate::AcceptanceValidator do
81
81
  it "should append the custom message when invalid" do
82
82
  @s.sailyness = "0"
83
83
  @s.valid?.should == false
84
- @s.errors.full_messages.join(" ").should =~ /hehu!/
84
+ @s.errors.on(:sailyness).should include('hehu!')
85
85
  end
86
86
  end
87
87
  end
@@ -4,7 +4,7 @@ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
4
4
  module TypecastBypassSetter
5
5
  # Bypass typecasting so we can set values for specs
6
6
  def set(attributes)
7
- attributes.each do |k,v|
7
+ attributes.each do |k, v|
8
8
  instance_variable_set("@#{k}", v)
9
9
  end
10
10
  end
@@ -65,6 +65,7 @@ describe "Automatic Validation from Property Definition" do
65
65
 
66
66
  boat = SailBoat.new
67
67
  boat.valid_for_presence_test?.should == false
68
+ boat.errors.on(:name).should include('Name must not be blank')
68
69
  boat.name = 'Float'
69
70
  boat.valid_for_presence_test?.should == true
70
71
  end
@@ -75,6 +76,7 @@ describe "Automatic Validation from Property Definition" do
75
76
  boat.valid_for_length_test_1?.should == true #no minimum length
76
77
  boat.description = 'ABCDEFGHIJK' #11
77
78
  boat.valid_for_length_test_1?.should == false
79
+ boat.errors.on(:description).should include('Description must be less than 10 characters long')
78
80
  boat.description = 'ABCDEFGHIJ' #10
79
81
  boat.valid_for_length_test_1?.should == true
80
82
  end
@@ -88,6 +90,7 @@ describe "Automatic Validation from Property Definition" do
88
90
  boat.should be_valid_for_length_test_2
89
91
  boat.notes = 'ABCDEFGHIJK' #11
90
92
  boat.should_not be_valid_for_length_test_2
93
+ boat.errors.on(:notes).should include('Notes must be between 2 and 10 characters long')
91
94
  boat.notes = 'ABCDEFGHIJ' #10
92
95
  boat.should be_valid_for_length_test_2
93
96
  end
@@ -100,6 +103,7 @@ describe "Automatic Validation from Property Definition" do
100
103
  boat.should be_valid_for_format_test
101
104
  boat.code = 'BAD CODE'
102
105
  boat.should_not be_valid_for_format_test
106
+ boat.errors.on(:code).should include('Code has an invalid format')
103
107
  end
104
108
 
105
109
  it "should auto validate all strings for max length" do
@@ -110,9 +114,9 @@ describe "Automatic Validation from Property Definition" do
110
114
  end
111
115
  t = klass.new(:id => 1)
112
116
  t.should be_valid
113
- t.name = 'Lip­smackin­thirst­quenchin­acetastin­motivatin­good­buzzin­cool­talkin­high­walkin­fast­livin­ever­givin­cool­fizzin'
117
+ t.name = 'a' * 51
114
118
  t.should_not be_valid
115
- t.errors.full_messages.should include('Name must be less than 50 characters long')
119
+ t.errors.on(:name).should include('Name must be less than 50 characters long')
116
120
  end
117
121
 
118
122
  it "should auto validate the primitive type" do
@@ -122,6 +126,7 @@ describe "Automatic Validation from Property Definition" do
122
126
  boat.should be_valid_for_primitive_test
123
127
  boat.build_date = 'ABC'
124
128
  boat.should_not be_valid_for_primitive_test
129
+ boat.errors.on(:build_date).should include('Build date must be of type Date')
125
130
  end
126
131
 
127
132
  it "should not auto add any validators if the option :auto_validation => false was given" do
@@ -164,13 +169,13 @@ describe "Automatic Validation from Property Definition" do
164
169
  it 'should not allow floats' do
165
170
  @boat.set(:id => 1.0)
166
171
  @boat.should_not be_valid
167
- @boat.errors.on(:id).should == [ 'Id must be an integer' ]
172
+ @boat.errors.on(:id).should include('Id must be an integer')
168
173
  end
169
174
 
170
175
  it 'should not allow decimals' do
171
176
  @boat.set(:id => BigDecimal('1'))
172
177
  @boat.should_not be_valid
173
- @boat.errors.on(:id).should == [ 'Id must be an integer' ]
178
+ @boat.errors.on(:id).should include('Id must be an integer')
174
179
  end
175
180
  end
176
181
 
@@ -213,6 +218,7 @@ describe "Automatic Validation from Property Definition" do
213
218
  it 'should not allow nil' do
214
219
  @boat.set(:bool => nil)
215
220
  @boat.should_not be_valid
221
+ @boat.errors.on(:bool).should include('Bool must not be nil')
216
222
  end
217
223
  end
218
224
 
@@ -234,10 +240,11 @@ describe "Automatic Validation from Property Definition" do
234
240
  it 'should not allow nil' do
235
241
  @boat.set(:bool => nil)
236
242
  @boat.should_not be_valid
243
+ @boat.errors.on(:bool).should include('Bool must not be nil')
237
244
  end
238
245
  end
239
246
 
240
- { :float => Float, :big_decimal => BigDecimal }.each do |column,type|
247
+ { :float => Float, :big_decimal => BigDecimal }.each do |column, type|
241
248
  describe "for #{type} properties" do
242
249
  before do
243
250
  @boat = SailBoat.new(:id => 1)
@@ -262,7 +269,7 @@ describe "Automatic Validation from Property Definition" do
262
269
 
263
270
  describe 'for within validator' do
264
271
  before :all do
265
- class LimitedBoat
272
+ class ::LimitedBoat
266
273
  include DataMapper::Resource
267
274
  property :id, Integer, :serial => true
268
275
  property :limited, String, :set => ['foo', 'bar', 'bang'], :default => 'foo'
@@ -280,6 +287,7 @@ describe "Automatic Validation from Property Definition" do
280
287
  it 'should not accept value not in range' do
281
288
  @boat.limited = "blah"
282
289
  @boat.should_not be_valid
290
+ @boat.errors.on(:limited).should include('Limited must be one of [foo, bar, bang]')
283
291
  end
284
292
 
285
293
  end
@@ -293,8 +301,7 @@ describe "Automatic Validation from Property Definition" do
293
301
  end
294
302
  boat = custom_boat.new
295
303
  boat.should_not be_valid
296
-
297
- boat.errors.on(:name).should eql(["This boat must have name"])
304
+ boat.errors.on(:name).should include('This boat must have name')
298
305
  end
299
306
 
300
307
  it "should have correct error messages" do
@@ -3,7 +3,7 @@ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
3
 
4
4
  describe DataMapper::Validate::ValidatesWithBlock do
5
5
  before(:all) do
6
- class Ship
6
+ class ::Ship
7
7
  include DataMapper::Resource
8
8
  property :id, Integer, :key => true
9
9
  property :name, String
@@ -20,13 +20,7 @@ describe DataMapper::Validate::ValidatesWithBlock do
20
20
  end
21
21
  end
22
22
 
23
- it "should validate via a block on the resource" do
24
- Ship.new.valid_for_testing_failure?.should == false
25
- Ship.new.valid_for_testing_success?.should == true
26
- ship = Ship.new
27
- ship.valid_for_testing_failure?.should == false
28
- ship.errors.full_messages.include?('Validation failed').should == true
29
- end
23
+ it "should validate via a block on the resource"
30
24
 
31
25
  it "should validate via a block and add error to field" do
32
26
  ship = Ship.new
@@ -3,7 +3,7 @@ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
3
 
4
4
  describe DataMapper::Validate::ConfirmationValidator do
5
5
  before(:all) do
6
- class Canoe
6
+ class ::Canoe
7
7
  include DataMapper::Resource
8
8
 
9
9
  property :id, Integer, :serial => true
@@ -16,7 +16,7 @@ describe DataMapper::Validate::ConfirmationValidator do
16
16
  end
17
17
 
18
18
  it "should only validate if the attribute is dirty" do
19
- class Transformer
19
+ class ::Transformer
20
20
  include DataMapper::Resource
21
21
 
22
22
  property :id, Integer, :serial => true
@@ -41,6 +41,7 @@ describe DataMapper::Validate::ConfirmationValidator do
41
41
  canoe.name = 'White Water'
42
42
  canoe.name_confirmation = 'Not confirmed'
43
43
  canoe.should_not be_valid
44
+ canoe.errors.on(:name).should include('Name does not match the confirmation')
44
45
  canoe.errors.full_messages.first.should == 'Name does not match the confirmation'
45
46
 
46
47
  canoe.name_confirmation = 'White Water'
@@ -60,15 +61,17 @@ describe DataMapper::Validate::ConfirmationValidator do
60
61
  end
61
62
 
62
63
  it "should not pass validation with a nil value when specified to" do
63
- class Canoe
64
+ class ::Canoe
64
65
  validators.clear!
65
66
  validates_is_confirmed :name, :allow_nil => false
66
67
  end
67
- Canoe.new.should_not be_valid
68
+ canoe = Canoe.new
69
+ canoe.should_not be_valid
70
+ canoe.errors.on(:name).should include('Name does not match the confirmation')
68
71
  end
69
72
 
70
73
  it "should allow the name of the confirmation field to be set" do
71
- class Canoe
74
+ class ::Canoe
72
75
  validators.clear!
73
76
  validates_is_confirmed :name, :confirm => :name_check
74
77
  def name_check=(value)
@@ -86,7 +89,7 @@ describe DataMapper::Validate::ConfirmationValidator do
86
89
  end
87
90
 
88
91
  it "should not require that the confirmation field be a property" do
89
- class Raft
92
+ class ::Raft
90
93
  include DataMapper::Resource
91
94
  attr_accessor :name, :name_confirmation
92
95
 
@@ -95,8 +98,8 @@ describe DataMapper::Validate::ConfirmationValidator do
95
98
  validates_is_confirmed :name
96
99
  end
97
100
 
98
- raft = Raft.new
101
+ raft = Raft.new(:id => 10)
99
102
  raft.name = 'Lifeboat'
100
- lambda { raft.should_not be_valid }.should_not raise_error
103
+ raft.should_not be_valid
101
104
  end
102
105
  end
@@ -4,7 +4,7 @@ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
4
4
  describe DataMapper::Validate::ContextualValidators do
5
5
 
6
6
  before :all do
7
- class Kayak
7
+ class ::Kayak
8
8
  include DataMapper::Resource
9
9
  property :id, Integer, :key => true
10
10
  property :salesman, String, :auto_validation => false
@@ -17,6 +17,7 @@ describe DataMapper::Validate::ContextualValidators do
17
17
  k.valid?(:sold).should == true
18
18
  k.salesman = 'John Doe'
19
19
  k.valid?(:sold).should_not == true
20
+ k.errors.on(:salesman).should include('Salesman must be absent')
20
21
  end
21
22
 
22
23
  it "should raise an exception if you provide an invalid context to save" do
@@ -3,7 +3,7 @@ require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
3
3
 
4
4
  describe DataMapper::Validate::FormatValidator do
5
5
  before :all do
6
- class BillOfLading
6
+ class ::BillOfLading
7
7
  include DataMapper::Resource
8
8
 
9
9
  property :id, Serial
@@ -0,0 +1,23 @@
1
+ require 'pathname'
2
+ __dir__ = Pathname(__FILE__).dirname.expand_path
3
+
4
+ # global first, then local to length validators
5
+ require __dir__.parent.parent + "spec_helper"
6
+ require __dir__ + 'spec_helper'
7
+
8
+ describe DataMapper::Validate::LengthValidator do
9
+ it "lets user specify custom error message" do
10
+ class Jabberwock
11
+ include DataMapper::Resource
12
+ property :id, Integer, :key => true
13
+ property :snickersnack, String
14
+ validates_length :snickersnack, :within => 3..40, :message => "worble warble"
15
+ end
16
+ wock = Jabberwock.new
17
+ wock.should_not be_valid
18
+ wock.errors.on(:snickersnack).should include('worble warble')
19
+ wock.snickersnack = "hello"
20
+ wock.id = 1
21
+ wock.should be_valid
22
+ end
23
+ end
@@ -0,0 +1,31 @@
1
+ require 'pathname'
2
+ __dir__ = Pathname(__FILE__).dirname.expand_path
3
+
4
+ # global first, then local to length validators
5
+ require __dir__.parent.parent + "spec_helper"
6
+ require __dir__ + 'spec_helper'
7
+
8
+ describe DataMapper::Validate::LengthValidator do
9
+ it "lets user specify a maximum length of a string field" do
10
+ class ::MotorLaunch
11
+ validators.clear!
12
+ validates_length :name, :max => 5
13
+ end
14
+
15
+ launch = MotorLaunch.new
16
+ launch.name = 'a' * 6
17
+ launch.should_not be_valid
18
+ launch.errors.on(:name).should include('Name must be less than 5 characters long')
19
+ end
20
+
21
+ it "aliases :maximum for :max" do
22
+ class ::MotorLaunch
23
+ validators.clear!
24
+ validates_length :name, :maximum => 5
25
+ end
26
+ launch = MotorLaunch.new
27
+ launch.name = 'a' * 6
28
+ launch.should_not be_valid
29
+ launch.errors.on(:name).should include('Name must be less than 5 characters long')
30
+ end
31
+ end