formulary 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +65 -34
  3. data/formulary.gemspec +0 -1
  4. data/lib/formulary.rb +1 -5
  5. data/lib/formulary/html_form.rb +31 -13
  6. data/lib/formulary/html_form/fields.rb +16 -0
  7. data/lib/formulary/html_form/fields/checkbox_group.rb +1 -1
  8. data/lib/formulary/html_form/fields/color_input.rb +24 -0
  9. data/lib/formulary/html_form/fields/date_input.rb +59 -0
  10. data/lib/formulary/html_form/fields/email_input.rb +11 -2
  11. data/lib/formulary/html_form/fields/field.rb +15 -3
  12. data/lib/formulary/html_form/fields/field_group.rb +3 -3
  13. data/lib/formulary/html_form/fields/input.rb +1 -1
  14. data/lib/formulary/html_form/fields/month_input.rb +15 -0
  15. data/lib/formulary/html_form/fields/number_input.rb +63 -0
  16. data/lib/formulary/html_form/fields/password_input.rb +7 -0
  17. data/lib/formulary/html_form/fields/range_input.rb +7 -0
  18. data/lib/formulary/html_form/fields/search_input.rb +7 -0
  19. data/lib/formulary/html_form/fields/select.rb +1 -1
  20. data/lib/formulary/html_form/fields/week_input.rb +15 -0
  21. data/lib/formulary/html_form/labels.rb +39 -0
  22. data/lib/formulary/version.rb +1 -1
  23. data/spec/html_form/fields/checkbox_group_spec.rb +23 -10
  24. data/spec/html_form/fields/color_input_spec.rb +58 -0
  25. data/spec/html_form/fields/date_input_spec.rb +60 -0
  26. data/spec/html_form/fields/email_input_spec.rb +16 -6
  27. data/spec/html_form/fields/field_spec.rb +40 -1
  28. data/spec/html_form/fields/hidden_input_spec.rb +3 -1
  29. data/spec/html_form/fields/month_input_spec.rb +60 -0
  30. data/spec/html_form/fields/number_input_spec.rb +67 -0
  31. data/spec/html_form/fields/password_input_spec.rb +29 -0
  32. data/spec/html_form/fields/radio_button_group_spec.rb +27 -8
  33. data/spec/html_form/fields/range_input_spec.rb +20 -0
  34. data/spec/html_form/fields/search_input_spec.rb +18 -0
  35. data/spec/html_form/fields/select_spec.rb +10 -7
  36. data/spec/html_form/fields/tel_input_spec.rb +3 -3
  37. data/spec/html_form/fields/text_input_spec.rb +3 -3
  38. data/spec/html_form/fields/textarea_spec.rb +2 -2
  39. data/spec/html_form/fields/week_input_spec.rb +60 -0
  40. data/spec/html_form_spec.rb +234 -100
  41. data/spec/support/element_helper.rb +1 -1
  42. data/spec/support/shared_examples_for_pattern.rb +4 -2
  43. data/spec/support/shared_examples_for_required.rb +4 -2
  44. metadata +114 -106
@@ -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
- $ bundle
20
+ $ bundle
21
21
 
22
22
  Or install it yourself as:
23
23
 
24
- $ gem install formulary
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
- <input type="email" name="email" required />
37
- <input type="username" name="username" pattern="[a-z0-9_-]{3,16}" />
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
- - type="email"
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
- - pattern="REGEX"
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
- - checkbox, radio and multiselect tags have one of the valid options selected
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
- * Don Petersen / [@dpetersen](https://github.com/dpetersen)
82
- * Matt Bohme / [@quady](https://github.com/quady)
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
- MIT License
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
- Permission is hereby granted, free of charge, to any person obtaining
103
- a copy of this software and associated documentation files (the
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
- The above copyright notice and this permission notice shall be
111
- included in all copies or substantial portions of the Software.
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.
@@ -20,7 +20,6 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_dependency "nokogiri"
22
22
  spec.add_dependency "activesupport"
23
- spec.add_dependency "email_veracity"
24
23
 
25
24
  spec.add_development_dependency "bundler", "~> 1.3"
26
25
  spec.add_development_dependency "rake"
@@ -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"
@@ -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
- raise UnexpectedParameter.new("Got unexpected field '#{key}'") unless find_field(key)
22
-
23
- find_field(key).set_value(value)
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(doc)
50
- build_grouped_fields_from(doc)
64
+ build_singular_fields_from
65
+ build_grouped_fields_from
51
66
  end
52
67
 
53
- def build_singular_fields_from(doc)
54
- doc.css(SINGULAR_FIELD_SELECTOR.strip).map do |element|
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(doc)
64
- grouped_elements = doc.css(GROUPED_FIELD_SELECTOR.strip).group_by do |element|
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
- @fields << group_klass.new(group_name, elements)
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'
@@ -8,7 +8,7 @@ module Formulary::HtmlForm::Fields
8
8
  true
9
9
  end
10
10
 
11
- def initialize(group_name, elements)
11
+ def initialize(html_form, group_name, elements)
12
12
  super
13
13
  @values = []
14
14
  end
@@ -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
- EmailVeracity::Address.new(@value).valid?
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