formulary 0.0.2 → 0.0.3
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.
- checksums.yaml +7 -0
- data/README.md +65 -34
- data/formulary.gemspec +0 -1
- data/lib/formulary.rb +1 -5
- data/lib/formulary/html_form.rb +31 -13
- data/lib/formulary/html_form/fields.rb +16 -0
- data/lib/formulary/html_form/fields/checkbox_group.rb +1 -1
- data/lib/formulary/html_form/fields/color_input.rb +24 -0
- data/lib/formulary/html_form/fields/date_input.rb +59 -0
- data/lib/formulary/html_form/fields/email_input.rb +11 -2
- data/lib/formulary/html_form/fields/field.rb +15 -3
- data/lib/formulary/html_form/fields/field_group.rb +3 -3
- data/lib/formulary/html_form/fields/input.rb +1 -1
- data/lib/formulary/html_form/fields/month_input.rb +15 -0
- data/lib/formulary/html_form/fields/number_input.rb +63 -0
- data/lib/formulary/html_form/fields/password_input.rb +7 -0
- data/lib/formulary/html_form/fields/range_input.rb +7 -0
- data/lib/formulary/html_form/fields/search_input.rb +7 -0
- data/lib/formulary/html_form/fields/select.rb +1 -1
- data/lib/formulary/html_form/fields/week_input.rb +15 -0
- data/lib/formulary/html_form/labels.rb +39 -0
- data/lib/formulary/version.rb +1 -1
- data/spec/html_form/fields/checkbox_group_spec.rb +23 -10
- data/spec/html_form/fields/color_input_spec.rb +58 -0
- data/spec/html_form/fields/date_input_spec.rb +60 -0
- data/spec/html_form/fields/email_input_spec.rb +16 -6
- data/spec/html_form/fields/field_spec.rb +40 -1
- data/spec/html_form/fields/hidden_input_spec.rb +3 -1
- data/spec/html_form/fields/month_input_spec.rb +60 -0
- data/spec/html_form/fields/number_input_spec.rb +67 -0
- data/spec/html_form/fields/password_input_spec.rb +29 -0
- data/spec/html_form/fields/radio_button_group_spec.rb +27 -8
- data/spec/html_form/fields/range_input_spec.rb +20 -0
- data/spec/html_form/fields/search_input_spec.rb +18 -0
- data/spec/html_form/fields/select_spec.rb +10 -7
- data/spec/html_form/fields/tel_input_spec.rb +3 -3
- data/spec/html_form/fields/text_input_spec.rb +3 -3
- data/spec/html_form/fields/textarea_spec.rb +2 -2
- data/spec/html_form/fields/week_input_spec.rb +60 -0
- data/spec/html_form_spec.rb +234 -100
- data/spec/support/element_helper.rb +1 -1
- data/spec/support/shared_examples_for_pattern.rb +4 -2
- data/spec/support/shared_examples_for_required.rb +4 -2
- metadata +114 -106
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 393050691325dca94776e614a5de5f939fd595de
|
4
|
+
data.tar.gz: 825e144f8e5a11d88756318ba32e9e5248088ca1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 51ebdb9b78b2e3bb3be922650daebd152cf302c00074cee83d3e7f350b9eb80ba9dc38e2dad34d113444faf2a53d4c20f8400309bcaa49ba29b5c9f2e4217b7e
|
7
|
+
data.tar.gz: f164cece566135c3400ce59ba8042e24cee8736b6469268921f302a9027a02e83f0a0cc2f423a789e84bdad76614cfdc39dc6e7a0207c643c0b651d1e2e02eb8
|
data/README.md
CHANGED
@@ -17,32 +17,32 @@ gem 'formulary'
|
|
17
17
|
|
18
18
|
And then execute:
|
19
19
|
|
20
|
-
|
20
|
+
$ bundle
|
21
21
|
|
22
22
|
Or install it yourself as:
|
23
23
|
|
24
|
-
|
24
|
+
$ gem install formulary
|
25
25
|
|
26
26
|
|
27
27
|
## Usage
|
28
28
|
|
29
29
|
Create a new Formulary Form
|
30
|
-
|
30
|
+
|
31
31
|
```ruby
|
32
32
|
require 'formulary'
|
33
33
|
|
34
34
|
form_html = <<EOF
|
35
35
|
<form>
|
36
|
-
|
37
|
-
|
36
|
+
<input type="email" name="email" required />
|
37
|
+
<input type="username" name="username" pattern="[a-z0-9_-]{3,16}" />
|
38
38
|
</form>
|
39
39
|
EOF
|
40
|
-
|
40
|
+
|
41
41
|
html_form = Formulary::HtmlForm.new(form_html)
|
42
42
|
```
|
43
43
|
|
44
44
|
Validate the form based on HTML5 field types and/or patterns and view which fields are invalid and why.
|
45
|
-
|
45
|
+
|
46
46
|
```ruby
|
47
47
|
html_form.valid?({ email: "test@example.com", username: "person" })
|
48
48
|
# => true
|
@@ -61,25 +61,55 @@ html_form.valid?({ unknown: "value" })
|
|
61
61
|
# => Formulary::UnexpectedParameter: Got unexpected field 'unknown'
|
62
62
|
```
|
63
63
|
|
64
|
-
|
65
64
|
## Currently Supported
|
66
65
|
|
67
|
-
|
66
|
+
**Supported Input Types**
|
67
|
+
- checkbox
|
68
|
+
- color
|
69
|
+
- date
|
70
|
+
- email
|
71
|
+
- hidden
|
72
|
+
- month
|
73
|
+
- number
|
74
|
+
- password
|
75
|
+
- radio
|
76
|
+
- range
|
77
|
+
- search
|
78
|
+
- tel
|
79
|
+
- text
|
80
|
+
- week
|
81
|
+
|
82
|
+
**Ignored Field Types (not validated but does not make things explode)
|
83
|
+
- button
|
84
|
+
- image
|
85
|
+
- reset
|
86
|
+
- submit
|
87
|
+
|
88
|
+
**Supported Input Attributes**
|
89
|
+
- max (number, range, date)
|
90
|
+
- min (number, range, date)
|
91
|
+
- pattern
|
68
92
|
- required
|
69
|
-
-
|
70
|
-
- selects, selected value is one of the options
|
93
|
+
- step (number, range)
|
71
94
|
|
95
|
+
**Other Supported Tags**
|
96
|
+
- select
|
97
|
+
- textarea
|
72
98
|
|
73
|
-
## TODO
|
74
99
|
|
75
|
-
|
76
|
-
- validate [other html5 field types](http://www.w3schools.com/html/html5_form_input_types.asp)
|
100
|
+
## TODO
|
77
101
|
|
102
|
+
**Add Unsupported Input Types**
|
103
|
+
- datetime
|
104
|
+
- datetime-local
|
105
|
+
- file
|
106
|
+
- time
|
107
|
+
- url
|
78
108
|
|
79
109
|
## Authors
|
80
110
|
|
81
|
-
*
|
82
|
-
*
|
111
|
+
* Matt Bohme / [@quady](https://github.com/quady)
|
112
|
+
* Don Petersen / [@dpetersen](https://github.com/dpetersen)
|
83
113
|
|
84
114
|
|
85
115
|
## Contributing
|
@@ -95,25 +125,26 @@ html_form.valid?({ unknown: "value" })
|
|
95
125
|
|
96
126
|
## License
|
97
127
|
|
98
|
-
Copyright (c) 2013 G5
|
128
|
+
Copyright (c) 2013 G5
|
129
|
+
|
130
|
+
MIT License
|
99
131
|
|
100
|
-
|
132
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
133
|
+
a copy of this software and associated documentation files (the
|
134
|
+
"Software"), to deal in the Software without restriction, including
|
135
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
136
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
137
|
+
permit persons to whom the Software is furnished to do so, subject to
|
138
|
+
the following conditions:
|
101
139
|
|
102
|
-
|
103
|
-
|
104
|
-
"Software"), to deal in the Software without restriction, including
|
105
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
106
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
107
|
-
permit persons to whom the Software is furnished to do so, subject to
|
108
|
-
the following conditions:
|
140
|
+
The above copyright notice and this permission notice shall be
|
141
|
+
included in all copies or substantial portions of the Software.
|
109
142
|
|
110
|
-
|
111
|
-
|
143
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
144
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
145
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
146
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
147
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
148
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
149
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
112
150
|
|
113
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
114
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
115
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
116
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
117
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
118
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
119
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/formulary.gemspec
CHANGED
data/lib/formulary.rb
CHANGED
@@ -1,11 +1,6 @@
|
|
1
1
|
require 'nokogiri'
|
2
2
|
require 'active_support/core_ext/object/blank'
|
3
3
|
require 'active_support/core_ext/object/try'
|
4
|
-
require 'email_veracity'
|
5
|
-
|
6
|
-
# Don't try and determine if this email address is legitimate, just validate
|
7
|
-
# the provided address.
|
8
|
-
EmailVeracity::Config[:skip_lookup] = true
|
9
4
|
|
10
5
|
module Formulary
|
11
6
|
class HtmlForm
|
@@ -16,4 +11,5 @@ end
|
|
16
11
|
|
17
12
|
require "formulary/version"
|
18
13
|
require "formulary/html_form/fields"
|
14
|
+
require "formulary/html_form/labels"
|
19
15
|
require "formulary/html_form"
|
data/lib/formulary/html_form.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
1
|
module Formulary
|
2
2
|
class HtmlForm
|
3
|
+
include Labels
|
4
|
+
|
3
5
|
SINGULAR_FIELD_SELECTOR = <<-EOS
|
4
|
-
input[type!='submit'][type!='radio'][type!='checkbox'],
|
6
|
+
input[type!='submit'][type!='button'][type!='reset'][type!='image'][type!='radio'][type!='checkbox'],
|
5
7
|
textarea,
|
6
8
|
select
|
7
9
|
EOS
|
@@ -18,9 +20,13 @@ module Formulary
|
|
18
20
|
|
19
21
|
def valid?(params)
|
20
22
|
params.each do |key, value|
|
21
|
-
|
22
|
-
|
23
|
-
|
23
|
+
if value.kind_of?(Hash)
|
24
|
+
value.each do |nested_key, nested_value|
|
25
|
+
set_field_value("#{key}[#{nested_key}]", nested_value)
|
26
|
+
end
|
27
|
+
else
|
28
|
+
set_field_value(key, value)
|
29
|
+
end
|
24
30
|
end
|
25
31
|
|
26
32
|
fields.all?(&:valid?)
|
@@ -34,6 +40,16 @@ module Formulary
|
|
34
40
|
|
35
41
|
protected
|
36
42
|
|
43
|
+
def document
|
44
|
+
@document ||= Nokogiri::HTML(@markup)
|
45
|
+
end
|
46
|
+
|
47
|
+
def set_field_value(field_name, value)
|
48
|
+
field = find_field(field_name)
|
49
|
+
raise UnexpectedParameter.new("Got unexpected field '#{field_name}'") unless field
|
50
|
+
field.set_value(value)
|
51
|
+
end
|
52
|
+
|
37
53
|
def fields
|
38
54
|
@fields || build_fields
|
39
55
|
end
|
@@ -44,24 +60,23 @@ module Formulary
|
|
44
60
|
|
45
61
|
def build_fields
|
46
62
|
@fields = []
|
47
|
-
doc = Nokogiri::HTML(@markup)
|
48
63
|
|
49
|
-
build_singular_fields_from
|
50
|
-
build_grouped_fields_from
|
64
|
+
build_singular_fields_from
|
65
|
+
build_grouped_fields_from
|
51
66
|
end
|
52
67
|
|
53
|
-
def build_singular_fields_from
|
54
|
-
|
68
|
+
def build_singular_fields_from
|
69
|
+
document.css(SINGULAR_FIELD_SELECTOR.strip).map do |element|
|
55
70
|
field_klass = FIELD_TYPES.detect { |k| k.compatible_with?(element) }
|
56
71
|
if field_klass.nil?
|
57
72
|
raise UnsupportedFieldType.new("I can't handle this field: #{element.inspect}")
|
58
73
|
end
|
59
|
-
@fields << field_klass.new(element)
|
74
|
+
@fields << field_klass.new(self, element)
|
60
75
|
end
|
61
76
|
end
|
62
77
|
|
63
|
-
def build_grouped_fields_from
|
64
|
-
grouped_elements =
|
78
|
+
def build_grouped_fields_from
|
79
|
+
grouped_elements = document.css(GROUPED_FIELD_SELECTOR.strip).group_by do |element|
|
65
80
|
element.attributes["name"].value
|
66
81
|
end
|
67
82
|
|
@@ -69,7 +84,10 @@ module Formulary
|
|
69
84
|
group_name, elements = *element_group
|
70
85
|
|
71
86
|
group_klass = FIELD_GROUP_TYPES.detect { |k| k.compatible_with?(elements) }
|
72
|
-
|
87
|
+
if group_klass.nil?
|
88
|
+
raise UnsupportedFieldType.new("I can't handle these fields: #{elements.inspect}")
|
89
|
+
end
|
90
|
+
@fields << group_klass.new(self, group_name, elements)
|
73
91
|
end
|
74
92
|
end
|
75
93
|
end
|
@@ -4,12 +4,28 @@ require 'formulary/html_form/fields/input'
|
|
4
4
|
|
5
5
|
require 'formulary/html_form/fields/text_input'
|
6
6
|
Formulary::HtmlForm::FIELD_TYPES << Formulary::HtmlForm::Fields::TextInput
|
7
|
+
require 'formulary/html_form/fields/search_input'
|
8
|
+
Formulary::HtmlForm::FIELD_TYPES << Formulary::HtmlForm::Fields::SearchInput
|
9
|
+
require 'formulary/html_form/fields/password_input'
|
10
|
+
Formulary::HtmlForm::FIELD_TYPES << Formulary::HtmlForm::Fields::PasswordInput
|
11
|
+
require 'formulary/html_form/fields/color_input'
|
12
|
+
Formulary::HtmlForm::FIELD_TYPES << Formulary::HtmlForm::Fields::ColorInput
|
13
|
+
require 'formulary/html_form/fields/number_input'
|
14
|
+
Formulary::HtmlForm::FIELD_TYPES << Formulary::HtmlForm::Fields::NumberInput
|
15
|
+
require 'formulary/html_form/fields/range_input'
|
16
|
+
Formulary::HtmlForm::FIELD_TYPES << Formulary::HtmlForm::Fields::RangeInput
|
7
17
|
require 'formulary/html_form/fields/email_input'
|
8
18
|
Formulary::HtmlForm::FIELD_TYPES << Formulary::HtmlForm::Fields::EmailInput
|
9
19
|
require 'formulary/html_form/fields/tel_input'
|
10
20
|
Formulary::HtmlForm::FIELD_TYPES << Formulary::HtmlForm::Fields::TelInput
|
11
21
|
require 'formulary/html_form/fields/hidden_input'
|
12
22
|
Formulary::HtmlForm::FIELD_TYPES << Formulary::HtmlForm::Fields::HiddenInput
|
23
|
+
require 'formulary/html_form/fields/date_input'
|
24
|
+
Formulary::HtmlForm::FIELD_TYPES << Formulary::HtmlForm::Fields::DateInput
|
25
|
+
require 'formulary/html_form/fields/month_input'
|
26
|
+
Formulary::HtmlForm::FIELD_TYPES << Formulary::HtmlForm::Fields::MonthInput
|
27
|
+
require 'formulary/html_form/fields/week_input'
|
28
|
+
Formulary::HtmlForm::FIELD_TYPES << Formulary::HtmlForm::Fields::WeekInput
|
13
29
|
require 'formulary/html_form/fields/textarea'
|
14
30
|
Formulary::HtmlForm::FIELD_TYPES << Formulary::HtmlForm::Fields::Textarea
|
15
31
|
require 'formulary/html_form/fields/select'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Formulary::HtmlForm::Fields
|
2
|
+
class ColorInput < Input
|
3
|
+
def self.compatible_type
|
4
|
+
"color"
|
5
|
+
end
|
6
|
+
|
7
|
+
def valid?
|
8
|
+
super && color_correct?
|
9
|
+
end
|
10
|
+
|
11
|
+
def error
|
12
|
+
return super if super.present?
|
13
|
+
return "'#{label}' is not a valid color hex value" unless color_correct?
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def color_correct?
|
19
|
+
return true if @value.blank?
|
20
|
+
return true if @value.match(/\A#[0-9A-F]{6}\z/)
|
21
|
+
false
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Formulary::HtmlForm::Fields
|
2
|
+
class DateInput < Input
|
3
|
+
def self.compatible_type
|
4
|
+
"date"
|
5
|
+
end
|
6
|
+
|
7
|
+
def display_format
|
8
|
+
"YYYY-MM-DD"
|
9
|
+
end
|
10
|
+
|
11
|
+
def datetime_format
|
12
|
+
"%Y-%m-%d"
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(html_form, element)
|
16
|
+
@html_form, @element = html_form, element
|
17
|
+
@min = @element.attributes['min'].try('value')
|
18
|
+
@max = @element.attributes['max'].try('value')
|
19
|
+
end
|
20
|
+
|
21
|
+
def valid?
|
22
|
+
super && date_correct? && min_correct? && max_correct?
|
23
|
+
end
|
24
|
+
|
25
|
+
def error
|
26
|
+
return super if super.present?
|
27
|
+
return "'#{label}' is not a properly formatted #{self.class.compatible_type}, please use #{display_format}" unless date_correct?
|
28
|
+
return "'#{label}' must be a #{self.class.compatible_type} after #{@min}" unless min_correct?
|
29
|
+
return "'#{label}' must be a #{self.class.compatible_type} before #{@max}" unless max_correct?
|
30
|
+
end
|
31
|
+
|
32
|
+
protected
|
33
|
+
|
34
|
+
def date_correct?
|
35
|
+
return true if @value.blank?
|
36
|
+
Date.strptime(@value, datetime_format)
|
37
|
+
rescue ArgumentError => e
|
38
|
+
if e.message.include?("invalid date")
|
39
|
+
return false
|
40
|
+
else
|
41
|
+
raise
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def min_correct?
|
46
|
+
return true if @value.blank?
|
47
|
+
return true if @min.blank?
|
48
|
+
return true if @value >= @min
|
49
|
+
false
|
50
|
+
end
|
51
|
+
|
52
|
+
def max_correct?
|
53
|
+
return true if @value.blank?
|
54
|
+
return true if @max.blank?
|
55
|
+
return true if @value <= @max
|
56
|
+
false
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -1,5 +1,14 @@
|
|
1
1
|
module Formulary::HtmlForm::Fields
|
2
2
|
class EmailInput < Input
|
3
|
+
# The acceptable email pattern in the standard is defined in a language
|
4
|
+
# that might not be possible to use in Ruby. Or if it is, we haven't spent
|
5
|
+
# the time to find out or not. We did find this supposedly compatible
|
6
|
+
# regex on StackOverflow and it passes our tests so we're rolling with that
|
7
|
+
# for now.
|
8
|
+
#
|
9
|
+
# http://stackoverflow.com/questions/4940120/is-there-a-java-implementation-of-the-html5-input-email-validation
|
10
|
+
REGEX = /[A-Za-z0-9!#$%&'*+-\/=?^_`{|}~]+@[A-Za-z0-9-]+(.[A-Za-z0-9-]+)*/
|
11
|
+
|
3
12
|
def self.compatible_type
|
4
13
|
"email"
|
5
14
|
end
|
@@ -10,14 +19,14 @@ module Formulary::HtmlForm::Fields
|
|
10
19
|
|
11
20
|
def error
|
12
21
|
return super if super.present?
|
13
|
-
return "email" unless email_correct?
|
22
|
+
return "'#{label}' is not a valid email address" unless email_correct?
|
14
23
|
end
|
15
24
|
|
16
25
|
protected
|
17
26
|
|
18
27
|
def email_correct?
|
19
28
|
return true if @value.blank?
|
20
|
-
|
29
|
+
return true if @value.match(REGEX)
|
21
30
|
end
|
22
31
|
end
|
23
32
|
end
|
@@ -4,8 +4,8 @@ module Formulary::HtmlForm::Fields
|
|
4
4
|
false
|
5
5
|
end
|
6
6
|
|
7
|
-
def initialize(element)
|
8
|
-
@element = element
|
7
|
+
def initialize(html_form, element)
|
8
|
+
@html_form, @element = html_form, element
|
9
9
|
end
|
10
10
|
|
11
11
|
def name
|
@@ -21,7 +21,19 @@ module Formulary::HtmlForm::Fields
|
|
21
21
|
end
|
22
22
|
|
23
23
|
def error
|
24
|
-
return "required" if supports_required? && !presence_correct?
|
24
|
+
return "'#{label}' is required" if supports_required? && !presence_correct?
|
25
|
+
end
|
26
|
+
|
27
|
+
def label
|
28
|
+
@label ||= \
|
29
|
+
begin
|
30
|
+
l = @html_form.label_for_field(name)
|
31
|
+
|
32
|
+
if l.nil? then nil
|
33
|
+
elsif l.is_a?(String) then l
|
34
|
+
else l["fieldset"]
|
35
|
+
end
|
36
|
+
end
|
25
37
|
end
|
26
38
|
|
27
39
|
protected
|