err_supply-orac 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +4 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +281 -0
- data/Rakefile +2 -0
- data/err_supply.gemspec +24 -0
- data/lib/assets/javascripts/err_supply-bootstrap.js +94 -0
- data/lib/assets/stylesheets/err_supply-bootstrap.scss +21 -0
- data/lib/err_supply.rb +26 -0
- data/lib/err_supply/controller_helpers.rb +90 -0
- data/lib/err_supply/version.rb +3 -0
- data/lib/err_supply/view_helpers.rb +22 -0
- metadata +89 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
MzRkYzJlZWZkMWY3YTc5YmIzODE2YWYwMDZhODBlMDczMmZhNzY4YQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NWQzMTU2ZDhlZmM4ODdkNjM1MjVlMDUyMzdkZWVkZjZhN2UwNTBjMA==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
ZjUwYWQ2MzU5NjUxZjY0ZjYyMzA2MzY2ZmIyYTM3NTI0MzRiOGQ4MDNiYTg2
|
10
|
+
YWVjYTQxM2NlNmI1MzE2ZmY3ZTBmZTJhNzAzODMwNmJkODU5ZWJmMTg5NTJm
|
11
|
+
ZGE0ZmRmYTc1ZmFjYTQ3OWE1Zjg5ZWZhOTcwZjg1ZTkxY2U3NmU=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZmM1YjY1YTY5YTg2Mjg0MmQzYzdkNmJmNjY1NmQ0YmM5ZGZlODY1YTY1Y2Y2
|
14
|
+
ZjJiNzUxMzBlMDE4NGNhZjczN2QxODI3ZGU4MjBhZWNlNTRlNWYwOTgyNmNi
|
15
|
+
NWU4MDUzODcwNDdjYzEwOTYyYWUyYjZhOWIzZjk0MzIyYTQ4ODg=
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2011 Coroutine, LLC
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,281 @@
|
|
1
|
+
# err_supply
|
2
|
+
|
3
|
+
Err Supply is a Rails view helper that produces simple, beautiful error messages.
|
4
|
+
|
5
|
+
The helper unpacks and rekeys the standard Rails error hash to make applying error messages to your views dead simple. Even better, because the extension cures Rails' brain-damaged way of recording errors from nested resources/attributes, it works with both simple and complex forms.
|
6
|
+
|
7
|
+
|
8
|
+
## What Is This?
|
9
|
+
|
10
|
+
Err Supply is designed to make the default Rails error reporting structure more useful for complex
|
11
|
+
interfaces.
|
12
|
+
|
13
|
+
### HTML vs AJAX form submissions
|
14
|
+
|
15
|
+
We started thinking about Err Supply when we worked on a project that required an AJAX form submission.
|
16
|
+
|
17
|
+
For normal HTML submissions, we coded up a custom error handler in Rails to display the messages below the
|
18
|
+
form input. To replicate this for the AJAX submission, we realized we would have to convert the error hash
|
19
|
+
to JSON and then wire up a jQuery event handler to perform the same DOM manipulations Rails was performing
|
20
|
+
internally. Obviously, we weren't super excited about having code in two places and in two languages to
|
21
|
+
provide the same business value.
|
22
|
+
|
23
|
+
|
24
|
+
### Ambiguous error messages for nested attributes
|
25
|
+
|
26
|
+
The problem was compounded a few months later when we worked on a different project that required an AJAX
|
27
|
+
form submission for a nested form.
|
28
|
+
|
29
|
+
Here, even the workaround is problematic. Because Rails reports errors on nested attributes ambiguously,
|
30
|
+
there really wasn't any way to use a javascript workaround without first reconstituting the error hash
|
31
|
+
itself.
|
32
|
+
|
33
|
+
If you don't know what I mean by saying the error messages are ambiguous, here's an example. Say you have
|
34
|
+
these models defined:
|
35
|
+
|
36
|
+
class Father < ActiveRecord::Base
|
37
|
+
attr_accessible :name
|
38
|
+
attr_accessible :age
|
39
|
+
|
40
|
+
has_many :sons
|
41
|
+
|
42
|
+
accepts_nested_attributes_for :sons, :allow_destroy => true
|
43
|
+
|
44
|
+
validates_presence_of :name, :age
|
45
|
+
end
|
46
|
+
|
47
|
+
class Son < ActiveRecord::Base
|
48
|
+
attr_accessible :name
|
49
|
+
attr_accessible :age
|
50
|
+
|
51
|
+
belongs_to :father
|
52
|
+
|
53
|
+
validates_presence_of :name, :age
|
54
|
+
end
|
55
|
+
|
56
|
+
If you pull up the nested edit form for a father with two sons and delete one son's name and the other
|
57
|
+
son's age, Rails will return the following error hash:
|
58
|
+
|
59
|
+
{
|
60
|
+
"sons.name": ["can't be blank"],
|
61
|
+
"sons.age": ["can't be blank"]
|
62
|
+
}
|
63
|
+
|
64
|
+
Umm, thanks, but which son is missing a name and which one is missing an age? Or is it the same son missing
|
65
|
+
both values? Or, do they both have problems?
|
66
|
+
|
67
|
+
|
68
|
+
|
69
|
+
## Our Solution
|
70
|
+
|
71
|
+
Err Supply converts the Rails error hash from a slightly ambiguous object graph to a flat, unambiguous
|
72
|
+
hash of DOM element ids. It does this by traversing the object graph for you and determining exactly which
|
73
|
+
child resources have errors. It then adds those errors to a new hash object where the key is the DOM id
|
74
|
+
of the corresponding form input.
|
75
|
+
|
76
|
+
Err Supply publishes this newly constituted error hash via a custom jQuery event, allowing the view
|
77
|
+
to handle errors through a single javascript interface. This strategy allows errors from HTML and AJAX
|
78
|
+
form submissions to be run through a single piece of code.
|
79
|
+
|
80
|
+
|
81
|
+
|
82
|
+
## Installation
|
83
|
+
|
84
|
+
Install me from RubyGems.org by adding a gem dependency to your Gemfile. Bundler does
|
85
|
+
the rest.
|
86
|
+
|
87
|
+
gem 'err_supply'
|
88
|
+
|
89
|
+
Add the appropriate javascript file to the asset pipeline. Err Supply comes with an adapter for
|
90
|
+
Twitter Bootstrap enabled sites. Those using other frameworks will need to write a separate adapter
|
91
|
+
based on their framework's interface.
|
92
|
+
|
93
|
+
#= require err_supply-bootstrap
|
94
|
+
|
95
|
+
|
96
|
+
Add the appropriate stylesheet file to the asset pipeline. Err Supply comes with style classes for
|
97
|
+
Twitter Bootstrap enabled sites. Those using other frameworks will need to write a separate set of
|
98
|
+
style classes based on their framework's interface.
|
99
|
+
|
100
|
+
@import 'err_supply-bootstrap';
|
101
|
+
|
102
|
+
|
103
|
+
|
104
|
+
## Basic Usage
|
105
|
+
|
106
|
+
The main `err_supply` helper returns an escaped javascript invocation that triggers a custom
|
107
|
+
event named `err_supply:loaded` and supplies the edited error hash as data.
|
108
|
+
|
109
|
+
<script type="text/javascript">
|
110
|
+
<%= err_supply @father %>
|
111
|
+
</script>
|
112
|
+
|
113
|
+
This will evaluate @father.errors and apply an errors to the form. It assumes all form inputs
|
114
|
+
are named in the standard rails way and that all error attribute keys match the form input
|
115
|
+
keys exactly.
|
116
|
+
|
117
|
+
|
118
|
+
### 1. Whitelists/Blacklists
|
119
|
+
|
120
|
+
Attributes can be whitelisted or blacklisted using the standard `:only` and `:except` notation.
|
121
|
+
|
122
|
+
Because `err_supply` will ignore any unmatched attributes, such declarations are not strictly
|
123
|
+
required. They typically only make sense for minor actions against models with many,
|
124
|
+
many attributes.
|
125
|
+
|
126
|
+
|
127
|
+
### 2. Changing Labels
|
128
|
+
|
129
|
+
Say a User class has an attribute `:born_on` to store the user's date of birth. In your form
|
130
|
+
builder you declare the textbox normally like so:
|
131
|
+
|
132
|
+
<%= f.text_field :born_on %>
|
133
|
+
|
134
|
+
A presence\_of error will be formatted as:
|
135
|
+
|
136
|
+
Born on can't be blank.
|
137
|
+
|
138
|
+
To make this nicer, we can change the label for the attribute like this:
|
139
|
+
|
140
|
+
<script type="text/javascript">
|
141
|
+
<%= err_supply @user, :born_on => { :label => "Date of birth" } %>
|
142
|
+
</script>
|
143
|
+
|
144
|
+
This will attach the following presence of error to the :born_on field:
|
145
|
+
|
146
|
+
Date of birth can't be blank.
|
147
|
+
|
148
|
+
|
149
|
+
### 3. Changing Keys
|
150
|
+
|
151
|
+
Say a User class belongs to an Organization class. In your form, you declare a selector
|
152
|
+
for assigning the organization. The selector is named `:ogranization_id`.
|
153
|
+
|
154
|
+
Depending on how your validations are written, you might very well get an error message for
|
155
|
+
this form keyed to `:organization`. Because your selector is keyed to `:organization_id`,
|
156
|
+
the default javascript handler will consider this an unmatched attribute.
|
157
|
+
|
158
|
+
You can solve this problem by changing the key for the attribute like so:
|
159
|
+
|
160
|
+
<script type="text/javascript">
|
161
|
+
<%= err_supply @user, :organization => { :key => :organization_id } %>
|
162
|
+
</script>
|
163
|
+
|
164
|
+
|
165
|
+
### 4. Nested Attributes
|
166
|
+
|
167
|
+
You can apply the same principles to nested attributes by nesting the instructions. To return
|
168
|
+
to our father/son example, you can change the name labels for both entities using the following
|
169
|
+
notation:
|
170
|
+
|
171
|
+
<script type="text/javascript">
|
172
|
+
<%= err_supply @father,
|
173
|
+
:name => { :label => "Father's name" },
|
174
|
+
:sons => {
|
175
|
+
:name => { :label => "Son's name" }
|
176
|
+
}
|
177
|
+
%>
|
178
|
+
</script>
|
179
|
+
|
180
|
+
|
181
|
+
### 5. Combining Instructions (aka Go Crazy)
|
182
|
+
|
183
|
+
Attribute instructions are provided as hashes so that both `key` and `label` changes can be
|
184
|
+
declared on the same attribute.
|
185
|
+
|
186
|
+
Honestly, such instructions are rare in the real world, but error handling can get weird fast,
|
187
|
+
so the library supports it.
|
188
|
+
|
189
|
+
|
190
|
+
|
191
|
+
|
192
|
+
## Advanced Usage
|
193
|
+
|
194
|
+
There are a handful of scenarios that fall outside the area of basic usage worth discussing.
|
195
|
+
|
196
|
+
### 1. AJAX Submissions and Nested Forms
|
197
|
+
|
198
|
+
When Rails submits a nested form via a full page refresh, Rails automatically re-indexes any
|
199
|
+
DOM elements in a nested collection starting at 0.
|
200
|
+
|
201
|
+
If you're submitting a form remotely, it's on you to do this by re-rendering the nested fields
|
202
|
+
with the current collection.
|
203
|
+
|
204
|
+
As long as you do this before the `err_supply` call is made, everything will work normally.
|
205
|
+
|
206
|
+
|
207
|
+
### 2. Other Javascript Libraries
|
208
|
+
|
209
|
+
If you don't use jQuery, you may want to override the `err_supply` helper to format the
|
210
|
+
javascript invocation differently.
|
211
|
+
|
212
|
+
The library is constructed so the main public method named `err_supply` is pretty dumb. Most
|
213
|
+
of the real hash-altering magic occurs in a protected method called `err_supply_hash`. You can
|
214
|
+
override the much simpler method without worrying about damaging the core hash interpretation
|
215
|
+
functionality.
|
216
|
+
|
217
|
+
See the `lib` directory for details.
|
218
|
+
|
219
|
+
|
220
|
+
### 3. Handling Unmatched Errors
|
221
|
+
|
222
|
+
The default javascript handler bundles up unmatched error keys into a new hash and publishes them
|
223
|
+
using the custom jQuery event `err_supply:unmatched`.
|
224
|
+
|
225
|
+
|
226
|
+
|
227
|
+
|
228
|
+
## Prerequisites
|
229
|
+
|
230
|
+
* <b>Ruby on Rails:</b> <http://rubyonrails.org>
|
231
|
+
* <b>jQuery:</b> <http://jquery.com>
|
232
|
+
|
233
|
+
|
234
|
+
|
235
|
+
|
236
|
+
## Helpful Links
|
237
|
+
|
238
|
+
* <b>Repository:</b> <http://github.com/coroutine/err_supply>
|
239
|
+
* <b>Gem:</b> <http://rubygems.org/gems/err_supply>
|
240
|
+
* <b>Authors:</b> <http://coroutine.com>
|
241
|
+
|
242
|
+
|
243
|
+
|
244
|
+
|
245
|
+
## Gemroll
|
246
|
+
|
247
|
+
Other gems by Coroutine include:
|
248
|
+
|
249
|
+
* [acts\_as\_current](http://github.com/coroutine/acts_as_current)
|
250
|
+
* [acts\_as\_label](http://github.com/coroutine/acts_as_label)
|
251
|
+
* [acts\_as\_list\_with\_sti\_support](http://github.com/coroutine/acts_as_list_with_sti_support)
|
252
|
+
* [delayed\_form\_observer](http://github.com/coroutine/delayed_form_observer)
|
253
|
+
* [kenny\_dialoggins](http://github.com/coroutine/kenny_dialoggins)
|
254
|
+
* [michael\_hintbuble](http://github.com/coroutine/michael_hintbuble)
|
255
|
+
* [tiny\_navigation](http://github.com/coroutine/tiny_navigation)
|
256
|
+
|
257
|
+
|
258
|
+
|
259
|
+
|
260
|
+
## License
|
261
|
+
|
262
|
+
Copyright (c) 2011 [Coroutine LLC](http://coroutine.com).
|
263
|
+
|
264
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
265
|
+
a copy of this software and associated documentation files (the
|
266
|
+
"Software"), to deal in the Software without restriction, including
|
267
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
268
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
269
|
+
permit persons to whom the Software is furnished to do so, subject to
|
270
|
+
the following conditions:
|
271
|
+
|
272
|
+
The above copyright notice and this permission notice shall be
|
273
|
+
included in all copies or substantial portions of the Software.
|
274
|
+
|
275
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
276
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
277
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
278
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
279
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
280
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
281
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
data/err_supply.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'err_supply/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'err_supply-orac'
|
8
|
+
spec.version = ErrSupply::VERSION
|
9
|
+
spec.authors = ['Coroutine', 'John Dugan']
|
10
|
+
spec.email = ['gems@coroutine.com']
|
11
|
+
spec.summary = %q{Simple, beautiful error messages for Rails.}
|
12
|
+
spec.description = %q{Simple, beautiful error messages for Rails. Err_supply unpacks and rekeys the standard Rails error hash to make applying error messages to your views dead simple. Even better, because the library cures Rails' brain-damaged way of recording errors from nested resources/attributes, err_supply works with both simple and complex forms.}
|
13
|
+
spec.homepage = 'http://github.com/coroutine/err_supply'
|
14
|
+
spec.licenses = ['MIT']
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.add_dependency 'rails', '>= 3.0.0'
|
22
|
+
|
23
|
+
spec.add_development_dependency 'rspec', '>= 2.0.0'
|
24
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
/*
|
2
|
+
ErrSupply Twitter Bootstrap3 Adapter
|
3
|
+
Copyright (c) 2014 Coroutine LLC
|
4
|
+
Licensed under the MIT license
|
5
|
+
*/
|
6
|
+
(function() {
|
7
|
+
var errSupplyPlugin = function(event, errors) {
|
8
|
+
|
9
|
+
// define how errors are applied to dom elements
|
10
|
+
var applyFn = function(error_hash) {
|
11
|
+
var unmatched = {};
|
12
|
+
var errors = error_hash || {};
|
13
|
+
var error;
|
14
|
+
var content;
|
15
|
+
var $field;
|
16
|
+
var position;
|
17
|
+
|
18
|
+
for (var id in errors) {
|
19
|
+
error = errors[id];
|
20
|
+
if (error && error.messages && error.messages.length) {
|
21
|
+
|
22
|
+
content = '<ul class="err_supply">';
|
23
|
+
for (var i = 0, n = error.messages.length; i < n; i++) {
|
24
|
+
content += '<li>' + error.label + " " + error.messages[i] + '</li>';
|
25
|
+
};
|
26
|
+
content += '</ul>';
|
27
|
+
|
28
|
+
$field = $('#' + id);
|
29
|
+
options = {}
|
30
|
+
if ($field.offset()) {
|
31
|
+
|
32
|
+
// determine position
|
33
|
+
if (($field.offset().left + $field.outerWidth() + 300) < $(window).width()) {
|
34
|
+
position = 'right';
|
35
|
+
}
|
36
|
+
else {
|
37
|
+
position = 'left';
|
38
|
+
};
|
39
|
+
|
40
|
+
// add class
|
41
|
+
$field.addClass('error').closest('.input-group').addClass('has-error');
|
42
|
+
|
43
|
+
// add popover
|
44
|
+
$field.popover({
|
45
|
+
html: true,
|
46
|
+
placement: position,
|
47
|
+
trigger: 'focus',
|
48
|
+
content: content
|
49
|
+
});
|
50
|
+
}
|
51
|
+
else {
|
52
|
+
unmatched[id] = error;
|
53
|
+
};
|
54
|
+
};
|
55
|
+
};
|
56
|
+
|
57
|
+
return unmatched;
|
58
|
+
};
|
59
|
+
|
60
|
+
// get reference to form firing event
|
61
|
+
var $form = $(this);
|
62
|
+
|
63
|
+
// find all contained elements with a style of error and remove the class.
|
64
|
+
// this is typically more important for ajax submissions. html submissions
|
65
|
+
// tend not to have this problem.
|
66
|
+
$form.find('.error').removeClass('error').closest('.input-group').removeClass('has-error');
|
67
|
+
|
68
|
+
// hide all fields that have been explicitly destroyed. when an html submission
|
69
|
+
// has errors, the _destroy value renders as true rather than 1, which may or may not
|
70
|
+
// causes destroyed sets to be visible after the reload. here, we scan for both
|
71
|
+
// values and hide any containing div.fields elements.
|
72
|
+
$form.find('input:hidden[id$=_destroy]').filter('[value=true], [value=1]').closest('.fields').hide();
|
73
|
+
|
74
|
+
// apply errors to dom elements
|
75
|
+
var unmatched_errors = applyFn(errors) || {};
|
76
|
+
|
77
|
+
// publish unmatched errors, in case view cares about that
|
78
|
+
$form.trigger('err_supply:unmatched', unmatched_errors);
|
79
|
+
|
80
|
+
// move focus to first field with error (or first field)
|
81
|
+
var $focus_field = ($form.find('.error').size() > 0) ?
|
82
|
+
$form.find('.error').filter(':first') :
|
83
|
+
$form.find(':not(.filter) :input:visible:enabled:first');
|
84
|
+
$focus_field.focus()
|
85
|
+
};
|
86
|
+
|
87
|
+
$.fn.err_supply = function() {
|
88
|
+
$(this).on("err_supply:loaded", errSupplyPlugin.bind(this));
|
89
|
+
}
|
90
|
+
})()
|
91
|
+
|
92
|
+
jQuery(function($) {
|
93
|
+
$("form").err_supply();
|
94
|
+
});
|
@@ -0,0 +1,21 @@
|
|
1
|
+
/*---------------------------------------------
|
2
|
+
err_supply styles
|
3
|
+
-----------------------------------------------*/
|
4
|
+
|
5
|
+
ul.err_supply {
|
6
|
+
margin: 0;
|
7
|
+
padding: 0;
|
8
|
+
list-style: none;
|
9
|
+
}
|
10
|
+
|
11
|
+
.form-group .popover {
|
12
|
+
width: 325px;
|
13
|
+
background: $brand-danger;
|
14
|
+
color: #fff;
|
15
|
+
}
|
16
|
+
.form-group .popover.right .arrow:after {
|
17
|
+
border-right-color: $brand-danger;
|
18
|
+
}
|
19
|
+
.form-group .popover.left .arrow:after {
|
20
|
+
border-left-color: $brand-danger;
|
21
|
+
}
|
data/lib/err_supply.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'err_supply/version'
|
2
|
+
require 'err_supply/controller_helpers'
|
3
|
+
require 'err_supply/view_helpers'
|
4
|
+
|
5
|
+
module ErrSupply
|
6
|
+
class Engine < ::Rails::Engine
|
7
|
+
|
8
|
+
#-------------------------------------------------------
|
9
|
+
# Initialization
|
10
|
+
#-------------------------------------------------------
|
11
|
+
initializer "err_supply.start" do |app|
|
12
|
+
|
13
|
+
# extend views
|
14
|
+
ActiveSupport.on_load :action_view do
|
15
|
+
include ErrSupply::ViewHelpers
|
16
|
+
end
|
17
|
+
|
18
|
+
# extend controllers
|
19
|
+
ActiveSupport.on_load :action_controller do
|
20
|
+
include ErrSupply::ControllerHelpers
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module ErrSupply
|
2
|
+
module ControllerHelpers
|
3
|
+
|
4
|
+
# Declare our method as a helper method so it is accessible to views.
|
5
|
+
ActionController::Base.helper_method :err_supply_hash
|
6
|
+
|
7
|
+
# This method can be called recursively to unpack a rails object's error hash
|
8
|
+
# into something more manageable for the view. Basically, it uses the keys in the
|
9
|
+
# object's error hash to traverse the object graph and expand the errors into a
|
10
|
+
# flattened hash, keyed using the dom ids rails will generate for the
|
11
|
+
# associated objects.
|
12
|
+
#
|
13
|
+
# Because the method is called recursively, it only pays attention to errors for
|
14
|
+
# the given object and its immediate child collections. More remote descendents
|
15
|
+
# will be processed in subsequent passes.
|
16
|
+
#
|
17
|
+
def err_supply_hash(obj, options={})
|
18
|
+
|
19
|
+
#---------------------------------------------
|
20
|
+
# set up main variables
|
21
|
+
#---------------------------------------------
|
22
|
+
|
23
|
+
prefix = options[:prefix]
|
24
|
+
errors = obj.errors
|
25
|
+
h = {}
|
26
|
+
|
27
|
+
|
28
|
+
#---------------------------------------------
|
29
|
+
# apply options to local attrs
|
30
|
+
#---------------------------------------------
|
31
|
+
|
32
|
+
# get keys
|
33
|
+
attrs = errors.keys.select { |k| k.to_s.split(".").size == 1 }
|
34
|
+
|
35
|
+
# whitelist/blacklist keys
|
36
|
+
if options.has_key?(:only)
|
37
|
+
attrs = attrs.select { |k| Array(options[:only]).flatten.include?(k) }
|
38
|
+
end
|
39
|
+
if options.has_key?(:except)
|
40
|
+
attrs = attrs.reject { |k| Array(options[:except]).flatten.include?(k) }
|
41
|
+
end
|
42
|
+
|
43
|
+
# apply errors that match our list (slightly inefficient, but a touch easier to read)
|
44
|
+
attrs.each do |attr|
|
45
|
+
o = options[attr.to_sym] || {}
|
46
|
+
id = [prefix, "#{(o[:key] || attr).to_s}"].reject(&:blank?).join('_')
|
47
|
+
|
48
|
+
unless h.has_key?(id)
|
49
|
+
h[id] = {
|
50
|
+
"label" => obj.class.human_attribute_name(attr),
|
51
|
+
"messages" => []
|
52
|
+
}
|
53
|
+
end
|
54
|
+
|
55
|
+
h[id]["label"] = o[:label].to_s.underscore.humanize unless o[:label].nil?
|
56
|
+
h[id]["messages"] = (h[id]["messages"] + errors[attr].map { |msg| "#{msg}.".gsub("..", ".") }).flatten
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
#---------------------------------------------
|
61
|
+
# apply options to children
|
62
|
+
#---------------------------------------------
|
63
|
+
|
64
|
+
# get keys
|
65
|
+
assoc_names = errors.keys.map { |k| k.to_s.split(".") }.select { |a| a.many? }.map { |a| a[0] }.compact.uniq
|
66
|
+
|
67
|
+
# if child has errors or is invalid (i.e., we prefer explicitly declared errrors),
|
68
|
+
# call function recursively and merge results
|
69
|
+
assoc_names.each do |assoc_name|
|
70
|
+
c_options = options[assoc_name.to_sym] || {}
|
71
|
+
obj.send("#{assoc_name}").each_with_index do |child, index|
|
72
|
+
if !child.errors.empty? or child.invalid?
|
73
|
+
c_prefix = [prefix, "#{assoc_name}_attributes_#{index}"].reject(&:blank?).join('_')
|
74
|
+
c_hash = err_supply_hash(child, c_options.merge({ :prefix => c_prefix }))
|
75
|
+
|
76
|
+
h.merge!(c_hash)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
#---------------------------------------------
|
83
|
+
# return the hash
|
84
|
+
#---------------------------------------------
|
85
|
+
h
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module ErrSupply
|
2
|
+
module ViewHelpers
|
3
|
+
|
4
|
+
# Converts the given object's error hash into our JSON structure and
|
5
|
+
# triggers a custom event on the associated form element.
|
6
|
+
#
|
7
|
+
def err_supply(obj, options={})
|
8
|
+
form_css_selector = options[:form_css_selector]
|
9
|
+
|
10
|
+
if form_css_selector.blank?
|
11
|
+
id = obj.new_record? ? dom_id(obj) : dom_id(obj, :edit)
|
12
|
+
form_css_selector = "##{id}"
|
13
|
+
end
|
14
|
+
|
15
|
+
prefix = obj.class.name.underscore.split('/').last
|
16
|
+
payload = err_supply_hash(obj, options.merge({ :prefix => prefix }))
|
17
|
+
|
18
|
+
"$('#{ form_css_selector }').trigger('err_supply:loaded', #{ payload.to_json });".html_safe
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: err_supply-orac
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Coroutine
|
8
|
+
- John Dugan
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2017-02-14 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rails
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ! '>='
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 3.0.0
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ! '>='
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 3.0.0
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rspec
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - ! '>='
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: 2.0.0
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - ! '>='
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: 2.0.0
|
42
|
+
description: Simple, beautiful error messages for Rails. Err_supply unpacks and rekeys
|
43
|
+
the standard Rails error hash to make applying error messages to your views dead
|
44
|
+
simple. Even better, because the library cures Rails' brain-damaged way of recording
|
45
|
+
errors from nested resources/attributes, err_supply works with both simple and complex
|
46
|
+
forms.
|
47
|
+
email:
|
48
|
+
- gems@coroutine.com
|
49
|
+
executables: []
|
50
|
+
extensions: []
|
51
|
+
extra_rdoc_files: []
|
52
|
+
files:
|
53
|
+
- .gitignore
|
54
|
+
- Gemfile
|
55
|
+
- LICENSE.txt
|
56
|
+
- README.md
|
57
|
+
- Rakefile
|
58
|
+
- err_supply.gemspec
|
59
|
+
- lib/assets/javascripts/err_supply-bootstrap.js
|
60
|
+
- lib/assets/stylesheets/err_supply-bootstrap.scss
|
61
|
+
- lib/err_supply.rb
|
62
|
+
- lib/err_supply/controller_helpers.rb
|
63
|
+
- lib/err_supply/version.rb
|
64
|
+
- lib/err_supply/view_helpers.rb
|
65
|
+
homepage: http://github.com/coroutine/err_supply
|
66
|
+
licenses:
|
67
|
+
- MIT
|
68
|
+
metadata: {}
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options: []
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ! '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
requirements: []
|
84
|
+
rubyforge_project:
|
85
|
+
rubygems_version: 2.5.1
|
86
|
+
signing_key:
|
87
|
+
specification_version: 4
|
88
|
+
summary: Simple, beautiful error messages for Rails.
|
89
|
+
test_files: []
|