yetanothernguyen-shoulda-matchers 1.0.0.beta3
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.
- data/Appraisals +12 -0
- data/CONTRIBUTION_GUIDELINES.rdoc +10 -0
- data/Gemfile +5 -0
- data/MIT-LICENSE +22 -0
- data/README.rdoc +78 -0
- data/Rakefile +55 -0
- data/lib/shoulda-matchers.rb +1 -0
- data/lib/shoulda/matchers.rb +8 -0
- data/lib/shoulda/matchers/action_controller.rb +38 -0
- data/lib/shoulda/matchers/action_controller/assign_to_matcher.rb +114 -0
- data/lib/shoulda/matchers/action_controller/filter_param_matcher.rb +50 -0
- data/lib/shoulda/matchers/action_controller/redirect_to_matcher.rb +62 -0
- data/lib/shoulda/matchers/action_controller/render_template_matcher.rb +54 -0
- data/lib/shoulda/matchers/action_controller/render_with_layout_matcher.rb +99 -0
- data/lib/shoulda/matchers/action_controller/respond_with_content_type_matcher.rb +74 -0
- data/lib/shoulda/matchers/action_controller/respond_with_matcher.rb +85 -0
- data/lib/shoulda/matchers/action_controller/route_matcher.rb +93 -0
- data/lib/shoulda/matchers/action_controller/set_session_matcher.rb +98 -0
- data/lib/shoulda/matchers/action_controller/set_the_flash_matcher.rb +94 -0
- data/lib/shoulda/matchers/action_mailer.rb +22 -0
- data/lib/shoulda/matchers/action_mailer/have_sent_email.rb +166 -0
- data/lib/shoulda/matchers/active_model.rb +33 -0
- data/lib/shoulda/matchers/active_model/allow_mass_assignment_of_matcher.rb +83 -0
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +110 -0
- data/lib/shoulda/matchers/active_model/ensure_inclusion_of_matcher.rb +87 -0
- data/lib/shoulda/matchers/active_model/ensure_length_of_matcher.rb +141 -0
- data/lib/shoulda/matchers/active_model/helpers.rb +29 -0
- data/lib/shoulda/matchers/active_model/validate_acceptance_of_matcher.rb +41 -0
- data/lib/shoulda/matchers/active_model/validate_format_of_matcher.rb +65 -0
- data/lib/shoulda/matchers/active_model/validate_numericality_of_matcher.rb +39 -0
- data/lib/shoulda/matchers/active_model/validate_presence_of_matcher.rb +61 -0
- data/lib/shoulda/matchers/active_model/validate_uniqueness_of_matcher.rb +148 -0
- data/lib/shoulda/matchers/active_model/validation_matcher.rb +56 -0
- data/lib/shoulda/matchers/active_record.rb +24 -0
- data/lib/shoulda/matchers/active_record/association_matcher.rb +226 -0
- data/lib/shoulda/matchers/active_record/have_db_column_matcher.rb +169 -0
- data/lib/shoulda/matchers/active_record/have_db_index_matcher.rb +112 -0
- data/lib/shoulda/matchers/active_record/have_readonly_attribute_matcher.rb +59 -0
- data/lib/shoulda/matchers/assertion_error.rb +11 -0
- data/lib/shoulda/matchers/integrations/rspec.rb +38 -0
- data/lib/shoulda/matchers/integrations/test_unit.rb +54 -0
- data/lib/shoulda/matchers/version.rb +5 -0
- metadata +169 -0
@@ -0,0 +1,94 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module Matchers
|
3
|
+
module ActionController # :nodoc:
|
4
|
+
|
5
|
+
# Ensures that the flash contains the given value. Can be a String, a
|
6
|
+
# Regexp, or nil (indicating that the flash should not be set).
|
7
|
+
#
|
8
|
+
# Example:
|
9
|
+
#
|
10
|
+
# it { should set_the_flash }
|
11
|
+
# it { should set_the_flash.to("Thank you for placing this order.") }
|
12
|
+
# it { should set_the_flash.to(/created/i) }
|
13
|
+
# it { should set_the_flash.to(/logged in/i).now }
|
14
|
+
# it { should_not set_the_flash }
|
15
|
+
def set_the_flash
|
16
|
+
SetTheFlashMatcher.new
|
17
|
+
end
|
18
|
+
|
19
|
+
class SetTheFlashMatcher # :nodoc:
|
20
|
+
|
21
|
+
def to(value)
|
22
|
+
@value = value
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def now
|
27
|
+
@now = true
|
28
|
+
self
|
29
|
+
end
|
30
|
+
|
31
|
+
def matches?(controller)
|
32
|
+
@controller = controller
|
33
|
+
sets_the_flash? && string_value_matches? && regexp_value_matches?
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_reader :failure_message, :negative_failure_message
|
37
|
+
|
38
|
+
def description
|
39
|
+
description = "set the flash"
|
40
|
+
description << " to #{@value.inspect}" unless @value.nil?
|
41
|
+
description
|
42
|
+
end
|
43
|
+
|
44
|
+
def failure_message
|
45
|
+
"Expected #{expectation}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def negative_failure_message
|
49
|
+
"Did not expect #{expectation}"
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def sets_the_flash?
|
55
|
+
!flash.blank?
|
56
|
+
end
|
57
|
+
|
58
|
+
def string_value_matches?
|
59
|
+
return true unless String === @value
|
60
|
+
flash.to_hash.values.any? {|value| value == @value }
|
61
|
+
end
|
62
|
+
|
63
|
+
def regexp_value_matches?
|
64
|
+
return true unless Regexp === @value
|
65
|
+
flash.to_hash.values.any? {|value| value =~ @value }
|
66
|
+
end
|
67
|
+
|
68
|
+
def flash
|
69
|
+
return @flash if @flash
|
70
|
+
@flash = @controller.flash.dup
|
71
|
+
@flash.sweep unless @now
|
72
|
+
@flash
|
73
|
+
end
|
74
|
+
|
75
|
+
def expectation
|
76
|
+
expectation = "the flash#{".now" if @now} to be set"
|
77
|
+
expectation << " to #{@value.inspect}" unless @value.nil?
|
78
|
+
expectation << ", but #{flash_description}"
|
79
|
+
expectation
|
80
|
+
end
|
81
|
+
|
82
|
+
def flash_description
|
83
|
+
if flash.blank?
|
84
|
+
"no flash was set"
|
85
|
+
else
|
86
|
+
"was #{flash.inspect}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'shoulda/matchers/action_mailer/have_sent_email'
|
2
|
+
|
3
|
+
module Shoulda
|
4
|
+
module Matchers
|
5
|
+
# = Matchers for your mailers
|
6
|
+
#
|
7
|
+
# This matcher will test that email is sent properly
|
8
|
+
#
|
9
|
+
# describe User do
|
10
|
+
# it { should have_sent_email.with_subject(/is spam$/) }
|
11
|
+
# it { should have_sent_email.from('do-not-reply@example.com') }
|
12
|
+
# it { should have_sent_email.with_body(/is spam\./) }
|
13
|
+
# it { should have_sent_email.to('myself@me.com') }
|
14
|
+
# it { should have_sent_email.with_subject(/spam/).
|
15
|
+
# from('do-not-reply@example.com').
|
16
|
+
# with_body(/spam/).
|
17
|
+
# to('myself@me.com') }
|
18
|
+
# end
|
19
|
+
module ActionMailer
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,166 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module Matchers
|
3
|
+
module ActionMailer # :nodoc:
|
4
|
+
|
5
|
+
# The right email is sent.
|
6
|
+
#
|
7
|
+
# it { should have_sent_email.with_subject(/is spam$/) }
|
8
|
+
# it { should have_sent_email.from('do-not-reply@example.com') }
|
9
|
+
# it { should have_sent_email.with_body(/is spam\./) }
|
10
|
+
# it { should have_sent_email.to('myself@me.com') }
|
11
|
+
# it { should have_sent_email.with_part('text/html', /HTML spam/) }
|
12
|
+
# it { should have_sent_email.with_subject(/spam/).
|
13
|
+
# from('do-not-reply@example.com').
|
14
|
+
# with_body(/spam/).
|
15
|
+
# to('myself@me.com') }
|
16
|
+
def have_sent_email
|
17
|
+
HaveSentEmailMatcher.new
|
18
|
+
end
|
19
|
+
|
20
|
+
class HaveSentEmailMatcher # :nodoc:
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
end
|
24
|
+
|
25
|
+
def with_subject(email_subject)
|
26
|
+
@email_subject = email_subject
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def from(sender)
|
31
|
+
@sender = sender
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def with_body(body)
|
36
|
+
@body = body
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
def with_part(content_type, body)
|
41
|
+
@parts ||= []
|
42
|
+
@parts << [/#{Regexp.escape(content_type)}/, body, content_type]
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
def to(recipient)
|
47
|
+
@recipient = recipient
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
def multipart(flag = true)
|
52
|
+
@multipart = !!flag
|
53
|
+
self
|
54
|
+
end
|
55
|
+
|
56
|
+
def matches?(subject)
|
57
|
+
::ActionMailer::Base.deliveries.each do |mail|
|
58
|
+
@subject_failed = !regexp_or_string_match(mail.subject, @email_subject) if @email_subject
|
59
|
+
@parts_failed = !parts_match(mail, @parts) if @parts
|
60
|
+
@body_failed = !body_match(mail, @body) if @body
|
61
|
+
@sender_failed = !regexp_or_string_match_in_array(mail.from, @sender) if @sender
|
62
|
+
@recipient_failed = !regexp_or_string_match_in_array(mail.to, @recipient) if @recipient
|
63
|
+
@multipart_failed = mail.multipart? != @multipart if defined?(@multipart)
|
64
|
+
return true unless anything_failed?
|
65
|
+
end
|
66
|
+
|
67
|
+
false
|
68
|
+
end
|
69
|
+
|
70
|
+
def failure_message
|
71
|
+
"Expected #{expectation}"
|
72
|
+
end
|
73
|
+
|
74
|
+
def negative_failure_message
|
75
|
+
"Did not expect #{expectation}"
|
76
|
+
end
|
77
|
+
|
78
|
+
def description
|
79
|
+
description = "send an email"
|
80
|
+
description << " with a subject of #{@email_subject.inspect}" if @email_subject
|
81
|
+
description << " containing #{@body.inspect}" if @body
|
82
|
+
@parts.each do |_, body, content_type|
|
83
|
+
description << " having a #{content_type} part containing #{body.inspect}"
|
84
|
+
end if @parts
|
85
|
+
description << " from #{@sender.inspect}" if @sender
|
86
|
+
description << " to #{@recipient.inspect}" if @recipient
|
87
|
+
description
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
|
92
|
+
def expectation
|
93
|
+
expectation = "sent email"
|
94
|
+
expectation << " with subject #{@email_subject.inspect}" if @subject_failed
|
95
|
+
expectation << " with body #{@body.inspect}" if @body_failed
|
96
|
+
@parts.each do |_, body, content_type|
|
97
|
+
expectation << " with a #{content_type} part containing #{body}"
|
98
|
+
end if @parts && @parts_failed
|
99
|
+
expectation << " from #{@sender.inspect}" if @sender_failed
|
100
|
+
expectation << " to #{@recipient.inspect}" if @recipient_failed
|
101
|
+
expectation << " #{'not ' if !@multipart}being multipart" if @multipart_failed
|
102
|
+
expectation << "\nDeliveries:\n#{inspect_deliveries}"
|
103
|
+
end
|
104
|
+
|
105
|
+
def inspect_deliveries
|
106
|
+
::ActionMailer::Base.deliveries.map do |delivery|
|
107
|
+
"#{delivery.subject.inspect} to #{delivery.to.inspect}"
|
108
|
+
end.join("\n")
|
109
|
+
end
|
110
|
+
|
111
|
+
def anything_failed?
|
112
|
+
@subject_failed || @body_failed || @parts_failed || @sender_failed ||
|
113
|
+
@recipient_failed || @multipart_failed
|
114
|
+
end
|
115
|
+
|
116
|
+
def regexp_or_string_match(a_string, a_regexp_or_string)
|
117
|
+
case a_regexp_or_string
|
118
|
+
when Regexp
|
119
|
+
a_string =~ a_regexp_or_string
|
120
|
+
when String
|
121
|
+
a_string == a_regexp_or_string
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def regexp_or_string_match_in_array(an_array, a_regexp_or_string)
|
126
|
+
case a_regexp_or_string
|
127
|
+
when Regexp
|
128
|
+
an_array.any? { |string| string =~ a_regexp_or_string }
|
129
|
+
when String
|
130
|
+
an_array.include?(a_regexp_or_string)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def body_match(mail, a_regexp_or_string)
|
135
|
+
# Mail objects instantiated by ActionMailer3 return a blank
|
136
|
+
# body if the e-mail is multipart. TMail concatenates the
|
137
|
+
# String representation of each part instead.
|
138
|
+
if mail.body.blank? && mail.multipart?
|
139
|
+
part_match(mail, /^text\//, a_regexp_or_string)
|
140
|
+
else
|
141
|
+
regexp_or_string_match(mail.body, a_regexp_or_string)
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def parts_match(mail, parts)
|
146
|
+
return false if mail.parts.empty?
|
147
|
+
|
148
|
+
parts.all? do |content_type, match, _|
|
149
|
+
part_match(mail, content_type, match)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def part_match(mail, content_type, a_regexp_or_string)
|
154
|
+
matching = mail.parts.select {|p| p.content_type =~ content_type}
|
155
|
+
return false if matching.empty?
|
156
|
+
|
157
|
+
matching.all? do |part|
|
158
|
+
regexp_or_string_match(part.body, a_regexp_or_string)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'shoulda/matchers/active_model/helpers'
|
2
|
+
require 'shoulda/matchers/active_model/validation_matcher'
|
3
|
+
require 'shoulda/matchers/active_model/allow_value_matcher'
|
4
|
+
require 'shoulda/matchers/active_model/ensure_length_of_matcher'
|
5
|
+
require 'shoulda/matchers/active_model/ensure_inclusion_of_matcher'
|
6
|
+
require 'shoulda/matchers/active_model/validate_presence_of_matcher'
|
7
|
+
require 'shoulda/matchers/active_model/validate_format_of_matcher'
|
8
|
+
require 'shoulda/matchers/active_model/validate_uniqueness_of_matcher'
|
9
|
+
require 'shoulda/matchers/active_model/validate_acceptance_of_matcher'
|
10
|
+
require 'shoulda/matchers/active_model/validate_numericality_of_matcher'
|
11
|
+
require 'shoulda/matchers/active_model/allow_mass_assignment_of_matcher'
|
12
|
+
|
13
|
+
|
14
|
+
module Shoulda
|
15
|
+
module Matchers
|
16
|
+
# = Matchers for your active record models
|
17
|
+
#
|
18
|
+
# These matchers will test most of the validations of ActiveModel::Validations.
|
19
|
+
#
|
20
|
+
# describe User do
|
21
|
+
# it { should validate_presence_of(:name) }
|
22
|
+
# it { should validate_presence_of(:phone_number) }
|
23
|
+
# %w(abcd 1234).each do |value|
|
24
|
+
# it { should_not allow_value(value).for(:phone_number) }
|
25
|
+
# end
|
26
|
+
# it { should allow_value("(123) 456-7890").for(:phone_number) }
|
27
|
+
# it { should_not allow_mass_assignment_of(:password) }
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
module ActiveModel
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module Matchers
|
3
|
+
module ActiveModel # :nodoc:
|
4
|
+
|
5
|
+
# Ensures that the attribute can be set on mass update.
|
6
|
+
#
|
7
|
+
# it { should_not allow_mass_assignment_of(:password) }
|
8
|
+
# it { should allow_mass_assignment_of(:first_name) }
|
9
|
+
#
|
10
|
+
def allow_mass_assignment_of(value)
|
11
|
+
AllowMassAssignmentOfMatcher.new(value)
|
12
|
+
end
|
13
|
+
|
14
|
+
class AllowMassAssignmentOfMatcher # :nodoc:
|
15
|
+
|
16
|
+
def initialize(attribute)
|
17
|
+
@attribute = attribute.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def matches?(subject)
|
21
|
+
@subject = subject
|
22
|
+
if attr_mass_assignable?
|
23
|
+
if whitelisting?
|
24
|
+
@negative_failure_message = "#{@attribute} was made accessible"
|
25
|
+
else
|
26
|
+
if protected_attributes.empty?
|
27
|
+
@negative_failure_message = "no attributes were protected"
|
28
|
+
else
|
29
|
+
@negative_failure_message = "#{class_name} is protecting " <<
|
30
|
+
"#{protected_attributes.to_a.to_sentence}, " <<
|
31
|
+
"but not #{@attribute}."
|
32
|
+
end
|
33
|
+
end
|
34
|
+
true
|
35
|
+
else
|
36
|
+
if whitelisting?
|
37
|
+
@failure_message =
|
38
|
+
"Expected #{@attribute} to be accessible"
|
39
|
+
else
|
40
|
+
@failure_message =
|
41
|
+
"Did not expect #{@attribute} to be protected"
|
42
|
+
end
|
43
|
+
false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
attr_reader :failure_message, :negative_failure_message
|
48
|
+
|
49
|
+
def description
|
50
|
+
"allow mass assignment of #{@attribute}"
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def protected_attributes
|
56
|
+
@protected_attributes ||= (@subject.class.protected_attributes || [])
|
57
|
+
end
|
58
|
+
|
59
|
+
def accessible_attributes
|
60
|
+
@accessible_attributes ||= (@subject.class.accessible_attributes || [])
|
61
|
+
end
|
62
|
+
|
63
|
+
def whitelisting?
|
64
|
+
!accessible_attributes.empty?
|
65
|
+
end
|
66
|
+
|
67
|
+
def attr_mass_assignable?
|
68
|
+
if whitelisting?
|
69
|
+
accessible_attributes.include?(@attribute)
|
70
|
+
else
|
71
|
+
!protected_attributes.include?(@attribute)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def class_name
|
76
|
+
@subject.class.name
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module Shoulda # :nodoc:
|
2
|
+
module Matchers
|
3
|
+
module ActiveModel # :nodoc:
|
4
|
+
|
5
|
+
# Ensures that the attribute can be set to the given value.
|
6
|
+
#
|
7
|
+
# Options:
|
8
|
+
# * <tt>with_message</tt> - value the test expects to find in
|
9
|
+
# <tt>errors.on(:attribute)</tt>. Regexp or string. If omitted,
|
10
|
+
# the test looks for any errors in <tt>errors.on(:attribute)</tt>.
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
# it { should_not allow_value('bad').for(:isbn) }
|
14
|
+
# it { should allow_value("isbn 1 2345 6789 0").for(:isbn) }
|
15
|
+
#
|
16
|
+
def allow_value(value)
|
17
|
+
AllowValueMatcher.new(value)
|
18
|
+
end
|
19
|
+
|
20
|
+
class AllowValueMatcher # :nodoc:
|
21
|
+
include Helpers
|
22
|
+
|
23
|
+
def initialize(value)
|
24
|
+
@value = value
|
25
|
+
end
|
26
|
+
|
27
|
+
def for(attribute)
|
28
|
+
@attribute = attribute
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def with_message(message)
|
33
|
+
@expected_message = message if message
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def matches?(instance)
|
38
|
+
@instance = instance
|
39
|
+
if Symbol === @expected_message
|
40
|
+
@expected_message = default_error_message(@expected_message)
|
41
|
+
end
|
42
|
+
@instance.send("#{@attribute}=", @value)
|
43
|
+
!errors_match?
|
44
|
+
end
|
45
|
+
|
46
|
+
def failure_message
|
47
|
+
"Did not expect #{expectation}, got error: #{@matched_error}"
|
48
|
+
end
|
49
|
+
|
50
|
+
def negative_failure_message
|
51
|
+
"Expected #{expectation}, got #{error_description}"
|
52
|
+
end
|
53
|
+
|
54
|
+
def description
|
55
|
+
"allow #{@attribute} to be set to #{@value.inspect}"
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def errors_match?
|
61
|
+
@instance.valid?
|
62
|
+
@errors = errors_for_attribute(@instance, @attribute)
|
63
|
+
@errors = [@errors] unless @errors.is_a?(Array)
|
64
|
+
@expected_message ? (errors_match_regexp? || errors_match_string?) : (@errors.compact.any?)
|
65
|
+
end
|
66
|
+
|
67
|
+
def errors_for_attribute(instance, attribute)
|
68
|
+
if instance.errors.respond_to?(:[])
|
69
|
+
instance.errors[attribute]
|
70
|
+
else
|
71
|
+
instance.errors.on(attribute)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def errors_match_regexp?
|
76
|
+
if Regexp === @expected_message
|
77
|
+
@matched_error = @errors.detect { |e| e =~ @expected_message }
|
78
|
+
!@matched_error.nil?
|
79
|
+
else
|
80
|
+
false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def errors_match_string?
|
85
|
+
if @errors.include?(@expected_message)
|
86
|
+
@matched_error = @expected_message
|
87
|
+
true
|
88
|
+
else
|
89
|
+
false
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def expectation
|
94
|
+
"errors " <<
|
95
|
+
(@expected_message ? "to include #{@expected_message.inspect} " : "") <<
|
96
|
+
"when #{@attribute} is set to #{@value.inspect}"
|
97
|
+
end
|
98
|
+
|
99
|
+
def error_description
|
100
|
+
if @instance.errors.empty?
|
101
|
+
"no errors"
|
102
|
+
else
|
103
|
+
"errors: #{pretty_error_messages(@instance)}"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|