caring_form 1.2.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.travis.yml +17 -0
- data/Appraisals +18 -0
- data/Gemfile +9 -0
- data/Guardfile +15 -0
- data/LICENSE +22 -0
- data/README.md +33 -0
- data/Rakefile +12 -0
- data/app/assets/javascripts/caring_form/form-observer.coffee +63 -0
- data/app/assets/javascripts/caring_form/main.coffee +15 -0
- data/app/assets/javascripts/caring_form/remote-validator.coffee +71 -0
- data/app/assets/javascripts/caring_form/rule-based-validator.coffee +37 -0
- data/app/assets/javascripts/caring_form/setup.coffee +5 -0
- data/app/assets/javascripts/caring_form/webshims-customization.coffee +25 -0
- data/app/assets/javascripts/caring_forms.js +7 -0
- data/caring_form.gemspec +31 -0
- data/gemfiles/rails_3_1.gemfile +8 -0
- data/gemfiles/rails_3_2.gemfile +8 -0
- data/gemfiles/rails_4.gemfile +7 -0
- data/gemfiles/rails_4_1.gemfile +8 -0
- data/lib/caring_form.rb +20 -0
- data/lib/caring_form/action_view_extensions/builder.rb +75 -0
- data/lib/caring_form/action_view_extensions/config.rb +4 -0
- data/lib/caring_form/action_view_extensions/flash_error_helper.rb +28 -0
- data/lib/caring_form/action_view_extensions/form_helper.rb +33 -0
- data/lib/caring_form/active_model/monkey_patching.rb +32 -0
- data/lib/caring_form/dsl.rb +91 -0
- data/lib/caring_form/engine.rb +4 -0
- data/lib/caring_form/field/base.rb +145 -0
- data/lib/caring_form/field/check_box.rb +23 -0
- data/lib/caring_form/field/check_boxes.rb +15 -0
- data/lib/caring_form/field/collection.rb +48 -0
- data/lib/caring_form/field/dummy.rb +30 -0
- data/lib/caring_form/field/email.rb +41 -0
- data/lib/caring_form/field/full_name.rb +40 -0
- data/lib/caring_form/field/looking_for.rb +37 -0
- data/lib/caring_form/field/metadata.rb +26 -0
- data/lib/caring_form/field/radio_buttons.rb +15 -0
- data/lib/caring_form/field/string.rb +13 -0
- data/lib/caring_form/field/submit.rb +83 -0
- data/lib/caring_form/field/tel.rb +37 -0
- data/lib/caring_form/field/text.rb +13 -0
- data/lib/caring_form/field/us_zip_code.rb +37 -0
- data/lib/caring_form/field_container.rb +126 -0
- data/lib/caring_form/form_builder.rb +51 -0
- data/lib/caring_form/model.rb +58 -0
- data/lib/caring_form/model/active_model.rb +11 -0
- data/lib/caring_form/model/active_record.rb +38 -0
- data/lib/caring_form/simple_form/initializer.rb +26 -0
- data/lib/caring_form/simple_form/monkey_patching.rb +51 -0
- data/lib/caring_form/tools/referee.rb +62 -0
- data/lib/caring_form/version.rb +3 -0
- data/lib/tasks/webshims.rake +7 -0
- data/spec/caring_form/action_view_extensions/flash_error_helper_spec.rb +50 -0
- data/spec/caring_form/action_view_extensions/form_helper_spec.rb +72 -0
- data/spec/caring_form/dsl_spec.rb +21 -0
- data/spec/caring_form/field/base_spec.rb +204 -0
- data/spec/caring_form/field/check_boxes_spec.rb +32 -0
- data/spec/caring_form/field/collection_spec.rb +28 -0
- data/spec/caring_form/field/radio_buttons_spec.rb +29 -0
- data/spec/caring_form/field/submit_spec.rb +57 -0
- data/spec/caring_form/field_container_spec.rb +85 -0
- data/spec/caring_form/form_builder_spec.rb +113 -0
- data/spec/caring_form/model_spec.rb +235 -0
- data/spec/caring_form/tools/referee_spec.rb +86 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/support/nokogiri_matcher.rb +204 -0
- metadata +278 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 9189ecabe4fbff2003c4100405b4b46bd96d222f
|
4
|
+
data.tar.gz: 811c47c4ed64c413dfc232642c23be924910bb58
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 51315c24df9c1872cf23ccc67799709a12597713b81982537c0e67678830d56dd62f0c5263e62d2b5ac5aea9ecd93a5c7a2f267adbf24a1702d9e93fa949e30c
|
7
|
+
data.tar.gz: db02f2691ea49e9cd8821c01397d813e4b62bd572c5254ca54118ab876cea5319466af9717c2667f40e4200d4b9c94c392f81ce6829696eca74ebbf8dd02ee7f
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
language: ruby
|
2
|
+
cache: bundler
|
3
|
+
script: bundle exec rake spec
|
4
|
+
|
5
|
+
gemfile:
|
6
|
+
- gemfiles/rails_3_1.gemfile
|
7
|
+
- gemfiles/rails_3_2.gemfile
|
8
|
+
- gemfiles/rails_4.gemfile
|
9
|
+
- gemfiles/rails_4_1.gemfile
|
10
|
+
|
11
|
+
rvm:
|
12
|
+
- 1.9.3
|
13
|
+
- 2.1.0
|
14
|
+
|
15
|
+
matrix:
|
16
|
+
allow_failures:
|
17
|
+
- rvm: ruby-head
|
data/Appraisals
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
appraise "rails-3-1" do
|
2
|
+
gem "minitest", '~> 4.0'
|
3
|
+
gem "rails", "~>3.1.0"
|
4
|
+
end
|
5
|
+
|
6
|
+
appraise "rails-3-2" do
|
7
|
+
gem "minitest", '~> 4.0'
|
8
|
+
gem "rails", "~>3.2.14"
|
9
|
+
end
|
10
|
+
|
11
|
+
appraise "rails-4" do
|
12
|
+
gem "rails", "~>4.0.0"
|
13
|
+
end
|
14
|
+
|
15
|
+
appraise "rails-4-1" do
|
16
|
+
gem "rails", "~> 4.1.1"
|
17
|
+
gem "simple_form", '~> 3.1.0.rc1'
|
18
|
+
end
|
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
# More info at https://github.com/guard/guard#readme
|
2
|
+
|
3
|
+
guard 'coffeescript', :input => 'javascripts/src', :output => 'javascripts/compiled', :all_on_start => true
|
4
|
+
|
5
|
+
guard :jammit, :config_path => 'javascripts/assets.yml', :output_folder => 'javascripts' do
|
6
|
+
watch(%r{^javascripts/compiled/(.*)\.js$})
|
7
|
+
watch('javascripts/assets.yml')
|
8
|
+
end
|
9
|
+
|
10
|
+
guard 'minitest' do
|
11
|
+
# with Minitest::Spec
|
12
|
+
watch(%r|^spec/(.*)_spec\.rb|)
|
13
|
+
watch(%r|^lib/(.*)([^/]+)\.rb|) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
14
|
+
watch(%r|^spec/spec_helper\.rb|) { "spec" }
|
15
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Mathieu Lajugie
|
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,33 @@
|
|
1
|
+
# CaringForm
|
2
|
+
|
3
|
+
[![Build Status](https://magnum.travis-ci.com/caring/caring_form.png?token=sKHYu3Tc8rtipypNpjWg&branch=master)](https://magnum.travis-ci.com/caring/caring_form)
|
4
|
+
|
5
|
+
Standardized forms, styles and JS validation for caring.com
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'caring_form'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
$ rake webshims:update_public # Allows dynamic module loading to continue working.
|
17
|
+
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
In `app/assets/javascripts/application.js` or any other asset pipeline enabled JS.
|
22
|
+
|
23
|
+
```
|
24
|
+
//= require caring_forms
|
25
|
+
```
|
26
|
+
|
27
|
+
## Contributing
|
28
|
+
|
29
|
+
1. Fork it
|
30
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
31
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
32
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
33
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
$ = jQuery
|
2
|
+
|
3
|
+
class CARING.FormObserver
|
4
|
+
|
5
|
+
@enableButtons: (form) ->
|
6
|
+
form
|
7
|
+
.find('.primary-button, .secondary-button, .marketing-button')
|
8
|
+
.prop('disabled', false)
|
9
|
+
.removeClass('disabled')
|
10
|
+
|
11
|
+
@disableButtons: (form) ->
|
12
|
+
# disable buttons in the next click as IE7-8 won't submit the form
|
13
|
+
# if the submit button is disabled while in the callback flow...
|
14
|
+
setTimeout((->
|
15
|
+
form
|
16
|
+
.find('.primary-button, .secondary-button, .marketing-button')
|
17
|
+
.prop('disabled', true)
|
18
|
+
.addClass('disabled')
|
19
|
+
), 0)
|
20
|
+
true
|
21
|
+
|
22
|
+
constructor: (@selector) ->
|
23
|
+
|
24
|
+
start: ->
|
25
|
+
changedProxy = $.proxy(@valueChanged, this)
|
26
|
+
$(@selector).on
|
27
|
+
'input': changedProxy
|
28
|
+
'change': changedProxy
|
29
|
+
'invalid': (event) =>
|
30
|
+
@markAsSubmitted(event.currentTarget)
|
31
|
+
@showErrorOn(event.target)
|
32
|
+
'submit': (event) =>
|
33
|
+
# IE7-8 trigger a form submit even if the form is not valid...
|
34
|
+
if $(event.currentTarget).checkValidity()
|
35
|
+
form = $(event.currentTarget)
|
36
|
+
@constructor.disableButtons(form) unless form.hasClass('no-disable-buttons')
|
37
|
+
|
38
|
+
wasSubmitted: (form) ->
|
39
|
+
$(form).hasClass('submitted')
|
40
|
+
|
41
|
+
markAsSubmitted: (form) ->
|
42
|
+
$(form).addClass('submitted')
|
43
|
+
|
44
|
+
fieldContainerFor: (element) ->
|
45
|
+
$(element).closest('.field')
|
46
|
+
|
47
|
+
showErrorOn: (element) ->
|
48
|
+
@fieldContainerFor(element).addClass('field-with-errors')
|
49
|
+
|
50
|
+
hideErrorOn: (element) ->
|
51
|
+
@fieldContainerFor(element).removeClass('field-with-errors')
|
52
|
+
|
53
|
+
hideErrorIfValidOnNextClick: (element) ->
|
54
|
+
# element validity is not accurate when checked right away, so let's
|
55
|
+
# wait for the next click in the event loop
|
56
|
+
setTimeout((=>
|
57
|
+
if $(element).hasClass('indicator-field-ok') or $(element).checkValidity()
|
58
|
+
@hideErrorOn(element)
|
59
|
+
), 0)
|
60
|
+
|
61
|
+
valueChanged: (event) ->
|
62
|
+
return unless @wasSubmitted(event.currentTarget)
|
63
|
+
@hideErrorIfValidOnNextClick(event.target)
|
@@ -0,0 +1,15 @@
|
|
1
|
+
jQuery ($) ->
|
2
|
+
observer = new CARING.FormObserver('form.caring-form')
|
3
|
+
observer.start()
|
4
|
+
|
5
|
+
validators = [
|
6
|
+
new CARING.RemoteValidator('.validate-remotely')
|
7
|
+
new CARING.RuleBasedValidator()
|
8
|
+
]
|
9
|
+
validator.addBehavior() for validator in validators
|
10
|
+
|
11
|
+
# allow to close flash
|
12
|
+
$('.flash .flash-close > a').click (event) ->
|
13
|
+
event.preventDefault()
|
14
|
+
flash = $(event.target).closest('.flash')
|
15
|
+
flash.slideUp()
|
@@ -0,0 +1,71 @@
|
|
1
|
+
$ = jQuery
|
2
|
+
|
3
|
+
class CARING.RemoteValidator
|
4
|
+
|
5
|
+
_removeAllValidationClassesFor = (element) ->
|
6
|
+
$(element).removeClass('indicator-field-ok indicator-field-ban indicator-field-loading')
|
7
|
+
|
8
|
+
_refreshFormValidationFor = (element) -> $(element).trigger('input', [true])
|
9
|
+
|
10
|
+
_setErrorMessage = (field, message) ->
|
11
|
+
if field.setCustomValidity?
|
12
|
+
$(field).setCustomValidity(message)
|
13
|
+
|
14
|
+
_showInlineError = (field, message) ->
|
15
|
+
field = $(field)
|
16
|
+
error = field.next('.inline-error')
|
17
|
+
error.text(message).show()
|
18
|
+
offset = field.offset()
|
19
|
+
offset.top += field.outerHeight()
|
20
|
+
if window.navigator.userAgent.match(/Gecko/)
|
21
|
+
offset.top += 1
|
22
|
+
offset.left += 2
|
23
|
+
offset.left += field.outerWidth() - error.outerWidth() - 2
|
24
|
+
error.offset(offset)
|
25
|
+
|
26
|
+
_hideInlineError = (field) ->
|
27
|
+
$(field).next('.inline-error')
|
28
|
+
.hide()
|
29
|
+
.text('')
|
30
|
+
|
31
|
+
constructor: (@fieldSelector, options = {}) ->
|
32
|
+
@validate($(@fieldSelector)) if $(@fieldSelector).hasClass("auto-validate")
|
33
|
+
|
34
|
+
addBehavior: ->
|
35
|
+
$(@fieldSelector).on 'keyup', =>
|
36
|
+
field = $(@fieldSelector)
|
37
|
+
_setErrorMessage(field, field.data('init-message') or "Please wait while I check ...")
|
38
|
+
clearTimeout(buffering) if buffering = field.data('buffering')
|
39
|
+
buffering = setTimeout((=>
|
40
|
+
@validate(field)
|
41
|
+
field.data(buffering: null)
|
42
|
+
), parseInt(field.data('max-idle-time') || "300", 10))
|
43
|
+
field.data(buffering: buffering)
|
44
|
+
|
45
|
+
validate: (element) ->
|
46
|
+
_removeAllValidationClassesFor(element)
|
47
|
+
value = element.val()
|
48
|
+
if value is ''
|
49
|
+
_setErrorMessage(element, '')
|
50
|
+
_refreshFormValidationFor(element)
|
51
|
+
_hideInlineError(element)
|
52
|
+
else
|
53
|
+
element.addClass('indicator-field-loading')
|
54
|
+
$.getJSON(element.data('validator-url'), {value: value}, (data, status, xhr) ->
|
55
|
+
_removeAllValidationClassesFor(element)
|
56
|
+
if data.valid
|
57
|
+
element
|
58
|
+
.addClass('indicator-field-ok')
|
59
|
+
.each (index, element) ->
|
60
|
+
$(element).trigger('data-valid')
|
61
|
+
_setErrorMessage(element, '')
|
62
|
+
_refreshFormValidationFor(element)
|
63
|
+
_hideInlineError(element)
|
64
|
+
else
|
65
|
+
element
|
66
|
+
.addClass('indicator-field-ban')
|
67
|
+
.each (index, element) ->
|
68
|
+
_setErrorMessage(element, data.message || data.short_message)
|
69
|
+
_refreshFormValidationFor(element)
|
70
|
+
_showInlineError(element, data.short_message)
|
71
|
+
)
|
@@ -0,0 +1,37 @@
|
|
1
|
+
$ = jQuery
|
2
|
+
|
3
|
+
class CARING.RuleBasedValidator
|
4
|
+
|
5
|
+
# RULES: hash of {name: rule}
|
6
|
+
# where rule has a targetEvent and an action
|
7
|
+
#
|
8
|
+
# The rule based validator expects rules set to DOM elements
|
9
|
+
# as data attributes. The data attribute is the name of the rule
|
10
|
+
# and its value is the target selector.
|
11
|
+
|
12
|
+
RULES =
|
13
|
+
'optional-if-absent':
|
14
|
+
targetEvent: 'change'
|
15
|
+
action: (source, target) ->
|
16
|
+
sourceRequired = $(target).val() isnt ''
|
17
|
+
$(source).attr('required', sourceRequired)
|
18
|
+
|
19
|
+
'optional-if-present':
|
20
|
+
targetEvent: 'change'
|
21
|
+
action: (source, target) ->
|
22
|
+
sourceRequired = $(target).val() is ''
|
23
|
+
$(source).attr('required', sourceRequired)
|
24
|
+
|
25
|
+
_applyRule = (source, ruleName, rule) ->
|
26
|
+
targetSelector = $(source).data(ruleName)
|
27
|
+
# run the rule once for each (source, target) and each
|
28
|
+
# time a target triggers the rule targetEvent
|
29
|
+
$(targetSelector)
|
30
|
+
.each(-> rule.action(source, this))
|
31
|
+
.bind(rule.targetEvent, (event) ->
|
32
|
+
rule.action(source, event.target)
|
33
|
+
)
|
34
|
+
|
35
|
+
addBehavior: ->
|
36
|
+
for name, rule of RULES
|
37
|
+
$("*[data-#{name}]").each -> _applyRule(this, name, rule)
|
@@ -0,0 +1,25 @@
|
|
1
|
+
$ = jQuery
|
2
|
+
|
3
|
+
useWebshimsFormUI = ->
|
4
|
+
$.webshims.setOptions('forms',
|
5
|
+
replaceValidationUI: true
|
6
|
+
customMessages: true
|
7
|
+
overrideMessages: true
|
8
|
+
)
|
9
|
+
|
10
|
+
if messages = $.webshims.validityMessages['en']
|
11
|
+
messages.valueMissing.defaultMessage = "Please fill out this field. {%title}"
|
12
|
+
messages.valueMissing.select = "Please select an option. {%title}"
|
13
|
+
messages.valueMissing.radio = "Please select one of these options. {%title}"
|
14
|
+
messages.patternMismatch = "Please match the requested format. {%title}"
|
15
|
+
|
16
|
+
useWebshimsDateSlider = ->
|
17
|
+
$.webshims.setOptions('forms-ext',
|
18
|
+
datepicker:
|
19
|
+
dateFormat: 'm/d/y'
|
20
|
+
)
|
21
|
+
|
22
|
+
CARING.WebshimsCustomization =
|
23
|
+
normalizeFormValidation: ->
|
24
|
+
useWebshimsFormUI()
|
25
|
+
useWebshimsDateSlider()
|
@@ -0,0 +1,7 @@
|
|
1
|
+
//= require webshims/polyfiller
|
2
|
+
//= require caring_form/setup
|
3
|
+
//= require caring_form/form-observer
|
4
|
+
//= require caring_form/remote-validator
|
5
|
+
//= require caring_form/rule-based-validator
|
6
|
+
//= require caring_form/webshims-customization
|
7
|
+
//= require caring_form/main
|
data/caring_form.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/caring_form/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Mathieu Lajugie"]
|
6
|
+
gem.email = ['mathieu@caring.com']
|
7
|
+
gem.description = %q{Build clean, robust and consistent forms for Caring.com.}
|
8
|
+
gem.summary = gem.description
|
9
|
+
gem.homepage = 'http://www.caring.com'
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split($\)
|
12
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "caring_form"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = CaringForm::VERSION
|
17
|
+
gem.platform = Gem::Platform::RUBY
|
18
|
+
|
19
|
+
gem.add_dependency 'simple_form'
|
20
|
+
gem.add_dependency 'actionpack'
|
21
|
+
gem.add_dependency 'activemodel'
|
22
|
+
gem.add_dependency 'webshims-rails', '~> 1.15.6'
|
23
|
+
|
24
|
+
gem.add_development_dependency "appraisal", '~> 0.5.2'
|
25
|
+
gem.add_development_dependency 'rake'
|
26
|
+
gem.add_development_dependency 'minitest'
|
27
|
+
gem.add_development_dependency 'minitest-reporters'
|
28
|
+
gem.add_development_dependency 'nokogiri'
|
29
|
+
gem.add_development_dependency 'awesome_print'
|
30
|
+
gem.add_development_dependency 'pry-nav'
|
31
|
+
end
|