active_model-better_errors 1.6.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.
Files changed (45) hide show
  1. data/.document +5 -0
  2. data/.gitmodules +3 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +3 -0
  5. data/Gemfile +17 -0
  6. data/LICENSE.txt +20 -0
  7. data/README.md +183 -0
  8. data/Rakefile +57 -0
  9. data/VERSION +1 -0
  10. data/active_model-better_errors.gemspec +113 -0
  11. data/lib/active_model/better_errors.rb +15 -0
  12. data/lib/active_model/error_collecting.rb +49 -0
  13. data/lib/active_model/error_collecting/array_reporter.rb +9 -0
  14. data/lib/active_model/error_collecting/core_ext.rb +6 -0
  15. data/lib/active_model/error_collecting/emulation.rb +65 -0
  16. data/lib/active_model/error_collecting/error_collection.rb +86 -0
  17. data/lib/active_model/error_collecting/error_message.rb +88 -0
  18. data/lib/active_model/error_collecting/error_message_set.rb +35 -0
  19. data/lib/active_model/error_collecting/errors.rb +52 -0
  20. data/lib/active_model/error_collecting/hash_reporter.rb +9 -0
  21. data/lib/active_model/error_collecting/human_array_reporter.rb +9 -0
  22. data/lib/active_model/error_collecting/human_hash_reporter.rb +15 -0
  23. data/lib/active_model/error_collecting/human_message_formatter.rb +60 -0
  24. data/lib/active_model/error_collecting/human_message_reporter.rb +32 -0
  25. data/lib/active_model/error_collecting/machine_array_reporter.rb +19 -0
  26. data/lib/active_model/error_collecting/machine_hash_reporter.rb +22 -0
  27. data/lib/active_model/error_collecting/message_reporter.rb +17 -0
  28. data/lib/active_model/error_collecting/reporter.rb +14 -0
  29. data/spec/lib/active_model/better_errors_spec.rb +7 -0
  30. data/spec/lib/active_model/error_collecting/emulation_spec.rb +45 -0
  31. data/spec/lib/active_model/error_collecting/error_collection_spec.rb +205 -0
  32. data/spec/lib/active_model/error_collecting/error_message_set_spec.rb +96 -0
  33. data/spec/lib/active_model/error_collecting/error_message_spec.rb +293 -0
  34. data/spec/lib/active_model/error_collecting/errors_spec.rb +95 -0
  35. data/spec/lib/active_model/error_collecting/human_array_reporter_spec.rb +33 -0
  36. data/spec/lib/active_model/error_collecting/human_hash_reporter_spec.rb +32 -0
  37. data/spec/lib/active_model/error_collecting/human_message_formatter_spec.rb +22 -0
  38. data/spec/lib/active_model/error_collecting/human_message_reporter_spec.rb +61 -0
  39. data/spec/lib/active_model/error_collecting/machine_array_reporter_spec.rb +40 -0
  40. data/spec/lib/active_model/error_collecting/machine_hash_reporter_spec.rb +40 -0
  41. data/spec/lib/active_model/error_collecting_spec.rb +22 -0
  42. data/spec/spec_helper.rb +15 -0
  43. data/spec/support/models.rb +28 -0
  44. data/test/integration.rb +10 -0
  45. metadata +274 -0
@@ -0,0 +1,32 @@
1
+ module ActiveModel
2
+ module ErrorCollecting
3
+ class HumanMessageReporter < MessageReporter
4
+ def full_messages
5
+ @collection.map do |attribute, error_message|
6
+ formatter = HumanMessageFormatter.new(base, error_message)
7
+ message = formatter.format_message
8
+ full_message attribute, message
9
+ end
10
+ end
11
+
12
+ def full_message(attribute, message)
13
+ return message if attribute == :base
14
+ attr_name = attribute.to_s.gsub('.', '_').humanize
15
+ attr_name = base.class.human_attribute_name(attribute, :default => attr_name)
16
+ I18n.t(:"errors.format", {
17
+ :default => "%{attribute} %{message}",
18
+ :attribute => attr_name,
19
+ :message => message
20
+ })
21
+ end
22
+
23
+ # This method is not used internally.
24
+ # This is for API Compatibility with ActiveModel::Errors only
25
+ def generate_message(attribute, type = :invalid, options = {})
26
+ error_message = ErrorMessage.build(base, attribute, type, options)
27
+ formatter = HumanMessageFormatter.new(base, error_message)
28
+ formatter.format_message
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,19 @@
1
+ module ActiveModel
2
+ module ErrorCollecting
3
+ class MachineArrayReporter < ArrayReporter
4
+ def to_a
5
+ collection.to_a.map do |error_message|
6
+ format_error_message error_message
7
+ end
8
+ end
9
+
10
+ def format_error_message(error_message)
11
+ result = {}
12
+ result[:attribute] = error_message.attribute.to_s
13
+ result[:type] = error_message.type || :invalid
14
+ result[:options] = error_message.options unless error_message.options.blank?
15
+ result
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ module ActiveModel
2
+ module ErrorCollecting
3
+ class MachineHashReporter < HashReporter
4
+ def to_hash
5
+ collection.to_hash.inject({}) do |hash, kv|
6
+ attribute, error_message_set = kv
7
+ hash[attribute] = error_message_set.map do |error_message|
8
+ format_error_message(error_message)
9
+ end
10
+ hash
11
+ end
12
+ end
13
+
14
+ def format_error_message(error_message)
15
+ result = {}
16
+ result[:type] = error_message.type || :invalid
17
+ result[:options] = error_message.options unless error_message.options.blank?
18
+ result
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ module ActiveModel
2
+ module ErrorCollecting
3
+ class MessageReporter < Reporter
4
+ def full_messages
5
+ raise "abstract method"
6
+ end
7
+
8
+ def full_message(attribute, message)
9
+ raise "abstract method"
10
+ end
11
+
12
+ def generate_message(attribute, type = :invalid, options = {})
13
+ raise "abstract method"
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,14 @@
1
+ module ActiveModel
2
+ module ErrorCollecting
3
+ class Reporter
4
+ attr_reader :collection
5
+ def initialize(collection)
6
+ @collection = collection
7
+ end
8
+
9
+ def base
10
+ @collection.base
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe "ActiveModel Better Errors" do
4
+ it "overrides ActiveModel Validations" do
5
+ User.new.errors.should be_a ActiveModel::ErrorCollecting::Errors
6
+ end
7
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ shared_examples_for "a delegated method" do
4
+ let(:target_instance) { mock() }
5
+ before do
6
+ target_instance.should_receive method
7
+ instance.stub(target).and_return(target_instance)
8
+ end
9
+
10
+ specify { instance.send method }
11
+ end
12
+
13
+ describe ActiveModel::ErrorCollecting::Emulation do
14
+ subject(:instance) { klass.new }
15
+ let(:klass) { Class.new { include ActiveModel::ErrorCollecting::Emulation } }
16
+
17
+ delegation_map = {
18
+ error_collection: [
19
+ :clear, :include?, :get, :set, :delete, :[], :[]=, :each, :size,
20
+ :values, :keys, :count, :empty?, :added?, :any?, :entries
21
+ ],
22
+
23
+ message_reporter: [
24
+ :full_messages, :full_message, :generate_message
25
+ ],
26
+
27
+ hash_reporter: [
28
+ :to_hash
29
+ ],
30
+
31
+ hash_reporter: [
32
+ :to_hash
33
+ ]
34
+ }
35
+
36
+ delegation_map.each do |target, methods|
37
+ methods.each do |method|
38
+ describe "delegating ##{method} to ##{target}" do
39
+ let(:target) { target }
40
+ let(:method) { method }
41
+ it_should_behave_like "a delegated method"
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,205 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveModel::ErrorCollecting::ErrorCollection do
4
+ subject(:collection) { klass.new(base) }
5
+ let(:klass) { ActiveModel::ErrorCollecting::ErrorCollection }
6
+ let(:base) { User.new }
7
+ let(:errors){{
8
+ :first_name => [ [ :too_long, { count: 3 } ] ],
9
+ 'last_name' => [ [ :invalid, { message: "Invalid" } ] ],
10
+ :email => [ :invalid ],
11
+ }}
12
+
13
+ before do
14
+ errors.each do |attribute, error_list|
15
+ error_list.each do |error|
16
+ collection[attribute] = error
17
+ end
18
+ end
19
+ end
20
+
21
+ describe "#clear" do
22
+ before { collection.clear }
23
+ it { should be_empty }
24
+ end
25
+
26
+ describe "#include?" do
27
+ it { should be_include :first_name }
28
+ it { should be_include :last_name }
29
+ it { should be_include :email }
30
+ it { should_not be_include 'first_name' }
31
+ it { should_not be_include 'last_name' }
32
+ it { should_not be_include 'email' }
33
+ end
34
+
35
+ describe "#get" do
36
+ subject { collection.get(:first_name) }
37
+ it { should be_a ActiveModel::ErrorCollecting::ErrorMessageSet }
38
+ its(:length) { should be 1 }
39
+
40
+ describe "when value is nil" do
41
+ before { collection.delete :first_name }
42
+ it { should be nil }
43
+ end
44
+ end
45
+
46
+ describe "#set" do
47
+ subject { collection.get :first_name }
48
+
49
+ describe "when value is array" do
50
+ before { collection.set(:first_name, []) }
51
+ it { should be_a ActiveModel::ErrorCollecting::ErrorMessageSet }
52
+ its(:length) { should be 0 }
53
+ end
54
+
55
+ describe "when value is nil" do
56
+ before { collection.set(:first_name, nil) }
57
+ it { should be_nil }
58
+ end
59
+ end
60
+
61
+ describe "#delete" do
62
+ subject { collection.get(:first_name) }
63
+ before { collection.delete(:first_name) }
64
+ it { should be_nil }
65
+ end
66
+
67
+ describe "#[]" do
68
+ subject { collection[:first_name] }
69
+
70
+ describe "when no error messages" do
71
+ before { collection.clear }
72
+ it { should be_blank }
73
+ it { should_not be_nil }
74
+ it { should be_a ActiveModel::ErrorCollecting::ErrorMessageSet }
75
+ end
76
+
77
+ describe "when there are error messages" do
78
+ it { should_not be_blank }
79
+ it { should_not be_nil }
80
+ it { should be_a ActiveModel::ErrorCollecting::ErrorMessageSet }
81
+ end
82
+ end
83
+
84
+ describe "#[]=" do
85
+ subject(:error_message_set) { collection.get field }
86
+
87
+ describe "when assigning existing attribute" do
88
+ let(:field) { :first_name }
89
+ it "should append to existing set" do
90
+ expect {
91
+ collection[field] = "I'm invalid."
92
+ }.to change { error_message_set.length }.by(1)
93
+ end
94
+ end
95
+
96
+ describe "when assigning to new attribute" do
97
+ let(:field) { :a_new_attribute }
98
+ before { collection[field] = "I'm invalid" }
99
+ it { should_not be_nil }
100
+ it { should be_a ActiveModel::ErrorCollecting::ErrorMessageSet }
101
+ its(:length) { should be 1 }
102
+ end
103
+ end
104
+
105
+ describe "#each" do
106
+ it "should loop through each error" do
107
+ count = 0
108
+ collection.each do |attribute, error|
109
+ attribute.should be_a Symbol
110
+ error.should be_a ActiveModel::ErrorCollecting::ErrorMessage
111
+ count += 1
112
+ end
113
+
114
+ expect(count).to be collection.size
115
+ end
116
+ end
117
+
118
+ describe "#size" do
119
+ subject { collection.size }
120
+ it { should be 3 }
121
+
122
+ describe "when adding one more error" do
123
+ before { collection[:name] = "Not Valid" }
124
+ it { should be 4 }
125
+ end
126
+ end
127
+
128
+ describe "#values" do
129
+ subject { collection.values }
130
+
131
+ it { should be_a Array }
132
+ its(:length) { should be 3 }
133
+
134
+ it "should contain ErrorMessageSet as elements" do
135
+ collection.values.each do |el|
136
+ el.should be_a ActiveModel::ErrorCollecting::ErrorMessageSet
137
+ end
138
+ end
139
+ end
140
+
141
+ describe "#keys" do
142
+ subject { collection.keys }
143
+ it { should == [:first_name, :last_name, :email] }
144
+ end
145
+
146
+ describe "#to_a" do
147
+ subject { collection.to_a }
148
+ its(:size) { should == 3 }
149
+
150
+ describe "when adding one more error" do
151
+ before { collection[:name] = "Not Valid" }
152
+ its(:size) { should == 4 }
153
+ end
154
+
155
+ it "should contain ErrorMessage as elements" do
156
+ collection.to_a.each do |error|
157
+ error.should be_a ActiveModel::ErrorCollecting::ErrorMessage
158
+ end
159
+ end
160
+ end
161
+
162
+ describe "#to_hash" do
163
+ subject { collection.to_hash }
164
+
165
+ it { should be_a Hash }
166
+ it { should == collection.instance_variable_get(:@collection) }
167
+ it { should_not be collection.instance_variable_get(:@collection) }
168
+ end
169
+
170
+ describe "#empty?" do
171
+ subject { collection.empty? }
172
+ it{ should be false }
173
+
174
+ describe "after clearing the collection" do
175
+ before { collection.clear }
176
+
177
+ it { should be true }
178
+ end
179
+ end
180
+
181
+ describe "#add" do
182
+ it "should add error to collection" do
183
+ expect {
184
+ collection.add :name, "Invalid"
185
+ }.to change { collection[:name].length }.by(1)
186
+ end
187
+ end
188
+
189
+ describe "#added?" do
190
+ describe "when an error message is not added" do
191
+ subject { collection.added? :name, :not_there }
192
+ it { should be false }
193
+ end
194
+
195
+ describe "when an error message with the same option is added" do
196
+ subject { collection.added? :first_name, :too_long, { :count => 3 } }
197
+ it { should be true }
198
+ end
199
+
200
+ describe "when an error message with the different option is added" do
201
+ subject { collection.added? :first_name, :too_long, { :count => 4 } }
202
+ it { should be false }
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,96 @@
1
+ require 'spec_helper'
2
+
3
+ describe ActiveModel::ErrorCollecting::ErrorMessageSet do
4
+ subject(:set) { klass.new base, attribute, errors }
5
+ let(:klass) { ActiveModel::ErrorCollecting::ErrorMessageSet }
6
+ let(:attribute) { :first_name }
7
+ let(:errors) { [] }
8
+ let(:base) { User.new }
9
+
10
+ describe "#initialize" do
11
+ context "with no errors" do
12
+ its(:length) { should == 0 }
13
+ end
14
+
15
+ context "with one error" do
16
+ let(:errors) { [:invalid] }
17
+ its(:length) { should == 1 }
18
+ end
19
+ end
20
+
21
+ describe "#push" do
22
+ before { set.push error, options }
23
+ let(:error) { :invalid }
24
+ let(:options) { nil }
25
+ subject { set.first }
26
+
27
+ describe "without options" do
28
+ it { should be_a ActiveModel::ErrorCollecting::ErrorMessage }
29
+ its(:message) { should == nil }
30
+ its(:type) { should == error }
31
+ end
32
+
33
+ describe "with options" do
34
+ let(:options) { { message: 'Invalid' } }
35
+
36
+ it { should be_a ActiveModel::ErrorCollecting::ErrorMessage }
37
+ its(:message) { should == options[:message] }
38
+ its(:type) { should == error }
39
+ end
40
+ end
41
+
42
+ describe "#<<" do
43
+ before { set << error }
44
+ subject { set.first }
45
+
46
+ describe "when accepting error as a symbol" do
47
+ let(:error) { :invalid }
48
+
49
+ it { should be_a ActiveModel::ErrorCollecting::ErrorMessage }
50
+ its(:message) { should == nil }
51
+ its(:type) { should == error }
52
+ end
53
+
54
+ describe "when accepting error as a tuple" do
55
+ let(:error) { [ :invalid, { message: "OMG!!" } ]}
56
+
57
+ it { should be_a ActiveModel::ErrorCollecting::ErrorMessage }
58
+ its(:message) { should == "OMG!!" }
59
+ its(:type) { should == :invalid }
60
+ end
61
+ end
62
+
63
+ describe "#to_a" do
64
+ let(:errors) { [:invalid, :too_long] }
65
+ subject { set.to_a }
66
+
67
+ it { should be_a Array }
68
+ its(:length) { should == 2 }
69
+ end
70
+
71
+ describe "#empty?" do
72
+ subject { set.empty? }
73
+
74
+ describe "when no error messages" do
75
+ it { should be_true }
76
+ end
77
+
78
+ describe "when contains error messages" do
79
+ let(:errors) { [:invalid, :too_long] }
80
+ it { should be_false }
81
+ end
82
+ end
83
+
84
+ describe "#==" do
85
+ subject { errors == set }
86
+ let(:errors) { [:invalid] }
87
+ let(:expected) { ["is invalid"] }
88
+ specify do
89
+ set.should == expected
90
+ end
91
+
92
+ specify do
93
+ expected.should == set
94
+ end
95
+ end
96
+ end