case_form 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.
Files changed (88) hide show
  1. data/CHANGELOG.rdoc +1 -0
  2. data/MIT-LICENSE.rdoc +20 -0
  3. data/Manifest +86 -0
  4. data/README.rdoc +0 -0
  5. data/Rakefile +30 -0
  6. data/TODO.rdoc +7 -0
  7. data/case_form.gemspec +31 -0
  8. data/lib/case_form.rb +88 -0
  9. data/lib/case_form/associations.rb +50 -0
  10. data/lib/case_form/buttons.rb +175 -0
  11. data/lib/case_form/core_ext/form_helper.rb +54 -0
  12. data/lib/case_form/core_ext/layout_helper.rb +16 -0
  13. data/lib/case_form/core_ext/sentence_error.rb +38 -0
  14. data/lib/case_form/element.rb +40 -0
  15. data/lib/case_form/element/base.rb +95 -0
  16. data/lib/case_form/element/button.rb +64 -0
  17. data/lib/case_form/element/error.rb +54 -0
  18. data/lib/case_form/element/errors/complex_error.rb +107 -0
  19. data/lib/case_form/element/errors/simple_error.rb +76 -0
  20. data/lib/case_form/element/fieldset.rb +35 -0
  21. data/lib/case_form/element/hint.rb +54 -0
  22. data/lib/case_form/element/input.rb +106 -0
  23. data/lib/case_form/element/inputs/collection/checkbox_input.rb +36 -0
  24. data/lib/case_form/element/inputs/collection/radio_input.rb +27 -0
  25. data/lib/case_form/element/inputs/collection/select_input.rb +22 -0
  26. data/lib/case_form/element/inputs/collection_input.rb +89 -0
  27. data/lib/case_form/element/inputs/datetime/date_input.rb +45 -0
  28. data/lib/case_form/element/inputs/datetime/date_time_input.rb +50 -0
  29. data/lib/case_form/element/inputs/datetime/time_input.rb +34 -0
  30. data/lib/case_form/element/inputs/datetime/time_zone_input.rb +24 -0
  31. data/lib/case_form/element/inputs/file_input.rb +13 -0
  32. data/lib/case_form/element/inputs/hidden_input.rb +17 -0
  33. data/lib/case_form/element/inputs/number_input.rb +42 -0
  34. data/lib/case_form/element/inputs/search_input.rb +32 -0
  35. data/lib/case_form/element/inputs/string_input.rb +18 -0
  36. data/lib/case_form/element/inputs/text_input.rb +19 -0
  37. data/lib/case_form/element/label.rb +52 -0
  38. data/lib/case_form/element/nested_model.rb +105 -0
  39. data/lib/case_form/element/nested_models/handle.rb +18 -0
  40. data/lib/case_form/element/nested_models/handles/destructor_handle.rb +47 -0
  41. data/lib/case_form/element/nested_models/handles/generator_handle.rb +55 -0
  42. data/lib/case_form/element_ext/associationable.rb +54 -0
  43. data/lib/case_form/element_ext/columnable.rb +21 -0
  44. data/lib/case_form/element_ext/naming.rb +17 -0
  45. data/lib/case_form/element_ext/validationable.rb +13 -0
  46. data/lib/case_form/errors.rb +189 -0
  47. data/lib/case_form/form_builder.rb +11 -0
  48. data/lib/case_form/inputs.rb +1095 -0
  49. data/lib/case_form/labels.rb +102 -0
  50. data/lib/case_form/version.rb +6 -0
  51. data/lib/generators/case_form/install_generator.rb +33 -0
  52. data/lib/generators/case_form/templates/case_form.rb +63 -0
  53. data/lib/generators/case_form/templates/javascripts/jquery.case_form.js +10 -0
  54. data/lib/generators/case_form/templates/javascripts/prototype.case_form.js +0 -0
  55. data/lib/generators/case_form/templates/locales/en.yml +28 -0
  56. data/lib/generators/case_form/templates/locales/pl.yml +28 -0
  57. data/lib/generators/case_form/templates/stylesheets/stylesheet.css +93 -0
  58. data/lib/generators/case_form/templates/stylesheets/stylesheet_changes.css +1 -0
  59. data/lib/generators/case_form/uninstall_generator.rb +30 -0
  60. data/rails/init.rb +1 -0
  61. data/test/element/button_test.rb +85 -0
  62. data/test/element/errors/complex_error_test.rb +140 -0
  63. data/test/element/errors/simple_error_test.rb +92 -0
  64. data/test/element/fieldset_test.rb +28 -0
  65. data/test/element/hint_test.rb +81 -0
  66. data/test/element/input_test.rb +197 -0
  67. data/test/element/inputs/collection/checkbox_input_test.rb +176 -0
  68. data/test/element/inputs/collection/radio_input_test.rb +156 -0
  69. data/test/element/inputs/collection/select_input_test.rb +152 -0
  70. data/test/element/inputs/datetime/date_input_test.rb +160 -0
  71. data/test/element/inputs/datetime/datetime_input_test.rb +227 -0
  72. data/test/element/inputs/datetime/time_input_test.rb +72 -0
  73. data/test/element/inputs/datetime/time_zone_input_test.rb +42 -0
  74. data/test/element/inputs/file_input_test.rb +13 -0
  75. data/test/element/inputs/hidden_input_test.rb +13 -0
  76. data/test/element/inputs/number_input_test.rb +50 -0
  77. data/test/element/inputs/search_input_test.rb +13 -0
  78. data/test/element/inputs/string_input_test.rb +33 -0
  79. data/test/element/inputs/text_input_test.rb +13 -0
  80. data/test/element/label_test.rb +62 -0
  81. data/test/element/nested_model_test.rb +163 -0
  82. data/test/element/nested_models/handles/destructor_handle_test.rb +35 -0
  83. data/test/element/nested_models/handles/generator_handle_test.rb +27 -0
  84. data/test/form_builder_test.rb +25 -0
  85. data/test/form_helper_test.rb +15 -0
  86. data/test/lib/models.rb +268 -0
  87. data/test/test_helper.rb +74 -0
  88. metadata +235 -0
@@ -0,0 +1,18 @@
1
+ # coding: utf-8
2
+ module CaseForm
3
+ module Element
4
+ class Handle < Base
5
+ self.allowed_options << [:text]
6
+
7
+ private
8
+ def default_options
9
+ super
10
+ options[:custom] = {}
11
+ end
12
+
13
+ def text
14
+ options[:text] || translated_text
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,47 @@
1
+ # coding: utf-8
2
+ module CaseForm
3
+ module Element
4
+ class DestructorHandle < Handle
5
+ self.allowed_options << [:as]
6
+
7
+ def generate
8
+ send("destroy_#{handle_type}")
9
+ end
10
+
11
+ private
12
+ def default_options
13
+ super
14
+ options[:as] ||= :checkbox
15
+ options[:custom][:action] = :destroy if link?
16
+ end
17
+
18
+ def destroy_link
19
+ builder.hidden_field(:_destroy) + template.link_to(text, "javascript:void(0)", html_options)
20
+ end
21
+
22
+ def destroy_checkbox
23
+ builder.checkbox(:_destroy, :label => text)
24
+ end
25
+
26
+ def translated_text
27
+ I18n.t(:"case_form.nested_attributes.destroy", :model => object_human_model_name, :default => default_text)
28
+ end
29
+
30
+ def default_text
31
+ "Destroy #{object_human_model_name}"
32
+ end
33
+
34
+ def handle_type
35
+ options[:as]
36
+ end
37
+
38
+ def link?
39
+ handle_type == :link
40
+ end
41
+
42
+ def checkbox?
43
+ handle_type == :checkbox
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,55 @@
1
+ # coding: utf-8
2
+ module CaseForm
3
+ module Element
4
+ class GeneratorHandle < Handle
5
+ include ElementExt::Associationable
6
+
7
+ self.allowed_options << [:fields]
8
+
9
+ attr_accessor :method
10
+
11
+ def initialize(builder, method, options={})
12
+ @method = method
13
+ super(builder, options)
14
+ end
15
+
16
+ def generate
17
+ Element::Fieldset.new(builder, :class => "new_#{method}_association").generate(new_nested_model + new_link)
18
+ end
19
+
20
+ private
21
+ def default_options
22
+ super
23
+ options[:custom][:association] = method
24
+ options[:custom][:action] = :new
25
+ options[:fields] ||= default_fields
26
+ end
27
+
28
+ def new_nested_model
29
+ template.content_tag(:div, nil, :class => "#{method}_association_inputs") do
30
+ if options[:fields].is_a?(Proc)
31
+ builder.case_fields_for(method, association_class.new, &block)
32
+ else
33
+ builder.case_fields_for(method, association_class.new) { |b| template.concat(b.attributes(*options[:fields])) }
34
+ end
35
+ end
36
+ end
37
+
38
+ def new_link
39
+ template.link_to(text, "javascript:void(0)", html_options)
40
+ end
41
+
42
+ def translated_text
43
+ I18n.t(:"case_form.nested_attributes.new", :model => association_human_model_name, :default => default_text)
44
+ end
45
+
46
+ def default_text
47
+ "New #{association_human_model_name}"
48
+ end
49
+
50
+ def default_fields
51
+ association_class.content_columns.map(&:name).map(&:to_sym) - CaseForm.locked_columns
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,54 @@
1
+ module CaseForm
2
+ module ElementExt
3
+ module Associationable
4
+ def associationable?
5
+ object.class.respond_to?(:reflect_on_association)
6
+ end
7
+
8
+ def association
9
+ object.class.reflect_on_association(method) if associationable?
10
+ end
11
+
12
+ def association_class
13
+ association.klass if association
14
+ end
15
+
16
+ def association_type?(type)
17
+ association and association.macro == type
18
+ end
19
+
20
+ def association_method
21
+ case association.macro
22
+ when :belongs_to
23
+ :"#{method.to_s}_id"
24
+ when :has_one
25
+ method
26
+ when :has_many
27
+ :"#{method.to_s.singularize}_ids"
28
+ end
29
+ end
30
+
31
+ def specific_method
32
+ association ? association_method : method
33
+ end
34
+
35
+ def validate_nested_attributes_association(method, object)
36
+ raise(NoMethodError, "Not defined :accepts_nested_attributes_for method " +
37
+ "for :#{method} association in #{object.class} model!") unless object.respond_to?(:"#{method}_attributes=")
38
+ method
39
+ end
40
+
41
+ def association_human_model_name
42
+ association_class.human_name
43
+ end
44
+
45
+ def one_to_one_association?
46
+ association_type?(:belongs_to) || association_type?(:has_one)
47
+ end
48
+
49
+ def collection_association?
50
+ association_type?(:has_many)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,21 @@
1
+ module CaseForm
2
+ module ElementExt
3
+ module Columnable
4
+ def columnable?
5
+ object.class.columns.map(&:name).include?(method.to_sym)
6
+ end
7
+
8
+ def object_column
9
+ object.column_for_attribute(method) if columnable?
10
+ end
11
+
12
+ def object_column_type?(type)
13
+ object_column and object_column.type == type
14
+ end
15
+
16
+ def specific_method
17
+ method
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,17 @@
1
+ module CaseForm
2
+ module ElementExt
3
+ module Naming
4
+ def sanitized_object_name
5
+ object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
6
+ end
7
+
8
+ def singularize_model_name
9
+ method.to_s.singularize.downcase
10
+ end
11
+
12
+ def object_human_model_name
13
+ object.class.human_name
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,13 @@
1
+ module CaseForm
2
+ module ElementExt
3
+ module Validationable
4
+ def validationable?
5
+ object.class.respond_to?(:validators_on)
6
+ end
7
+
8
+ def method_validations
9
+ object.class.validators_on(method)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,189 @@
1
+ module CaseForm
2
+ module Errors
3
+ # == Error messages
4
+ #
5
+ # Creates a list with all errors on object (same as *error_messages_for* in Rails2).
6
+ # Available is full configuration of every element in error list.
7
+ # Elements of list can be seperated for each error (*list* type) or grouped by attribute
8
+ # (*sentence* type). Type of elements list should be defined in CaseForm config or options.
9
+ #
10
+ # == List type
11
+ #
12
+ # # object.errors = { :name => ['can't be blank', 'should be uniq'],
13
+ # :price => 'should be more than 10' }
14
+ #
15
+ # # errors:
16
+ # Name can't be blank
17
+ # Name should be uniq
18
+ # Price should be more than 10
19
+ #
20
+ # == Sentence type
21
+ #
22
+ # # object.errors = { :name => ['can't be blank', 'should be uniq'],
23
+ # :price => 'should be more than 10' }
24
+ #
25
+ # # errors:
26
+ # Name can't be blank and should be uniq
27
+ # Price should be more than 10
28
+ #
29
+ # == Error messages elements
30
+ #
31
+ # * main container
32
+ # * header message - excluded if +:header_message+ is *false*
33
+ # * message - excluded if +:message+ is *false*
34
+ # * list of errors
35
+ #
36
+ # == Default error messages config
37
+ #
38
+ # * CaseForm.error_tag
39
+ # * CaseForm.error_type
40
+ # * CaseForm.error_connector
41
+ # * CaseForm.last_error_connector
42
+ # * CaseForm.complex_error_header_tag
43
+ # * CaseForm.complex_error_message_tag
44
+ #
45
+ # == Examples:
46
+ #
47
+ # # @user.errors = { :firstname => ["can't be blank", "should be uniq"],
48
+ # :lastname => "can't be blank" }
49
+ #
50
+ # # Generate list of errors as list
51
+ #
52
+ # <%= case_form_for(@user) do |f| %>
53
+ # <%= f.errors
54
+ # <% end %>
55
+ #
56
+ # <div class="errors" id="user_errors">
57
+ # <h2>Some errors prohibited this object from being saved</h2>
58
+ # <p>There were problems with the following fields:</p>
59
+ # <ul>
60
+ # <li>Firstname can't be blank</li>
61
+ # <li>Firstname should be uniq</li>
62
+ # <li>Lastname can't be blank</li>
63
+ # </ul>
64
+ # </div>
65
+ #
66
+ # # Generate list of errors as sentence
67
+ #
68
+ # <%= case_form_for(@user) do |f| %>
69
+ # <%= f.errors
70
+ # <% end %>
71
+ #
72
+ # <div class="errors" id="user_errors">
73
+ # <h2>Some errors prohibited this object from being saved</h2>
74
+ # <p>There were problems with the following fields:</p>
75
+ # <ul>
76
+ # <li>Firstname can't be blank and should be uniq</li>
77
+ # <li>Lastname can't be blank</li>
78
+ # </ul>
79
+ # </div>
80
+ #
81
+ # == Allowed options:
82
+ # * +:id+ - HTML ID
83
+ # * +:class+ - HTML class
84
+ # * +:style+ - not recommended HTML styles (use CSS)
85
+ # * +:tag+ - container tag, overwrite CaseForm config
86
+ # * +:as+ - list type, overwrite CaseForm config (supported values: +:list+ and +:sentence+)
87
+ # * +:header_tag+ - header tag
88
+ # * +:header_message+ - header message
89
+ # * +:message_tag+ - message tag
90
+ # * +:message+ - message
91
+ # * +:connector+ - connector for sentence, default is ', '
92
+ # * +:last_connector+ - last connector for sentence, default is ' and '
93
+ #
94
+ # == Header message I18n lookups priority:
95
+ #
96
+ # * "activemodel.errors.template.header_message"
97
+ # * "activerecord.errors.template.header_message"
98
+ # * "case_form.errors.template.header_message"
99
+ # * "Some errors prohibited this object from being saved"
100
+ #
101
+ # == Message I18n lookups priority:
102
+ #
103
+ # * "activemodel.errors.template.message"
104
+ # * "activerecord.errors.template.message"
105
+ # * "case_form.errors.template.message"
106
+ # * "There were problems with the following fields:"
107
+ #
108
+ def error_messages(options={})
109
+ Element::ComplexError.new(self, options).generate if object.errors.any?
110
+ end
111
+ alias_method :error_messages_for, :error_messages
112
+ alias_method :errors_for, :error_messages
113
+ alias_method :errors, :error_messages
114
+
115
+ # == Error messages on method
116
+ #
117
+ # Creates a list with all errors on method in object.
118
+ # Elements of list can be seperated for each error (*list* type) or grouped (*sentence* type).
119
+ # Type of elements list should be defined in CaseForm config or options.
120
+ #
121
+ # == List type
122
+ #
123
+ # # object.errors = { :name => ['can't be blank', 'should be uniq'],
124
+ # :price => 'should be more than 10' }
125
+ #
126
+ # # errors on firstname:
127
+ # can't be blank
128
+ # should be uniq
129
+ #
130
+ # == Sentence type
131
+ #
132
+ # # object.errors = { :name => ['can't be blank', 'should be uniq'],
133
+ # :price => 'should be more than 10' }
134
+ #
135
+ # # errors on firstname:
136
+ # can't be blank and should be uniq
137
+ #
138
+ # == Default error messages config
139
+ #
140
+ # * CaseForm.error_tag
141
+ # * CaseForm.error_type
142
+ # * CaseForm.error_connector
143
+ # * CaseForm.last_error_connector
144
+ #
145
+ # == Examples:
146
+ #
147
+ # # @user.errors = { :firstname => ["can't be blank", "should be uniq"],
148
+ # :lastname => "can't be blank" }
149
+ #
150
+ # # Generate list of errors as list
151
+ #
152
+ # <%= case_form_for(@user) do |f| %>
153
+ # <%= f.error :firstname
154
+ # <% end %>
155
+ #
156
+ # <div class="error" id="user_firstname_error">
157
+ # <ul>
158
+ # <li>can't be blank</li>
159
+ # <li>should be uniq</li>
160
+ # </ul>
161
+ # </div>
162
+ #
163
+ # # Generate list of errors as sentence
164
+ #
165
+ # <%= case_form_for(@user) do |f| %>
166
+ # <%= f.error :firstname
167
+ # <% end %>
168
+ #
169
+ # <div class="error" id="user_firstname_error">
170
+ # can't be blank and should be uniq
171
+ # </div>
172
+ #
173
+ # == Allowed options:
174
+ # * +:id+ - HTML ID
175
+ # * +:class+ - HTML class
176
+ # * +:style+ - not recommended HTML styles (use CSS)
177
+ # * +:tag+ - container tag, overwrite CaseForm config
178
+ # * +:as+ - list type, overwrite CaseForm config (supported values: +:list+ and +:sentence+)
179
+ # * +:connector+ - connector for sentence, default is ', '
180
+ # * +:last_connector+ - last connector for sentence, default is ' and '
181
+ #
182
+ def error_message(method, options={})
183
+ Element::SimpleError.new(self, method, options).generate if object.errors[method].any?
184
+ end
185
+ alias_method :error_message_on, :error_message
186
+ alias_method :error_on, :error_message
187
+ alias_method :error, :error_message
188
+ end
189
+ end
@@ -0,0 +1,11 @@
1
+ module CaseForm
2
+ class FormBuilder < ActionView::Helpers::FormBuilder
3
+ include CaseForm::Inputs
4
+ include CaseForm::Associations
5
+ include CaseForm::Buttons
6
+ include CaseForm::Labels
7
+ include CaseForm::Errors
8
+
9
+ attr_reader :template, :object_name, :object, :options
10
+ end
11
+ end
@@ -0,0 +1,1095 @@
1
+ # coding: utf-8
2
+ module CaseForm
3
+ module Inputs
4
+ # == Attributes with or without block
5
+ #
6
+ # Creates fieldset for inputs. With no block or arguments (except allowed options for fieldset)
7
+ # it generates for each model column (without locked columns) a specific input. Also can create
8
+ # only for specified attributes in model as list of arguments in method.
9
+ #
10
+ # Every input has specified type (support for HTML5).
11
+ #
12
+ # == Input elements:
13
+ #
14
+ # * label
15
+ # * input
16
+ # * hint - if hint exist
17
+ # * error - if method has errors
18
+ #
19
+ # To change list of elements (global) for each attribute go to CaseForm config.
20
+ #
21
+ # == Examples:
22
+ #
23
+ # # Short form with all model columns
24
+ # # (imagine that User.column_names = [:id, :firstname, :lastname, :email, :created_at, :updated_at])
25
+ #
26
+ # <&= case_form_for @user do |f| &>
27
+ # <%= f.attributes %> # Create inputs for all columns
28
+ # <% end %>
29
+ #
30
+ # <fieldset class="fieldset">
31
+ # <div class="inputs">
32
+ # <label class="label" for="user_firstname">Firstname</label>
33
+ # <input class="input" id="user_firstname" name="user[firstname]" type="text" />
34
+ # </div>
35
+ # <div class="inputs">
36
+ # <label class="label" for="user_lastname">Lastname</label>
37
+ # <input class="input" id="user_lastname" name="user[lastname]" type="text" />
38
+ # </div>
39
+ # <div class="inputs">
40
+ # <label class="label" for="user_email">Email</label>
41
+ # <input class="input" id="user_email" name="user[email]" type="email" />
42
+ # </div>
43
+ # </fieldset>
44
+ #
45
+ # # Form with specified attributes in arguments
46
+ #
47
+ # <&= case_form_for @user do |f| &>
48
+ # <%= f.attributes(:firstname, :lastname) %> # Create inputs for firstname and lastname
49
+ # <% end %>
50
+ #
51
+ # <fieldset class="fieldset">
52
+ # <div class="inputs">
53
+ # <label class="label" for="user_firstname">Firstname</label>
54
+ # <input class="input" id="user_firstname" name="user[firstname]" type="text" />
55
+ # </div>
56
+ # <div class="inputs">
57
+ # <label class="label" for="user_lastname">Lastname</label>
58
+ # <input class="input" id="user_lastname" name="user[lastname]" type="text" />
59
+ # </div>
60
+ # </fieldset>
61
+ #
62
+ # # Attributes in block
63
+ #
64
+ # <&= case_form_for @user do |f| &>
65
+ # <%= f.attributes do %>
66
+ # <%= f.string :firstname %>
67
+ # <%= f.email :email %>
68
+ # <% end %>
69
+ # <% end %>
70
+ #
71
+ # <fieldset class="fieldset">
72
+ # <div class="inputs">
73
+ # <label class="label" for="user_firstname">Firstname</label>
74
+ # <input class="input" id="user_firstname" name="user[firstname]" type="text" />
75
+ # </div>
76
+ # <div class="inputs">
77
+ # <label class="label" for="user_email">Email</label>
78
+ # <input class="input" id="user_email" name="user[email]" type="email" />
79
+ # </div>
80
+ # </fieldset>
81
+ #
82
+ # == Default attributes config
83
+ #
84
+ # * CaseForm.input_elements
85
+ # * CaseForm.locked_columns
86
+ #
87
+ # == Allowed options:
88
+ # * +:id+ - HTML ID
89
+ # * +:class+ - HTML class
90
+ # * +:style+ - not recommended HTML styles (use CSS)
91
+ # * +:text+ - legend for fieldset
92
+ # For more advanced options for each attribute use block.
93
+ #
94
+ def attributes(*args, &block)
95
+ options = args.extract_options!
96
+ options[:class] ||= :inputs
97
+
98
+ fieldset = Element::Fieldset.new(self, options)
99
+
100
+ if block_given?
101
+ fieldset.generate(&block)
102
+ else
103
+ args = object.class.content_columns.map(&:name) if args.empty?
104
+ args -= CaseForm.locked_columns.collect! { |c| c.to_s }
105
+ fieldset.generate(args.collect { |a| attribute(a) })
106
+ end
107
+ end
108
+ alias_method :inputs, :attributes
109
+
110
+ # == Attribute with dynamic type
111
+ #
112
+ # Creates a input with elements defined in CaseForm config or options for method.
113
+ # Supports association and simple methods, generating for them right type of input.
114
+ # Using option +:as+ can be specified the type yourself.
115
+ #
116
+ # == Input elements:
117
+ #
118
+ # * label
119
+ # * input
120
+ # * hint - if hint exist
121
+ # * error - if method has errors
122
+ #
123
+ #
124
+ # == Input types:
125
+ # * +:string+
126
+ # * +:text+
127
+ # * +:hidden+
128
+ # * +:password+
129
+ # * +:email+
130
+ # * +:url+
131
+ # * +:telephone+
132
+ # * +:search+
133
+ # * +:file+
134
+ # * +:datetime+
135
+ # * +:date+
136
+ # * +:time+
137
+ # * +:number+
138
+ # * +:range+
139
+ # * +:checkbox+
140
+ # * +:radio+
141
+ # * +:select+
142
+ # * +:association+
143
+ # * +:has_many+
144
+ # * +:has_one+
145
+ # * +:belongs_to+
146
+ #
147
+ # == Examples:
148
+ #
149
+ # <&= case_form_for @user do |f| &>
150
+ # <%= f.string :firstname %>
151
+ # <%= f.email :email %>
152
+ # <% end %>
153
+ #
154
+ # <div class="inputs">
155
+ # <label class="label" for="user_firstname">Firstname</label>
156
+ # <input class="input" id="user_firstname" name="user[firstname]" type="text" />
157
+ # </div>
158
+ # <div class="inputs">
159
+ # <label class="label" for="user_email">Email</label>
160
+ # <input class="input" id="user_email" name="user[email]" type="email" /> # email type
161
+ # </div>
162
+ #
163
+ # # with own input type
164
+ #
165
+ # <&= case_form_for @user do |f| &>
166
+ # <%= f.email :email, :as => :string %>
167
+ # <% end %>
168
+ #
169
+ # <div class="inputs">
170
+ # <label class="label" for="user_email">Email</label>
171
+ # <input class="input" id="user_email" name="user[email]" type="text" /> # text type
172
+ # </div>
173
+ #
174
+ # # without some element (for example label)
175
+ #
176
+ # <&= case_form_for @user do |f| &>
177
+ # <%= f.email :email, :label => false %>
178
+ # <% end %>
179
+ #
180
+ # <div class="inputs">
181
+ # <input class="input" id="user_email" name="user[email]" type="email" />
182
+ # </div>
183
+ #
184
+ # # with label/hint options as hash
185
+ #
186
+ # <&= case_form_for @user do |f| &>
187
+ # <%= f.email :email, :label => { :text => "E-mail", :id => "email_label"} %>
188
+ # <% end %>
189
+ #
190
+ # <div class="inputs">
191
+ # <label class="label" for="user_email" id="email_label">E-mail</label>
192
+ # <input class="input" id="user_email" name="user[email]" type="email" />
193
+ # </div>
194
+ #
195
+ # # with text label/hint
196
+ #
197
+ # <&= case_form_for @user do |f| &>
198
+ # <%= f.email :email, :hint => "enter your e-mail" %>
199
+ # <% end %>
200
+ #
201
+ # <div class="inputs">
202
+ # <label class="label" for="user_email">Email</label>
203
+ # <input class="input" id="user_email" name="user[email]" type="email" />
204
+ # <span>enter your e-mail</span>
205
+ # </div>
206
+ #
207
+ # == Default attribute config:
208
+ #
209
+ # * CaseForm.input_types
210
+ # * CaseForm.input_limit
211
+ #
212
+ # == Allowed options:
213
+ # * +:id+ - HTML ID
214
+ # * +:class+ - HTML class
215
+ # * +:style+ - not recommended HTML styles (use CSS)
216
+ # * +:autofocus+ - HTML autofocus
217
+ # * +:required+ - overwrite required field
218
+ # * +:label+ - label options (*false* to not create)
219
+ # * +:hint+ - hint options (*false* to not create)
220
+ # * +:error+ - error options (*false* to not create)
221
+ # * and much more for each input type
222
+ #
223
+ def attribute(method, options={})
224
+ if options.has_key?(:as)
225
+ type, input_types = options.delete(:as), CaseForm.input_types
226
+ raise(ArgumentError, "Unknown input type: #{type}. Available types: #{input_types.join(', ')}") unless input_types.include?(type.to_sym)
227
+ send(type, method, options)
228
+ elsif object.class.reflect_on_association(method)
229
+ association(method, options)
230
+ else
231
+ specified_input(method, options)
232
+ end
233
+ end
234
+ alias_method :input, :attribute
235
+
236
+ # == Text field
237
+ #
238
+ # Creates text field with defined input elements. HTML input attribute *size* is defined from
239
+ # +validates_length_of+ and option +:maximum+ validation or from CaseForm config.
240
+ #
241
+ # == Example:
242
+ #
243
+ # case_form_for(@user) do |f|
244
+ # f.string(:firstname)
245
+ # end
246
+ #
247
+ # <div class="inputs">
248
+ # <label class="label" for="user_firstname" id="user_firstname_label">Firstame</label>
249
+ # <input class="input" id="user_firstname" name="user[firstname]" size="50" type="text" value="" />
250
+ # </div>
251
+ #
252
+ # == Default text field config:
253
+ #
254
+ # * CaseForm.input_size
255
+ #
256
+ # == Allowed options:
257
+ # * +:id+ - HTML ID
258
+ # * +:class+ - HTML class
259
+ # * +:style+ - not recommended HTML styles (use CSS)
260
+ # * +:autofocus+ - HTML autofocus
261
+ # * +:required+ - overwrite required field
262
+ # * +:label+ - label options (*false* to not create)
263
+ # * +:hint+ - hint options (*false* to not create)
264
+ # * +:error+ - error options (*false* to not create)
265
+ # * +:size+ - input size (default value in CaseForm config)
266
+ # * +:maxlength+ - max lenght of text
267
+ # * +:placeholder+ - HTML placeholder (HTML5)
268
+ # * +:pattern+ - Javascript pattern of text (HTML5)
269
+ # * +:readonly+ - read-only input
270
+ # * +:disabled+ - disable input
271
+ #
272
+ def string(method, options={})
273
+ Element::StringInput.new(self, method, options.merge(:as => :text)).generate
274
+ end
275
+
276
+ # == Text area
277
+ #
278
+ # Creates text area with defined input elements.
279
+ #
280
+ # == Example:
281
+ #
282
+ # case_form_for(@user) do |f|
283
+ # f.text(:firstname)
284
+ # end
285
+ #
286
+ # <div class="inputs">
287
+ # <label class="label" for="user_firstname" id="user_firstname_label">Firstname</label>
288
+ # <textarea class="input" cols="40" id="user_firstname" name="user[firstname]" rows="20"></textarea>
289
+ # </div>
290
+ #
291
+ # == Default attribute config:
292
+ #
293
+ # * CaseForm.textarea_cols
294
+ # * CaseForm.textarea_rows
295
+ #
296
+ # == Allowed options:
297
+ # * +:id+ - HTML ID
298
+ # * +:class+ - HTML class
299
+ # * +:style+ - not recommended HTML styles (use CSS)
300
+ # * +:autofocus+ - HTML autofocus
301
+ # * +:required+ - overwrite required field
302
+ # * +:label+ - label options (*false* to not create)
303
+ # * +:hint+ - hint options (*false* to not create)
304
+ # * +:error+ - error options (*false* to not create)
305
+ # * +:cols+ - textarea cols (default value in CaseForm config)
306
+ # * +:rows+ - textarea rows (default value in CaseForm config)
307
+ # * +:maxlength+ - max lenght of text
308
+ # * +:placeholder+ - HTML placeholder (HTML5)
309
+ # * +:readonly+ - read-only input
310
+ # * +:disabled+ - disable input
311
+ #
312
+ def text(method, options={})
313
+ Element::TextInput.new(self, method, options).generate
314
+ end
315
+
316
+ # == Hidden field
317
+ #
318
+ # Generates hidden field without any elements.
319
+ #
320
+ # == Example:
321
+ #
322
+ # case_form_for(@user) do |f|
323
+ # f.hidden(:firstname)
324
+ # end
325
+ #
326
+ # <input class="input hidden" id="user_firstname" name="user[firstname]" type="hidden" value="" />
327
+ #
328
+ # == Allowed options:
329
+ # * +:id+ - HTML ID
330
+ # * +:class+ - HTML class
331
+ # * +:style+ - not recommended HTML styles (use CSS)
332
+ #
333
+ def hidden(method, options={})
334
+ Element::HiddenInput.new(self, method, options).generate
335
+ end
336
+
337
+ # == Password field
338
+ #
339
+ # Generate password field with defined input elements.
340
+ #
341
+ # == Example:
342
+ #
343
+ # case_form_for(@user) do |f|
344
+ # f.password(:password)
345
+ # end
346
+ #
347
+ # <div class="inputs">
348
+ # <label class="label" for="user_password" id="user_password_label">Password</label>
349
+ # <input class="input" id="user_password" name="user[password]" size="50" type="password" value="" />
350
+ # </div>
351
+ #
352
+ # == Allowed options:
353
+ # * +:id+ - HTML ID
354
+ # * +:class+ - HTML class
355
+ # * +:style+ - not recommended HTML styles (use CSS)
356
+ # * +:autofocus+ - HTML autofocus
357
+ # * +:required+ - overwrite required field
358
+ # * +:label+ - label options (*false* to not create)
359
+ # * +:hint+ - hint options (*false* to not create)
360
+ # * +:error+ - error options (*false* to not create)
361
+ # * +:size+ - input size (default value in CaseForm config)
362
+ # * +:maxlength+ - max lenght of text
363
+ # * +:placeholder+ - HTML placeholder (HTML5)
364
+ # * +:pattern+ - Javascript pattern of text (HTML5)
365
+ # * +:readonly+ - read-only input
366
+ # * +:disabled+ - disable input
367
+ #
368
+ def password(method = :password, options={})
369
+ Element::StringInput.new(self, method, options.merge(:as => :password)).generate
370
+ end
371
+
372
+ # == Search field
373
+ #
374
+ # Generate search field with label (without hint and error).
375
+ #
376
+ # == Example:
377
+ #
378
+ # case_form_for(@user) do |f|
379
+ # f.search(:firstname)
380
+ # end
381
+ #
382
+ # <div class="inputs search">
383
+ # <label class="label" for="user_firstname" id="user_firstname_label">Firstname</label>
384
+ # <input class="input" id="user_firstname" name="user[firstname]" size="50" type="search" value="" />
385
+ # </div>
386
+ #
387
+ # == Allowed options:
388
+ # * +:id+ - HTML ID
389
+ # * +:class+ - HTML class
390
+ # * +:style+ - not recommended HTML styles (use CSS)
391
+ # * +:autofocus+ - HTML autofocus
392
+ # * +:required+ - overwrite required field
393
+ # * +:label+ - label options (*false* to not create)
394
+ #
395
+ def search(method, options={})
396
+ Element::StringInput.new(self, method, options.merge(:as => :search)).generate
397
+ end
398
+
399
+ # == Email field
400
+ #
401
+ # Generate email field with defined input elements.
402
+ #
403
+ # == Example:
404
+ #
405
+ # case_form_for(@user) do |f|
406
+ # f.email(:email)
407
+ # end
408
+ #
409
+ # <div class="inputs">
410
+ # <label class="label" for="user_email" id="user_email_label">Email</label>
411
+ # <input class="input" id="user_email" name="user[email]" size="50" type="email" value="" />
412
+ # </div>
413
+ #
414
+ # == Allowed options:
415
+ # * +:id+ - HTML ID
416
+ # * +:class+ - HTML class
417
+ # * +:style+ - not recommended HTML styles (use CSS)
418
+ # * +:autofocus+ - HTML autofocus
419
+ # * +:required+ - overwrite required field
420
+ # * +:label+ - label options (*false* to not create)
421
+ # * +:hint+ - hint options (*false* to not create)
422
+ # * +:error+ - error options (*false* to not create)
423
+ # * +:size+ - input size (default value in CaseForm config)
424
+ # * +:maxlength+ - max lenght of text
425
+ # * +:placeholder+ - HTML placeholder (HTML5)
426
+ # * +:pattern+ - Javascript pattern of text (HTML5)
427
+ # * +:readonly+ - read-only input
428
+ # * +:disabled+ - disable input
429
+ #
430
+ def email(method = :email, options={})
431
+ Element::StringInput.new(self, method, options.merge(:as => :email)).generate
432
+ end
433
+ alias_method :mail, :email
434
+
435
+ # == URL field
436
+ #
437
+ # Generate URL field with defined input elements.
438
+ #
439
+ # == Example:
440
+ #
441
+ # case_form_for(@user) do |f|
442
+ # f.url(:profile_url)
443
+ # end
444
+ #
445
+ # <div class="inputs">
446
+ # <label class="label" for="user_profile_url" id="user_profile_url_label">Profile url</label>
447
+ # <input class="input" id="user_profile_url" name="user[profile_url]" size="50" type="url" value="" />
448
+ # </div>
449
+ #
450
+ # == Allowed options:
451
+ # * +:id+ - HTML ID
452
+ # * +:class+ - HTML class
453
+ # * +:style+ - not recommended HTML styles (use CSS)
454
+ # * +:autofocus+ - HTML autofocus
455
+ # * +:required+ - overwrite required field
456
+ # * +:label+ - label options (*false* to not create)
457
+ # * +:hint+ - hint options (*false* to not create)
458
+ # * +:error+ - error options (*false* to not create)
459
+ # * +:size+ - input size (default value in CaseForm config)
460
+ # * +:maxlength+ - max lenght of text
461
+ # * +:placeholder+ - HTML placeholder (HTML5)
462
+ # * +:pattern+ - Javascript pattern of text (HTML5)
463
+ # * +:readonly+ - read-only input
464
+ # * +:disabled+ - disable input
465
+ #
466
+ def url(method = :url, options={})
467
+ Element::StringInput.new(self, method, options.merge(:as => :url)).generate
468
+ end
469
+ alias_method :http, :url
470
+
471
+ # == Telephone field
472
+ #
473
+ # Generate telephone field with defined input elements.
474
+ #
475
+ # == Example:
476
+ #
477
+ # case_form_for(@user) do |f|
478
+ # f.telephone(:telephone)
479
+ # end
480
+ #
481
+ # <div class="inputs">
482
+ # <label class="label" for="user_telephone" id="user_telephone_label">Telephone</label>
483
+ # <input class="input" id="user_telephone" name="user[telephone]" size="50" type="tel" value="" />
484
+ # </div>
485
+ #
486
+ # == Allowed options:
487
+ # * +:id+ - HTML ID
488
+ # * +:class+ - HTML class
489
+ # * +:style+ - not recommended HTML styles (use CSS)
490
+ # * +:autofocus+ - HTML autofocus
491
+ # * +:required+ - overwrite required field
492
+ # * +:label+ - label options (*false* to not create)
493
+ # * +:hint+ - hint options (*false* to not create)
494
+ # * +:error+ - error options (*false* to not create)
495
+ # * +:size+ - input size (default value in CaseForm config)
496
+ # * +:maxlength+ - max lenght of text
497
+ # * +:placeholder+ - HTML placeholder (HTML5)
498
+ # * +:pattern+ - Javascript pattern of text (HTML5)
499
+ # * +:readonly+ - read-only input
500
+ # * +:disabled+ - disable input
501
+ #
502
+ def telephone(method = :telephone, options={})
503
+ Element::StringInput.new(self, method, options.merge(:as => :telephone)).generate
504
+ end
505
+ alias_method :tel, :telephone
506
+ alias_method :phone, :telephone
507
+
508
+ # == Datetime field
509
+ #
510
+ # Generate datetime field with defined input elements.
511
+ #
512
+ # == Example:
513
+ #
514
+ # case_form_for(@user) do |f|
515
+ # f.datetime(:created_at)
516
+ # end
517
+ #
518
+ # <div class="inputs">
519
+ # <label class="label" for="user_created_at" id="user_created_at_label">Created at</label>
520
+ # <select class="input" id="user_created_at_1i" name="user[created_at(1i)]">
521
+ # <option>...</option> # Options for year
522
+ # <select>
523
+ # <select class="input" id="user_created_at_2i" name="user[created_at(2i)]">
524
+ # <option>...</option> # Options for month
525
+ # </select>
526
+ # <select class="input" id="user_created_at_3i" name="user[created_at(3i)]">
527
+ # <option>...</option> # Options for day
528
+ # </select>
529
+ # <select class="input" id="user_created_at_4i" name="user[created_at(4i)]">
530
+ # <option>...</option> # Options for hour
531
+ # </select>
532
+ # <select class="input" id="user_created_at_5i" name="user[created_at(5i)]">
533
+ # <option>...</option> # Options for minute
534
+ # </select>
535
+ # </div>
536
+ #
537
+ # # only year and month select
538
+ #
539
+ # case_form_for(@user) do |f|
540
+ # f.datetime(:created_at, :elements => [:year, :month])
541
+ # end
542
+ #
543
+ # <div class="inputs">
544
+ # <label class="label" for="user_created_at" id="user_created_at_label">Created at</label>
545
+ # <select class="input" id="user_created_at_1i" name="user[created_at(1i)]">
546
+ # <option>...</option> # Options for year
547
+ # <select>
548
+ # <select class="input" id="user_created_at_2i" name="user[created_at(2i)]">
549
+ # <option>...</option> # Options for month
550
+ # </select>
551
+ # </div>
552
+ #
553
+ # # without minute select
554
+ #
555
+ # case_form_for(@user) do |f|
556
+ # f.datetime(:created_at, :minute => false)
557
+ # end
558
+ #
559
+ # <div class="inputs">
560
+ # <label class="label" for="user_created_at" id="user_created_at_label">Created at</label>
561
+ # <select class="input" id="user_created_at_1i" name="user[created_at(1i)]">
562
+ # <option>...</option> # Options for year
563
+ # <select>
564
+ # <select class="input" id="user_created_at_2i" name="user[created_at(2i)]">
565
+ # <option>...</option> # Options for month
566
+ # </select>
567
+ # <select class="input" id="user_created_at_3i" name="user[created_at(3i)]">
568
+ # <option>...</option> # Options for day
569
+ # </select>
570
+ # <select class="input" id="user_created_at_4i" name="user[created_at(4i)]">
571
+ # <option>...</option> # Options for hour
572
+ # </select>
573
+ # </div>
574
+ #
575
+ # == Allowed options:
576
+ # * +:id+ - HTML ID
577
+ # * +:class+ - HTML class
578
+ # * +:style+ - not recommended HTML styles (use CSS)
579
+ # * +:autofocus+ - HTML autofocus
580
+ # * +:required+ - overwrite required field
581
+ # * +:label+ - label options (*false* to not create)
582
+ # * +:hint+ - hint options (*false* to not create)
583
+ # * +:error+ - error options (*false* to not create)
584
+ # * +:elements+ - elements of datetime field (available: +:year+, +:month+, +:day+, +:hour+, +:minute+, +:second+)
585
+ # * +:datetime_separator+ - separator for each datetime select
586
+ # * +:separator+ - alias for +:datetime_separator+
587
+ # * +:date_separator+ - separator for each date select
588
+ # * +:time_separator+ - separator for each time select
589
+ # * +:year+ - year select (available options: +:start+ as start year, +:end+ as end year or *false* to not create)
590
+ # * +:start_year+ - overwrite <tt>:year => { :start => ... }</tt> option
591
+ # * +:end_year+ - overwrite <tt>:year => { :end => ... }</tt> option
592
+ # * +:month+ - month select (available options: +:names+ as month names, +:short+ to use short names or *false* to not create)
593
+ # * +:short_month+ - overwrite <tt>:month => { :short => ... }</tt> option
594
+ # * +:day+ - day select (*false* to not create)
595
+ # * +:hour+ - hour select (*false* to not create)
596
+ # * +:minute+ - minute select (available options: +:step+ or *false* to not create)
597
+ # * +:minute_step+ - overwrite <tt>:minute => { :step => ...}</tt> option
598
+ # * +:second+ - hour select (default *false* and not create)
599
+ # * +:datetime+ - selected datetime
600
+ # * +:default+ - alias for +:datetime+ option
601
+ # * +:prompt+ - custom messages for each select (available options: +:year+, +:month+ ... or *true* for auto)
602
+ # * +:blank+ - include blank option for each select
603
+ # * +:placeholder+ - HTML placeholder (HTML5)
604
+ # * +:readonly+ - read-only input
605
+ # * +:disabled+ - disable input
606
+ #
607
+ def datetime(method, options={})
608
+ Element::DateTimeInput.new(self, method, options).generate
609
+ end
610
+
611
+ # == Date field
612
+ #
613
+ # Generate date field with defined input elements.
614
+ #
615
+ # == Example:
616
+ #
617
+ # case_form_for(@user) do |f|
618
+ # f.date(:born_at)
619
+ # end
620
+ #
621
+ # <div class="inputs">
622
+ # <label class="label" for="user_born_at" id="user_born_at_label">Born at</label>
623
+ # <select class="input" id="user_born_at_1i" name="user[born_at(1i)]">
624
+ # <option>...</option> # Options for year
625
+ # <select>
626
+ # <select class="input" id="user_born_at_2i" name="user[born_at(2i)]">
627
+ # <option>...</option> # Options for month
628
+ # </select>
629
+ # <select class="input" id="user_born_at_3i" name="user[born_at(3i)]">
630
+ # <option>...</option> # Options for day
631
+ # </select>
632
+ # </div>
633
+ #
634
+ # # without day
635
+ #
636
+ # case_form_for(@user) do |f|
637
+ # f.date(:born_at, :day => false)
638
+ # end
639
+ #
640
+ # <div class="inputs">
641
+ # <label class="label" for="user_born_at" id="user_born_at_label">Born at</label>
642
+ # <select class="input" id="user_born_at_1i" name="user[born_at(1i)]">
643
+ # <option>...</option> # Options for year
644
+ # <select>
645
+ # <select class="input" id="user_born_at_2i" name="user[born_at(2i)]">
646
+ # <option>...</option> # Options for month
647
+ # </select>
648
+ # </div>
649
+ #
650
+ # == Allowed options:
651
+ # * +:id+ - HTML ID
652
+ # * +:class+ - HTML class
653
+ # * +:style+ - not recommended HTML styles (use CSS)
654
+ # * +:autofocus+ - HTML autofocus
655
+ # * +:required+ - overwrite required field
656
+ # * +:label+ - label options (*false* to not create)
657
+ # * +:hint+ - hint options (*false* to not create)
658
+ # * +:error+ - error options (*false* to not create)
659
+ # * +:elements+ - elements of date field (available: +:year+, +:month+, +:day+)
660
+ # * +:separator+ - date separator (default: " - ")
661
+ # * +:year+ - year select (available options: +:start+ as start year, +:end+ as end year or *false* to not create)
662
+ # * +:start_year+ - overwrite <tt>:year => { :start => ... }</tt> option
663
+ # * +:end_year+ - overwrite <tt>:year => { :end => ... }</tt> option
664
+ # * +:month+ - month select (available options: +:names+ as month names, +:short+ to use short names or *false* to not create)
665
+ # * +:short_month+ - overwrite <tt>:month => { :short => ... }</tt> option
666
+ # * +:day+ - day select (*false* to not create)
667
+ # * +:date+ - selected date
668
+ # * +:default+ - alias for +:date+ option
669
+ # * +:prompt+ - custom messages for each select (available options: +:year+, +:month+ and +:day+ or *true* for auto)
670
+ # * +:blank+ - include blank option for each select
671
+ # * +:placeholder+ - HTML placeholder (HTML5)
672
+ # * +:readonly+ - read-only input
673
+ # * +:disabled+ - disable input
674
+ #
675
+ def date(method, options={})
676
+ Element::DateInput.new(self, method, options).generate
677
+ end
678
+
679
+ # == Time field
680
+ #
681
+ # Generate time field with defined input elements.
682
+ #
683
+ # == Example:
684
+ #
685
+ # case_form_for(@user) do |f|
686
+ # f.time(:login_at)
687
+ # end
688
+ #
689
+ # <div class="inputs">
690
+ # <label class="label" for="user_login_at" id="user_login_at_label">Login at</label>
691
+ # <select class="input" id="user_login_at_4i" name="user[login_at(4i)]">
692
+ # <option>...</option> # Options for hour
693
+ # <select>
694
+ # <select class="input" id="user_login_at_5i" name="user[login_at(5i)]">
695
+ # <option>...</option> # Options for minute
696
+ # </select>
697
+ # </div>
698
+ #
699
+ # # with second
700
+ #
701
+ # case_form_for(@user) do |f|
702
+ # f.date(:born_at, :elements => [:hour, :minute, :second])
703
+ # end
704
+ #
705
+ # <div class="inputs">
706
+ # <label class="label" for="user_login_at" id="user_login_at_label">Login at</label>
707
+ # <select class="input" id="user_login_at_4i" name="user[login_at(4i)]">
708
+ # <option>...</option> # Options for hour
709
+ # <select>
710
+ # <select class="input" id="user_login_at_5i" name="user[login_at(5i)]">
711
+ # <option>...</option> # Options for minute
712
+ # </select>
713
+ # <select class="input" id="user_login_at_6i" name="user[login_at(6i)]">
714
+ # <option>...</option> # Options for second
715
+ # </select>
716
+ # </div>
717
+ #
718
+ # == Allowed options:
719
+ # * +:id+ - HTML ID
720
+ # * +:class+ - HTML class
721
+ # * +:style+ - not recommended HTML styles (use CSS)
722
+ # * +:autofocus+ - HTML autofocus
723
+ # * +:required+ - overwrite required field
724
+ # * +:label+ - label options (*false* to not create)
725
+ # * +:hint+ - hint options (*false* to not create)
726
+ # * +:error+ - error options (*false* to not create)
727
+ # * +:elements+ - elements of time field (available: +:hour+, +:minute+, +:second+)
728
+ # * +:separator+ - time separator (default " : ")
729
+ # * +:minute_step+ - minute steps (for example: "15" gives values: ["00", "15", "30", "45"])
730
+ # * +:date+ - add date fields as hidden
731
+ # * +:time+ - selected time
732
+ # * +:default+ - alias for +:time+ option
733
+ # * +:prompt+ - custom messages for each select (available options: +:hour+, +:minute+ and +:second+ or *true* for auto)
734
+ # * +:blank+ - include blank option for each select
735
+ # * +:placeholder+ - HTML placeholder (HTML5)
736
+ # * +:readonly+ - read-only input
737
+ # * +:disabled+ - disable input
738
+ #
739
+ def time(method, options={})
740
+ Element::TimeInput.new(self, method, options).generate
741
+ end
742
+
743
+ # == Time zone field
744
+ #
745
+ # Generate time zone field with input elements.
746
+ #
747
+ # == Example:
748
+ #
749
+ # case_form_for(@user) do |f|
750
+ # f.time(:time_zone)
751
+ # end
752
+ #
753
+ # <div class="inputs">
754
+ # <label class="label" for="user_time_zone" id="user_time_zone_label">Time zone</label>
755
+ # <select class="input" id="user_time_zone" name="user[time_zone]">
756
+ # <option>...</option>
757
+ # </select>
758
+ # </div>
759
+ #
760
+ # == Allowed options:
761
+ # * +:id+ - HTML ID
762
+ # * +:class+ - HTML class
763
+ # * +:style+ - not recommended HTML styles (use CSS)
764
+ # * +:autofocus+ - HTML autofocus
765
+ # * +:required+ - overwrite required field
766
+ # * +:label+ - label options (*false* to not create)
767
+ # * +:hint+ - hint options (*false* to not create)
768
+ # * +:error+ - error options (*false* to not create)
769
+ # * +:priority_zones+ - collection of priority zones
770
+ # * +:zones+ - collection of all time zones
771
+ # * +:time_zone+ - selected time zone
772
+ # * +:default+ - alias for +:time_zone+ option
773
+ # * +:prompt+ - custom messages for select
774
+ # * +:blank+ - include blank option for select
775
+ # * +:placeholder+ - HTML placeholder (HTML5)
776
+ # * +:readonly+ - read-only input
777
+ # * +:disabled+ - disable input
778
+ #
779
+ def time_zone(method, options={})
780
+ Element::TimeZoneInput.new(self, method, options).generate
781
+ end
782
+
783
+ # == File field
784
+ #
785
+ # Generate file field with input elements. Remeber to add <tt>:multipart => true</tt> option in form!
786
+ #
787
+ # == Example:
788
+ #
789
+ # case_form_for(@user, :multipart => true) do |f|
790
+ # f.file(:avatar)
791
+ # end
792
+ #
793
+ # <div class="inputs">
794
+ # <label class="label" for="user_avatar" id="user_avatar_label">Avatar</label>
795
+ # <input class="input" id="user_avatar" name="user[avatar]" type="file" />
796
+ # </div>
797
+ #
798
+ # == Allowed options:
799
+ # * +:id+ - HTML ID
800
+ # * +:class+ - HTML class
801
+ # * +:style+ - not recommended HTML styles (use CSS)
802
+ # * +:autofocus+ - HTML autofocus
803
+ # * +:required+ - overwrite required field
804
+ # * +:label+ - label options (*false* to not create)
805
+ # * +:hint+ - hint options (*false* to not create)
806
+ # * +:error+ - error options (*false* to not create)
807
+ # * +:multiple+ - allow upload multiple files
808
+ # * +:readonly+ - read-only input
809
+ # * +:disabled+ - disable input
810
+ #
811
+ def file(method, options={})
812
+ Element::FileInput.new(self, method, options).generate
813
+ end
814
+
815
+ # == Checkbox field
816
+ #
817
+ # Generate simple or collection of checkboxes with input elements. Collection can have many of types.
818
+ #
819
+ # == Collection types:
820
+ #
821
+ # * association class - <tt>:collection => Country</tt>
822
+ # * +Array+ with model objects - <tt>:collection => Country.priority</tt>
823
+ # * +Array+ with label and value - <tt>:collection => [["Administrator", 1], ["Author", 2]]</tt>
824
+ # * +Array+ with same labels and values - <tt>:collection => ["Administrator", "Author"]</tt>
825
+ # * +Hash+ - <tt>:collection => { "Administrator" => 1, "Author" => 2 }</tt>
826
+ # * +Range+ - <tt>:collection => 1..100</tt>
827
+ #
828
+ # Default collection is <tt>[["Yes", true], ["No", false]]</tt>
829
+ #
830
+ # == Example:
831
+ #
832
+ # # With default collection
833
+ #
834
+ # case_form_for(@user) do |f|
835
+ # f.checkbox(:admin)
836
+ # end
837
+ #
838
+ # <div class="inputs">
839
+ # <label class="label" for="user_admin" id="user_admin_label">Admin</label>
840
+ # <input name="user[admin]" type="hidden" value="" />
841
+ # <input class="input" id="user_admin_true" name="user[admin]" type="checkbox" value="true" />
842
+ # <label class="label" for="user_admin_true" id="user_admin_true_label">Yes</label>
843
+ # </div>
844
+ #
845
+ # # with array collection
846
+ #
847
+ # case_form_for(@user) do |f|
848
+ # f.checkbox(:role, :collection => [["Admin", 1], ["Author", 2], ["User", 3]])
849
+ # end
850
+ #
851
+ # <div class="inputs">
852
+ # <label class="label" for="user_role" id="user_role_label">Role</label>
853
+ # <input name="user[role]" type="hidden" value="" />
854
+ # <input class="input" id="user_role_1" name="user[role]" type="checkbox" value="1" />
855
+ # <label class="label" for="user_role_1" id="user_role_1_label">Administrator</label>
856
+ # <input name="user[role]" type="hidden" value="" />
857
+ # <input class="input" id="user_role_2" name="user[role]" type="checkbox" value="2" />
858
+ # <label class="label" for="user_role_2" id="user_role_2_label">Author</label>
859
+ # <input name="user[role]" type="hidden" value="" />
860
+ # <input class="input" id="user_role_3" name="user[role]" type="checkbox" value="3" />
861
+ # <label class="label" for="user_role_3" id="user_role_3_label">User</label>
862
+ # </div>
863
+ #
864
+ # # with association class
865
+ #
866
+ # # class User
867
+ # # belongs_to :country
868
+ # # end
869
+ #
870
+ # case_form_for(@user) do |f|
871
+ # f.checkbox(:country, :collection => Country, :label_method => :short_name, :value_method => :id)
872
+ # end
873
+ #
874
+ # <div class="inputs">
875
+ # <label class="label" for="user_country" id="user_country_label">Country</label>
876
+ # <input name="user[country_id]" type="hidden" value="" />
877
+ # <input class="input" id="user_country_1" name="user[country_id]" type="checkbox" value="1" />
878
+ # <label class="label" for="user_country_1" id="user_country_1_label">Poland</label>
879
+ # <input name="user[country_id]" type="hidden" value="" />
880
+ # <input class="input" id="user_country_2" name="user[country_id]" type="checkbox" value="2" />
881
+ # <label class="label" for="user_country_2" id="user_country_2_label">Spain</label>
882
+ # </div>
883
+ #
884
+ # == Allowed options:
885
+ # * +:id+ - HTML ID
886
+ # * +:class+ - HTML class
887
+ # * +:style+ - not recommended HTML styles (use CSS)
888
+ # * +:autofocus+ - HTML autofocus
889
+ # * +:required+ - overwrite required field
890
+ # * +:label+ - label options (*false* to not create)
891
+ # * +:hint+ - hint options (*false* to not create)
892
+ # * +:error+ - error options (*false* to not create)
893
+ # * +:checked+ - list of checked values
894
+ # * +:selected+ - same as +:checked+ option
895
+ # * +:collection+ - collection of available values
896
+ # * +:label_method+ - method for labels in collection
897
+ # * +:value_method+ - method for values in collection
898
+ # * +:unchecked_value+ - value for hidden input
899
+ # * +:allow_multiple+ - allow multiple select (add "[]" to checkbox name)
900
+ # * +:readonly+ - read-only input
901
+ # * +:disabled+ - disable input
902
+ #
903
+ def checkbox(method, options={})
904
+ Element::CheckboxInput.new(self, method, options).generate
905
+ end
906
+
907
+ # == Select field
908
+ def select(method, options={})
909
+ Element::SelectInput.new(self, method, options).generate
910
+ end
911
+
912
+ # == Radio button field
913
+ #
914
+ # Generate simple or collection of radio's with input elements. Collection can have many of types.
915
+ #
916
+ # == Collection types:
917
+ #
918
+ # * association class - <tt>:collection => Country</tt>
919
+ # * +Array+ with model objects - <tt>:collection => Country.priority</tt>
920
+ # * +Array+ with label and value - <tt>:collection => [["Administrator", 1], ["Author", 2]]</tt>
921
+ # * +Array+ with same labels and values - <tt>:collection => ["Administrator", "Author"]</tt>
922
+ # * +Hash+ - <tt>:collection => { "Administrator" => 1, "Author" => 2 }</tt>
923
+ # * +Range+ - <tt>:collection => 1..100</tt>
924
+ #
925
+ # Default collection is <tt>[["Yes", true], ["No", false]]</tt>
926
+ #
927
+ # == Example:
928
+ #
929
+ # # With default collection
930
+ #
931
+ # case_form_for(@user) do |f|
932
+ # f.radio(:admin)
933
+ # end
934
+ #
935
+ # <div class="inputs">
936
+ # <label class="label" for="user_admin" id="user_admin_label">Admin</label>
937
+ # <input class="input" id="user_admin_true" name="user[admin]" type="radio" value="true" />
938
+ # <label class="label" for="user_admin_true" id="user_admin_true_label">Yes</label>
939
+ # <input class="input" id="user_admin_false" name="user[admin]" type="radio" value="false" />
940
+ # <label class="label" for="user_admin_false" id="user_admin_false_label">No</label>
941
+ # </div>
942
+ #
943
+ # # with array collection
944
+ #
945
+ # case_form_for(@user) do |f|
946
+ # f.radio(:role, :collection => [["Admin", 1], ["Author", 2], ["User", 3]])
947
+ # end
948
+ #
949
+ # <div class="inputs">
950
+ # <label class="label" for="user_admin" id="user_admin_label">Admin</label>
951
+ # <input class="input" id="user_admin_1" name="user[admin]" type="radio" value="1" />
952
+ # <label class="label" for="user_admin_1" id="user_admin_1_label">Admin</label>
953
+ # <input class="input" id="user_admin_2" name="user[admin]" type="radio" value="2" />
954
+ # <label class="label" for="user_admin_2" id="user_admin_2_label">Author</label>
955
+ # <input class="input" id="user_admin_3" name="user[admin]" type="radio" value="3" />
956
+ # <label class="label" for="user_admin_3" id="user_admin_3_label">User</label>
957
+ # </div>
958
+ #
959
+ # # with association class
960
+ #
961
+ # # class User
962
+ # # belongs_to :country
963
+ # # end
964
+ #
965
+ # case_form_for(@user) do |f|
966
+ # f.radio(:country, :collection => Country, :label_method => :short_name, :value_method => :id)
967
+ # end
968
+ #
969
+ # <div class="inputs">
970
+ # <input class="input" id="user_country_id_1" name="user[country_id]" type="radio" value="1" />
971
+ # <label class="label" for="user_country_id_1" id="user_country_id_1_label">Poland</label>
972
+ # <input class="input" id="user_country_id_2" name="user[country_id]" type="radio" value="2" />
973
+ # <label class="label" for="user_country_id_2" id="user_country_id_2_label">Germany</label>
974
+ # </div>
975
+ #
976
+ # == Allowed options:
977
+ # * +:id+ - HTML ID
978
+ # * +:class+ - HTML class
979
+ # * +:style+ - not recommended HTML styles (use CSS)
980
+ # * +:autofocus+ - HTML autofocus
981
+ # * +:required+ - overwrite required field
982
+ # * +:label+ - label options (*false* to not create)
983
+ # * +:hint+ - hint options (*false* to not create)
984
+ # * +:error+ - error options (*false* to not create)
985
+ # * +:checked+ - list of checked values
986
+ # * +:selected+ - same as +:checked+ option
987
+ # * +:collection+ - collection of available values
988
+ # * +:label_method+ - method for labels in collection
989
+ # * +:value_method+ - method for values in collection
990
+ # * +:unchecked_value+ - value for hidden input
991
+ # * +:allow_multiple+ - allow multiple select (add "[]" to checkbox name)
992
+ # * +:readonly+ - read-only input
993
+ # * +:disabled+ - disable input
994
+ #
995
+ def radio(method, options={})
996
+ Element::RadioInput.new(self, method, options).generate
997
+ end
998
+
999
+ # == Number field
1000
+ #
1001
+ # Generate number field with input elements.
1002
+ #
1003
+ # == Example:
1004
+ #
1005
+ # case_form_for(@user) do |f|
1006
+ # f.number(:age)
1007
+ # end
1008
+ #
1009
+ # <div class="inputs">
1010
+ # <label class="label" for="user_age" id="user_age_label">Age</label>
1011
+ # <input class="input" id="user_age" name="user[age]" type="number" />
1012
+ # </div>
1013
+ #
1014
+ # == Allowed options:
1015
+ # * +:id+ - HTML ID
1016
+ # * +:class+ - HTML class
1017
+ # * +:style+ - not recommended HTML styles (use CSS)
1018
+ # * +:autofocus+ - HTML autofocus
1019
+ # * +:required+ - overwrite required field
1020
+ # * +:label+ - label options (*false* to not create)
1021
+ # * +:hint+ - hint options (*false* to not create)
1022
+ # * +:error+ - error options (*false* to not create)
1023
+ # * +:min+ - min value
1024
+ # * +:max+ - max value
1025
+ # * +:in+ - range between minimum and maximum
1026
+ # * +:step+ - step between values
1027
+ # * +:readonly+ - read-only input
1028
+ # * +:disabled+ - disable input
1029
+ #
1030
+ def number(method, options={})
1031
+ Element::NumberInput.new(self, method, options.merge(:as => :number)).generate
1032
+ end
1033
+
1034
+ # == Range field
1035
+ #
1036
+ # Generate range field with input elements.
1037
+ #
1038
+ # == Example:
1039
+ #
1040
+ # case_form_for(@user) do |f|
1041
+ # f.range(:height)
1042
+ # end
1043
+ #
1044
+ # <div class="inputs">
1045
+ # <label class="label" for="user_height" id="user_height_label">Height</label>
1046
+ # <input class="input" id="user_height" name="user[height]" type="range" />
1047
+ # </div>
1048
+ #
1049
+ # == Allowed options:
1050
+ # * +:id+ - HTML ID
1051
+ # * +:class+ - HTML class
1052
+ # * +:style+ - not recommended HTML styles (use CSS)
1053
+ # * +:autofocus+ - HTML autofocus
1054
+ # * +:required+ - overwrite required field
1055
+ # * +:label+ - label options (*false* to not create)
1056
+ # * +:hint+ - hint options (*false* to not create)
1057
+ # * +:error+ - error options (*false* to not create)
1058
+ # * +:min+ - min value
1059
+ # * +:max+ - max value
1060
+ # * +:in+ - range between minimum and maximum
1061
+ # * +:step+ - step between values
1062
+ # * +:readonly+ - read-only input
1063
+ # * +:disabled+ - disable input
1064
+ #
1065
+ def range(method, options={})
1066
+ Element::NumberInput.new(self, method, options.merge(:as => :range)).generate
1067
+ end
1068
+
1069
+ private
1070
+ def specified_input(method, options)
1071
+ input = case method.to_s
1072
+ when /password/ then :password
1073
+ when /file/ then :file
1074
+ when /mail/ then :email
1075
+ when /url/ then :url
1076
+ when /phone/ then :telephone
1077
+ when /zone/ then :time_zone
1078
+ when "_destroy" then :checkbox
1079
+ else
1080
+ case object.column_for_attribute(method).type
1081
+ when (:string || :binary) then :string
1082
+ when :text then :text
1083
+ when (:integer || :float || :decimal) then :number
1084
+ when :datetime then :datetime
1085
+ when :date then :date
1086
+ when (:timestamp || :time) then :time
1087
+ when :boolean then :checkbox
1088
+ else
1089
+ :string
1090
+ end
1091
+ end
1092
+ send(input, method, options)
1093
+ end
1094
+ end
1095
+ end