judge 0.5.0 → 1.0.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 (79) hide show
  1. data/.gitignore +8 -0
  2. data/.travis.yml +2 -1
  3. data/Gemfile +1 -10
  4. data/Rakefile +9 -44
  5. data/judge.gemspec +17 -164
  6. data/lib/generators/judge/templates/json2.js +46 -42
  7. data/lib/generators/judge/templates/judge.js +42 -41
  8. data/lib/generators/judge/templates/underscore.js +219 -97
  9. data/lib/judge.rb +6 -4
  10. data/lib/judge/form_builder.rb +75 -0
  11. data/lib/judge/{utils.rb → message_collection.rb} +45 -28
  12. data/lib/judge/validator.rb +21 -0
  13. data/lib/judge/validator_collection.rb +28 -0
  14. data/lib/judge/version.rb +3 -0
  15. data/spec/javascripts/JudgeSpec.js +25 -23
  16. data/test/expected_elements.rb +235 -0
  17. data/test/factories.rb +23 -0
  18. data/test/setup.rb +70 -0
  19. data/test/test_form_builder.rb +69 -0
  20. data/test/test_helper.rb +8 -20
  21. data/test/test_message_collection.rb +84 -0
  22. data/test/test_validator.rb +37 -0
  23. data/test/test_validator_collection.rb +19 -0
  24. metadata +46 -137
  25. data/.document +0 -5
  26. data/Gemfile.lock +0 -116
  27. data/VERSION +0 -1
  28. data/docs/docco.css +0 -196
  29. data/docs/judge.html +0 -280
  30. data/lib/judge/form.rb +0 -59
  31. data/test/dummy/Gemfile +0 -3
  32. data/test/dummy/Gemfile.lock +0 -75
  33. data/test/dummy/Rakefile +0 -7
  34. data/test/dummy/app/controllers/application_controller.rb +0 -3
  35. data/test/dummy/app/controllers/foos_controller.rb +0 -11
  36. data/test/dummy/app/helpers/application_helper.rb +0 -2
  37. data/test/dummy/app/models/city.rb +0 -5
  38. data/test/dummy/app/models/continent.rb +0 -4
  39. data/test/dummy/app/models/country.rb +0 -6
  40. data/test/dummy/app/models/fake.rb +0 -2
  41. data/test/dummy/app/models/foo.rb +0 -19
  42. data/test/dummy/app/views/foos/new.html.erb +0 -71
  43. data/test/dummy/app/views/layouts/application.html.erb +0 -14
  44. data/test/dummy/config.ru +0 -4
  45. data/test/dummy/config/application.rb +0 -45
  46. data/test/dummy/config/boot.rb +0 -10
  47. data/test/dummy/config/database.yml +0 -22
  48. data/test/dummy/config/environment.rb +0 -5
  49. data/test/dummy/config/environments/development.rb +0 -26
  50. data/test/dummy/config/environments/production.rb +0 -49
  51. data/test/dummy/config/environments/test.rb +0 -35
  52. data/test/dummy/config/initializers/backtrace_silencers.rb +0 -7
  53. data/test/dummy/config/initializers/inflections.rb +0 -10
  54. data/test/dummy/config/initializers/mime_types.rb +0 -5
  55. data/test/dummy/config/initializers/secret_token.rb +0 -7
  56. data/test/dummy/config/initializers/session_store.rb +0 -8
  57. data/test/dummy/config/locales/en.yml +0 -12
  58. data/test/dummy/config/routes.rb +0 -3
  59. data/test/dummy/db/development.sqlite3 +0 -0
  60. data/test/dummy/db/migrate/20110624115516_create_foos.rb +0 -26
  61. data/test/dummy/db/migrate/20110724201117_create_fake_collections.rb +0 -14
  62. data/test/dummy/db/migrate/20110724201548_rename_fake_collection_to_fake.rb +0 -9
  63. data/test/dummy/db/migrate/20110725082530_create_continent_country_and_city_tables.rb +0 -24
  64. data/test/dummy/db/schema.rb +0 -55
  65. data/test/dummy/db/test.sqlite3 +0 -0
  66. data/test/dummy/log/server.log +0 -0
  67. data/test/dummy/public/404.html +0 -26
  68. data/test/dummy/public/422.html +0 -26
  69. data/test/dummy/public/500.html +0 -26
  70. data/test/dummy/public/favicon.ico +0 -0
  71. data/test/dummy/public/javascripts/application.js +0 -2
  72. data/test/dummy/public/javascripts/controls.js +0 -965
  73. data/test/dummy/public/javascripts/dragdrop.js +0 -974
  74. data/test/dummy/public/javascripts/effects.js +0 -1123
  75. data/test/dummy/public/javascripts/prototype.js +0 -6001
  76. data/test/dummy/public/javascripts/rails.js +0 -175
  77. data/test/dummy/public/stylesheets/.gitkeep +0 -0
  78. data/test/dummy/script/rails +0 -6
  79. data/test/judge_test.rb +0 -186
data/lib/judge.rb CHANGED
@@ -1,4 +1,6 @@
1
- require File.dirname(__FILE__) + '/judge/utils'
2
- require File.dirname(__FILE__) + '/judge/form'
3
-
4
- ::ActionView::Helpers::FormBuilder.send(:include, Judge::FormBuilder)
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "judge"))
2
+ require "version"
3
+ require "validator"
4
+ require "validator_collection"
5
+ require "message_collection"
6
+ require "form_builder"
@@ -0,0 +1,75 @@
1
+ module Judge
2
+
3
+ class FormBuilder < ActionView::Helpers::FormBuilder
4
+
5
+ #include ActionView::Helpers::TagHelper
6
+
7
+ %w{text_field text_area password_field}.each do |type|
8
+ helper = <<-END
9
+ def #{type}(method, options = {})
10
+ if options.delete(:validate).present?
11
+ options = { "data-validate" => Judge::ValidatorCollection.new(self.object, method).to_json }.merge(options)
12
+ end
13
+ @template.#{type}(self.object_name, method, options)
14
+ end
15
+ END
16
+ class_eval helper, __FILE__, __LINE__
17
+ end
18
+
19
+ def radio_button(method, tag_value, options = {})
20
+ if options.delete(:validate).present?
21
+ options = { "data-validate" => Judge::ValidatorCollection.new(self.object, method).to_json }.merge(options)
22
+ end
23
+ @template.radio_button(@object_name, method, tag_value, objectify_options(options))
24
+ end
25
+
26
+ def check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
27
+ if options.delete(:validate).present?
28
+ options = { "data-validate" => Judge::ValidatorCollection.new(self.object, method).to_json }.merge(options)
29
+ end
30
+ @template.check_box(self.object_name, method, objectify_options(options), checked_value, unchecked_value)
31
+ end
32
+
33
+ def select(method, choices, options = {}, html_options = {})
34
+ if options.delete(:validate).present?
35
+ html_options = { "data-validate" => Judge::ValidatorCollection.new(self.object, method).to_json }.merge(html_options)
36
+ end
37
+ @template.select(self.object_name, method, choices, objectify_options(options), @default_options.merge(html_options))
38
+ end
39
+
40
+ def collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
41
+ if options.delete(:validate).present?
42
+ html_options = { "data-validate" => Judge::ValidatorCollection.new(self.object, method).to_json }.merge(html_options)
43
+ end
44
+ @template.collection_select(self.object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options))
45
+ end
46
+
47
+ def grouped_collection_select(method, collection, group_method, group_label_method, option_key_method, option_value_method, options = {}, html_options = {})
48
+ if options.delete(:validate).present?
49
+ html_options = { "data-validate" => Judge::ValidatorCollection.new(self.object, method).to_json }.merge(html_options)
50
+ end
51
+ @template.grouped_collection_select(self.object_name, method, collection, group_method, group_label_method, option_key_method, option_value_method, objectify_options(options), @default_options.merge(html_options))
52
+ end
53
+
54
+ %w{date_select datetime_select time_select}.each do |type|
55
+ helper = <<-END
56
+ def #{type}(method, options = {}, html_options = {})
57
+ if options.delete(:validate).present?
58
+ html_options = { "data-validate" => Judge::ValidatorCollection.new(self.object, method).to_json }.merge(html_options)
59
+ end
60
+ @template.#{type}(self.object_name, method, objectify_options(options), html_options)
61
+ end
62
+ END
63
+ class_eval helper, __FILE__, __LINE__
64
+ end
65
+
66
+ def time_zone_select(method, priority_zones = nil, options = {}, html_options = {})
67
+ if options.delete(:validate).present?
68
+ html_options = { "data-validate" => Judge::ValidatorCollection.new(self.object, method).to_json }.merge(html_options)
69
+ end
70
+ @template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options))
71
+ end
72
+
73
+ end
74
+
75
+ end
@@ -1,8 +1,6 @@
1
1
  module Judge
2
-
3
- module Utils
4
2
 
5
- extend self
3
+ class MessageCollection
6
4
 
7
5
  MESSAGE_MAP = {
8
6
  :confirmation => { :base => :confirmation },
@@ -33,48 +31,67 @@ module Judge
33
31
 
34
32
  ALLOW_BLANK = [:format, :exclusion, :inclusion, :length]
35
33
 
36
- # Returns all decorated validators for an object's method, as JSON
37
- # Judge::Utils.validators_to_json(@user, :name)
38
- def validators_to_json(object, method)
39
- validators = object.class.validators_on(method).reject { |validator| validator.kind == :uniqueness }
40
- validators.collect! do |validator|
41
- Judge::Utils.decorate_validator(validator, object, method)
34
+ DEFAULT_OPTS = { :generate => true }
35
+
36
+ attr_reader :object, :method, :kind, :options, :mm
37
+ attr_accessor :messages
38
+
39
+ def initialize(object, method, amv, opts = {})
40
+ opts = DEFAULT_OPTS.merge(opts)
41
+ @object = object
42
+ @method = method
43
+ @kind = amv.kind
44
+ @options = amv.options.dup
45
+ @mm = MESSAGE_MAP
46
+ @messages = {}
47
+ generate_messages! unless opts[:generate] == false
48
+ end
49
+
50
+ def generate_messages!
51
+ if messages.blank?
52
+ %w{base options blank integer}.each do |type|
53
+ self.send(:"generate_#{type}!")
54
+ end
42
55
  end
43
- validators.to_json
44
56
  end
45
-
46
- # Convert validator to hash, removing all parts we don't support and adding all possible error messages
47
- # Judge::Utils.decorate_validator(validator_instance, @user, :name)
48
- def decorate_validator(validator, object, method)
49
- kind = validator.kind
50
- mm = MESSAGE_MAP
51
57
 
52
- # remove callbacks and tokenizer, which we don't support
53
- validator_options = validator.options.reject { |key| [:if, :on, :unless, :tokenizer].include?(key) }
54
-
55
- messages = {}
58
+ def to_hash
59
+ messages
60
+ end
61
+
62
+ private
63
+
64
+ def generate_base!
56
65
  if mm.has_key?(kind) && mm[kind][:base].present?
57
66
  base_message = mm[kind][:base]
58
- messages[base_message] = object.errors.generate_message(method, base_message, validator_options)
67
+ messages[base_message] = object.errors.generate_message(method, base_message, options)
59
68
  end
69
+ end
70
+
71
+ def generate_options!
60
72
  if mm.has_key?(kind) && mm[kind][:options].present?
61
73
  opt_messages = mm[kind][:options]
62
74
  opt_messages.each do |opt, opt_message|
63
- if validator_options.has_key?(opt)
64
- options_for_interpolation = { :count => validator_options[opt] }.merge(validator_options)
75
+ if options.has_key?(opt)
76
+ options_for_interpolation = { :count => options[opt] }.merge(options)
65
77
  messages[opt_message] = object.errors.generate_message(method, opt_message, options_for_interpolation)
66
78
  end
67
79
  end
68
80
  end
69
- if ALLOW_BLANK.include?(kind) && validator_options[:allow_blank].blank? && messages[:blank].blank?
81
+ end
82
+
83
+ def generate_blank!
84
+ if ALLOW_BLANK.include?(kind) && options[:allow_blank].blank? && messages[:blank].blank?
70
85
  messages[:blank] = object.errors.generate_message(method, :blank)
71
86
  end
72
- if kind == :numericality && validator_options[:only_integer].present?
87
+ end
88
+
89
+ def generate_integer!
90
+ if kind == :numericality && options[:only_integer].present?
73
91
  messages[:not_an_integer] = object.errors.generate_message(method, :not_an_integer)
74
92
  end
75
-
76
- { :kind => kind, :options => validator_options, :messages => messages }
77
93
  end
78
94
 
79
95
  end
80
- end
96
+
97
+ end
@@ -0,0 +1,21 @@
1
+ module Judge
2
+
3
+ class Validator
4
+
5
+ attr_reader :active_model_validator, :kind, :options, :method, :messages
6
+
7
+ def initialize(amv, method, message_collection)
8
+ @active_model_validator = amv
9
+ @kind = @active_model_validator.kind
10
+ @options = @active_model_validator.options.reject { |key| [:if, :on, :unless, :tokenizer].include?(key) }
11
+ @method = method
12
+ @messages = message_collection
13
+ end
14
+
15
+ def to_hash
16
+ { :kind => kind, :options => options, :messages => messages.to_hash }
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,28 @@
1
+ module Judge
2
+
3
+ class ValidatorCollection
4
+
5
+ include Enumerable
6
+
7
+ attr_reader :validators
8
+
9
+ def initialize(object, method)
10
+ active_model_validators = object.class.validators_on(method).reject { |amv| amv.kind == :uniqueness }
11
+ @validators = active_model_validators.map do |amv|
12
+ Judge::Validator.new(amv, method, Judge::MessageCollection.new(object, method, amv))
13
+ end
14
+ end
15
+
16
+ def each
17
+ validators.each do |v|
18
+ yield v
19
+ end
20
+ end
21
+
22
+ def to_json
23
+ validators.map { |v| v.to_hash }.to_json
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,3 @@
1
+ module Judge
2
+ VERSION = "1.0.0"
3
+ end
@@ -114,7 +114,6 @@ describe('judge', function() {
114
114
  judge.store.save('mykey', document.getElementById('foo_two_foobar'));
115
115
  var d = judge.store.getDOM('mykey');
116
116
  expect(d.length).toEqual(2);
117
- console.log(d);
118
117
  expect(Object.prototype.toString.call(d[0])).toEqual('[object HTMLSelectElement]');
119
118
  });
120
119
 
@@ -134,12 +133,33 @@ describe('judge', function() {
134
133
 
135
134
  });
136
135
 
136
+ describe('validate', function() {
137
+
138
+ it('validates all elements stored against key', function() {
139
+ judge.store.save('mykey', e);
140
+ var results = judge.store.validate('mykey');
141
+ expect(_(results).first()).toBeInstanceOf(Object);
142
+ expect(_(results).first().element).toEqual(e);
143
+ });
144
+
145
+ it('returns null if no elements found', function() {
146
+ var results = judge.store.validate('mykey');
147
+ expect(results).toBe(null);
148
+ });
149
+
150
+ it('returns null if key is not passed', function() {
151
+ var results = judge.store.validate();
152
+ expect(results).toBe(null);
153
+ });
154
+
155
+ });
156
+
137
157
  describe('remove', function() {
138
158
 
139
159
  it('removes Watcher from store', function() {
140
160
  judge.store.save('mykey', e);
141
- expect(judge.store.remove('mykey', e)).not.toEqual(null);
142
- expect(judge.store.get('mykey').length).toEqual(0);
161
+ expect(_(judge.store.remove('mykey', e)).isUndefined()).toEqual(true);
162
+ expect(judge.store.get('mykey')).toBe(null);
143
163
  });
144
164
 
145
165
  it('returns null if key not found', function() {
@@ -161,12 +181,12 @@ describe('judge', function() {
161
181
  judge.store.save('mykey', e);
162
182
  judge.store.save('mykey2', e);
163
183
  judge.store.clear('mykey');
164
- expect(judge.store.get('mykey')).toEqual([]);
184
+ expect(judge.store.get('mykey')).toBe(null);
165
185
  expect(judge.store.get('mykey2').length).toEqual(1);
166
186
  });
167
187
 
168
188
  it('returns null if key not found', function() {
169
- expect(judge.store.clear('notakey')).toEqual(null);
189
+ expect(judge.store.clear('notakey')).toBe(null);
170
190
  });
171
191
 
172
192
  });
@@ -509,24 +529,6 @@ describe('judge', function() {
509
529
 
510
530
  describe('utils', function() {
511
531
 
512
- describe('isValidatable', function() {
513
-
514
- it('returns true if judge can validate object', function() {
515
- var i = document.createElement('input'),
516
- s = document.createElement('select'),
517
- t = document.createElement('textarea');
518
- expect(judge.utils.isValidatable(i)).toEqual(true);
519
- expect(judge.utils.isValidatable(s)).toEqual(true);
520
- expect(judge.utils.isValidatable(t)).toEqual(true);
521
- });
522
-
523
- it('returns false otherwise', function() {
524
- var p = document.createElement('p');
525
- expect(judge.utils.isValidatable(p)).toEqual(false);
526
- });
527
-
528
- });
529
-
530
532
  describe('isCollection', function() {
531
533
 
532
534
  beforeEach(function() {
@@ -0,0 +1,235 @@
1
+ module ExpectedElements
2
+
3
+ def expected_text_field
4
+ %Q{<input data-validate="[{&quot;kind&quot;:&quot;presence&quot;,&quot;options&quot;:{},&quot;messages&quot;:{&quot;blank&quot;:&quot;can't be blank&quot;}}]" id="user_name" name="user[name]" size="30" type="text" />}
5
+ end
6
+
7
+ def expected_text_area
8
+ %Q{<textarea cols="40" data-validate="[{&quot;kind&quot;:&quot;presence&quot;,&quot;options&quot;:{},&quot;messages&quot;:{&quot;blank&quot;:&quot;can't be blank&quot;}}]" id="user_bio" name="user[bio]" rows="20"></textarea>}
9
+ end
10
+
11
+ def expected_password_field
12
+ %Q{<input data-validate="[{&quot;kind&quot;:&quot;format&quot;,&quot;options&quot;:{&quot;with&quot;:&quot;(?-mix:.+)&quot;},&quot;messages&quot;:{&quot;invalid&quot;:&quot;is invalid&quot;,&quot;blank&quot;:&quot;can't be blank&quot;}},{&quot;kind&quot;:&quot;confirmation&quot;,&quot;options&quot;:{},&quot;messages&quot;:{&quot;confirmation&quot;:&quot;doesn't match confirmation&quot;}}]" id="user_password" name="user[password]" size="30" type="password" />}
13
+ end
14
+
15
+ def expected_check_box
16
+ %Q{<input data-validate="[{&quot;kind&quot;:&quot;acceptance&quot;,&quot;options&quot;:{&quot;allow_nil&quot;:true,&quot;accept&quot;:&quot;1&quot;},&quot;messages&quot;:{&quot;accepted&quot;:&quot;must be accepted&quot;}}]" id="user_accepted" name="user[accepted]" size="30" type="password" />}
17
+ end
18
+
19
+ def expected_radio_button
20
+ %Q{<input data-validate="[{&quot;kind&quot;:&quot;inclusion&quot;,&quot;options&quot;:{&quot;in&quot;:[&quot;male&quot;,&quot;female&quot;,&quot;other&quot;,&quot;withheld&quot;]},&quot;messages&quot;:{&quot;inclusion&quot;:&quot;is not included in the list&quot;,&quot;blank&quot;:&quot;can't be blank&quot;}}]" id="user_gender_female" name="user[gender]" type="radio" value="female" />}
21
+ end
22
+
23
+ def expected_select
24
+ %Q{<select data-validate="[{&quot;kind&quot;:&quot;format&quot;,&quot;options&quot;:{&quot;with&quot;:&quot;(?-mix:[A-Za-z])&quot;,&quot;allow_blank&quot;:true},&quot;messages&quot;:{&quot;invalid&quot;:&quot;is invalid&quot;}}]" id="user_country" name="user[country]"><option value="US">US</option>
25
+ <option value="GB">GB</option></select>}
26
+ end
27
+
28
+ def expected_collection_select
29
+ %Q{<select data-validate="[{&quot;kind&quot;:&quot;presence&quot;,&quot;options&quot;:{},&quot;messages&quot;:{&quot;blank&quot;:&quot;can't be blank&quot;}}]" id="user_team_id" name="user[team_id]"><option value="1">Team 1</option>
30
+ <option value="2">Team 2</option>
31
+ <option value="3">Team 3</option>
32
+ <option value="4">Team 4</option>
33
+ <option value="5">Team 5</option></select>}
34
+ end
35
+
36
+ def expected_grouped_collection_select
37
+ %Q{<select data-validate="[{&quot;kind&quot;:&quot;presence&quot;,&quot;options&quot;:{},&quot;messages&quot;:{&quot;blank&quot;:&quot;can't be blank&quot;}}]" id="user_discipline_id" name="user[discipline_id]"><optgroup label="Category 1"><option value="">Sport 1</option></optgroup></select>}
38
+ end
39
+
40
+ def expected_date_select
41
+ %Q{<select data-validate="[{&quot;kind&quot;:&quot;presence&quot;,&quot;options&quot;:{},&quot;messages&quot;:{&quot;blank&quot;:&quot;can't be blank&quot;}}]" id="user_dob_1i" name="user[dob(1i)]">
42
+ <option value="2006">2006</option>
43
+ <option value="2007">2007</option>
44
+ <option value="2008">2008</option>
45
+ <option value="2009">2009</option>
46
+ <option value="2010">2010</option>
47
+ <option selected="selected" value="2011">2011</option>
48
+ <option value="2012">2012</option>
49
+ <option value="2013">2013</option>
50
+ <option value="2014">2014</option>
51
+ <option value="2015">2015</option>
52
+ <option value="2016">2016</option>
53
+ </select>
54
+ <select data-validate="[{&quot;kind&quot;:&quot;presence&quot;,&quot;options&quot;:{},&quot;messages&quot;:{&quot;blank&quot;:&quot;can't be blank&quot;}}]" id="user_dob_2i" name="user[dob(2i)]">
55
+ <option value="1">January</option>
56
+ <option value="2">February</option>
57
+ <option value="3">March</option>
58
+ <option value="4">April</option>
59
+ <option value="5">May</option>
60
+ <option value="6">June</option>
61
+ <option value="7">July</option>
62
+ <option value="8">August</option>
63
+ <option value="9">September</option>
64
+ <option value="10">October</option>
65
+ <option selected="selected" value="11">November</option>
66
+ <option value="12">December</option>
67
+ </select>
68
+ <select data-validate="[{&quot;kind&quot;:&quot;presence&quot;,&quot;options&quot;:{},&quot;messages&quot;:{&quot;blank&quot;:&quot;can't be blank&quot;}}]" id="user_dob_3i" name="user[dob(3i)]">
69
+ <option value="1">1</option>
70
+ <option value="2">2</option>
71
+ <option value="3">3</option>
72
+ <option value="4">4</option>
73
+ <option selected="selected" value="5">5</option>
74
+ <option value="6">6</option>
75
+ <option value="7">7</option>
76
+ <option value="8">8</option>
77
+ <option value="9">9</option>
78
+ <option value="10">10</option>
79
+ <option value="11">11</option>
80
+ <option value="12">12</option>
81
+ <option value="13">13</option>
82
+ <option value="14">14</option>
83
+ <option value="15">15</option>
84
+ <option value="16">16</option>
85
+ <option value="17">17</option>
86
+ <option value="18">18</option>
87
+ <option value="19">19</option>
88
+ <option value="20">20</option>
89
+ <option value="21">21</option>
90
+ <option value="22">22</option>
91
+ <option value="23">23</option>
92
+ <option value="24">24</option>
93
+ <option value="25">25</option>
94
+ <option value="26">26</option>
95
+ <option value="27">27</option>
96
+ <option value="28">28</option>
97
+ <option value="29">29</option>
98
+ <option value="30">30</option>
99
+ <option value="31">31</option>
100
+ </select>
101
+ }
102
+ end
103
+
104
+ def expected_datetime_select
105
+ %Q{<select data-validate="[{&quot;kind&quot;:&quot;presence&quot;,&quot;options&quot;:{},&quot;messages&quot;:{&quot;blank&quot;:&quot;can't be blank&quot;}}]" id="user_dob_1i" name="user[dob(1i)]">
106
+ <option value="2006">2006</option>
107
+ <option value="2007">2007</option>
108
+ <option value="2008">2008</option>
109
+ <option value="2009">2009</option>
110
+ <option value="2010">2010</option>
111
+ <option selected="selected" value="2011">2011</option>
112
+ <option value="2012">2012</option>
113
+ <option value="2013">2013</option>
114
+ <option value="2014">2014</option>
115
+ <option value="2015">2015</option>
116
+ <option value="2016">2016</option>
117
+ </select>
118
+ <select data-validate="[{&quot;kind&quot;:&quot;presence&quot;,&quot;options&quot;:{},&quot;messages&quot;:{&quot;blank&quot;:&quot;can't be blank&quot;}}]" id="user_dob_2i" name="user[dob(2i)]">
119
+ <option value="1">January</option>
120
+ <option value="2">February</option>
121
+ <option value="3">March</option>
122
+ <option value="4">April</option>
123
+ <option value="5">May</option>
124
+ <option value="6">June</option>
125
+ <option value="7">July</option>
126
+ <option value="8">August</option>
127
+ <option value="9">September</option>
128
+ <option value="10">October</option>
129
+ <option selected="selected" value="11">November</option>
130
+ <option value="12">December</option>
131
+ </select>
132
+ <select data-validate="[{&quot;kind&quot;:&quot;presence&quot;,&quot;options&quot;:{},&quot;messages&quot;:{&quot;blank&quot;:&quot;can't be blank&quot;}}]" id="user_dob_3i" name="user[dob(3i)]">
133
+ <option value="1">1</option>
134
+ <option value="2">2</option>
135
+ <option value="3">3</option>
136
+ <option value="4">4</option>
137
+ <option selected="selected" value="5">5</option>
138
+ <option value="6">6</option>
139
+ <option value="7">7</option>
140
+ <option value="8">8</option>
141
+ <option value="9">9</option>
142
+ <option value="10">10</option>
143
+ <option value="11">11</option>
144
+ <option value="12">12</option>
145
+ <option value="13">13</option>
146
+ <option value="14">14</option>
147
+ <option value="15">15</option>
148
+ <option value="16">16</option>
149
+ <option value="17">17</option>
150
+ <option value="18">18</option>
151
+ <option value="19">19</option>
152
+ <option value="20">20</option>
153
+ <option value="21">21</option>
154
+ <option value="22">22</option>
155
+ <option value="23">23</option>
156
+ <option value="24">24</option>
157
+ <option value="25">25</option>
158
+ <option value="26">26</option>
159
+ <option value="27">27</option>
160
+ <option value="28">28</option>
161
+ <option value="29">29</option>
162
+ <option value="30">30</option>
163
+ <option value="31">31</option>
164
+ </select>
165
+ &mdash; <select data-validate="[{&quot;kind&quot;:&quot;presence&quot;,&quot;options&quot;:{},&quot;messages&quot;:{&quot;blank&quot;:&quot;can't be blank&quot;}}]" id="user_dob_4i" name="user[dob(4i)]">
166
+ <option value="00">00</option>
167
+ <option value="01">01</option>
168
+ <option value="02">02</option>
169
+ <option value="03">03</option>
170
+ <option value="04">04</option>
171
+ <option value="05">05</option>
172
+ <option value="06">06</option>
173
+ <option value="07">07</option>
174
+ <option value="08">08</option>
175
+ <option value="09">09</option>
176
+ <option value="10">10</option>
177
+ <option value="11">11</option>
178
+ <option value="12">12</option>
179
+ <option value="13">13</option>
180
+ <option value="14">14</option>
181
+ <option value="15">15</option>
182
+ <option value="16">16</option>
183
+ <option selected="selected" value="17">17</option>
184
+ <option value="18">18</option>
185
+ <option value="19">19</option>
186
+ <option value="20">20</option>
187
+ <option value="21">21</option>
188
+ <option value="22">22</option>
189
+ <option value="23">23</option>
190
+ </select>
191
+ : <select data-validate="[{&quot;kind&quot;:&quot;presence&quot;,&quot;options&quot;:{},&quot;messages&quot;:{&quot;blank&quot;:&quot;can't be blank&quot;}}]" id="user_dob_5i" name="user[dob(5i)]">
192
+ <option selected="selected" value="00">00</option>
193
+ <option value="30">30</option>
194
+ </select>
195
+ }
196
+ end
197
+
198
+ def expected_time_select
199
+ %Q{<input id="user_dob_1i" name="user[dob(1i)]" type="hidden" value="2011" />
200
+ <input id="user_dob_2i" name="user[dob(2i)]" type="hidden" value="11" />
201
+ <input id="user_dob_3i" name="user[dob(3i)]" type="hidden" value="5" />
202
+ <select data-validate="[{&quot;kind&quot;:&quot;presence&quot;,&quot;options&quot;:{},&quot;messages&quot;:{&quot;blank&quot;:&quot;can't be blank&quot;}}]" id="user_dob_4i" name="user[dob(4i)]">
203
+ <option value="00">00</option>
204
+ <option value="01">01</option>
205
+ <option value="02">02</option>
206
+ <option value="03">03</option>
207
+ <option value="04">04</option>
208
+ <option value="05">05</option>
209
+ <option value="06">06</option>
210
+ <option value="07">07</option>
211
+ <option value="08">08</option>
212
+ <option value="09">09</option>
213
+ <option value="10">10</option>
214
+ <option value="11">11</option>
215
+ <option value="12">12</option>
216
+ <option value="13">13</option>
217
+ <option value="14">14</option>
218
+ <option value="15">15</option>
219
+ <option value="16">16</option>
220
+ <option selected="selected" value="17">17</option>
221
+ <option value="18">18</option>
222
+ <option value="19">19</option>
223
+ <option value="20">20</option>
224
+ <option value="21">21</option>
225
+ <option value="22">22</option>
226
+ <option value="23">23</option>
227
+ </select>
228
+ : <select data-validate="[{&quot;kind&quot;:&quot;presence&quot;,&quot;options&quot;:{},&quot;messages&quot;:{&quot;blank&quot;:&quot;can't be blank&quot;}}]" id="user_dob_5i" name="user[dob(5i)]">
229
+ <option selected="selected" value="00">00</option>
230
+ <option value="30">30</option>
231
+ </select>
232
+ }
233
+ end
234
+
235
+ end