nuatt-formtastic 0.2.2 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (68) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.textile +635 -0
  3. data/lib/formtastic.rb +24 -0
  4. data/lib/formtastic/form_builder.rb +75 -0
  5. data/lib/formtastic/helpers.rb +15 -0
  6. data/lib/formtastic/helpers/buttons_helper.rb +277 -0
  7. data/lib/formtastic/helpers/errors_helper.rb +124 -0
  8. data/lib/formtastic/helpers/fieldset_wrapper.rb +62 -0
  9. data/lib/formtastic/helpers/file_column_detection.rb +16 -0
  10. data/lib/formtastic/helpers/form_helper.rb +221 -0
  11. data/lib/formtastic/helpers/input_helper.rb +357 -0
  12. data/lib/formtastic/helpers/inputs_helper.rb +381 -0
  13. data/lib/formtastic/helpers/reflection.rb +12 -0
  14. data/lib/formtastic/helpers/semantic_form_helper.rb +11 -0
  15. data/lib/formtastic/html_attributes.rb +21 -0
  16. data/lib/formtastic/i18n.rb +32 -0
  17. data/lib/formtastic/inputs.rb +29 -0
  18. data/lib/formtastic/inputs/base.rb +50 -0
  19. data/lib/formtastic/inputs/base/associations.rb +33 -0
  20. data/lib/formtastic/inputs/base/choices.rb +88 -0
  21. data/lib/formtastic/inputs/base/collections.rb +94 -0
  22. data/lib/formtastic/inputs/base/database.rb +17 -0
  23. data/lib/formtastic/inputs/base/errors.rb +58 -0
  24. data/lib/formtastic/inputs/base/fileish.rb +23 -0
  25. data/lib/formtastic/inputs/base/grouped_collections.rb +77 -0
  26. data/lib/formtastic/inputs/base/hints.rb +31 -0
  27. data/lib/formtastic/inputs/base/html.rb +51 -0
  28. data/lib/formtastic/inputs/base/labelling.rb +53 -0
  29. data/lib/formtastic/inputs/base/naming.rb +54 -0
  30. data/lib/formtastic/inputs/base/options.rb +18 -0
  31. data/lib/formtastic/inputs/base/stringish.rb +30 -0
  32. data/lib/formtastic/inputs/base/timeish.rb +125 -0
  33. data/lib/formtastic/inputs/base/validations.rb +125 -0
  34. data/lib/formtastic/inputs/base/wrapping.rb +38 -0
  35. data/lib/formtastic/inputs/boolean_input.rb +87 -0
  36. data/lib/formtastic/inputs/check_boxes_input.rb +169 -0
  37. data/lib/formtastic/inputs/country_input.rb +66 -0
  38. data/lib/formtastic/inputs/date_input.rb +14 -0
  39. data/lib/formtastic/inputs/datetime_input.rb +9 -0
  40. data/lib/formtastic/inputs/email_input.rb +40 -0
  41. data/lib/formtastic/inputs/file_input.rb +42 -0
  42. data/lib/formtastic/inputs/hidden_input.rb +66 -0
  43. data/lib/formtastic/inputs/number_input.rb +72 -0
  44. data/lib/formtastic/inputs/numeric_input.rb +20 -0
  45. data/lib/formtastic/inputs/password_input.rb +40 -0
  46. data/lib/formtastic/inputs/phone_input.rb +41 -0
  47. data/lib/formtastic/inputs/radio_input.rb +146 -0
  48. data/lib/formtastic/inputs/search_input.rb +40 -0
  49. data/lib/formtastic/inputs/select_input.rb +208 -0
  50. data/lib/formtastic/inputs/string_input.rb +34 -0
  51. data/lib/formtastic/inputs/text_input.rb +47 -0
  52. data/lib/formtastic/inputs/time_input.rb +14 -0
  53. data/lib/formtastic/inputs/time_zone_input.rb +48 -0
  54. data/lib/formtastic/inputs/url_input.rb +40 -0
  55. data/lib/formtastic/localized_string.rb +96 -0
  56. data/lib/formtastic/railtie.rb +12 -0
  57. data/lib/formtastic/semantic_form_builder.rb +11 -0
  58. data/lib/formtastic/util.rb +25 -0
  59. data/lib/generators/formtastic/form/form_generator.rb +95 -0
  60. data/lib/generators/formtastic/install/install_generator.rb +23 -0
  61. data/lib/generators/templates/_form.html.erb +7 -0
  62. data/lib/generators/templates/_form.html.haml +5 -0
  63. data/lib/generators/templates/formtastic.css +145 -0
  64. data/lib/generators/templates/formtastic.rb +74 -0
  65. data/lib/generators/templates/formtastic_changes.css +14 -0
  66. data/lib/locale/en.yml +7 -0
  67. data/lib/tasks/verify_rcov.rb +44 -0
  68. metadata +206 -19
@@ -0,0 +1,38 @@
1
+ module Formtastic
2
+ module Inputs
3
+ module Base
4
+ # @todo relies on `dom_id`, `required?`, `optional`, `errors?`, `association_primary_key` & `sanitized_method_name` methods from another module
5
+ module Wrapping
6
+
7
+ # Override this method if you want to change the display order (for example, rendering the
8
+ # errors before the body of the input).
9
+ def input_wrapping(&block)
10
+ template.content_tag(:li,
11
+ [template.capture(&block), error_html, hint_html].join("\n").html_safe,
12
+ wrapper_html_options
13
+ )
14
+ end
15
+
16
+ def wrapper_html_options
17
+ opts = options[:wrapper_html] || {}
18
+ opts[:class] ||= []
19
+ opts[:class] = [opts[:class].to_s] unless opts[:class].is_a?(Array)
20
+ opts[:class] << as
21
+ opts[:class] << "error" if errors?
22
+ opts[:class] << "optional" if optional?
23
+ opts[:class] << "required" if required?
24
+ opts[:class] = opts[:class].join(' ')
25
+
26
+ opts[:id] ||= wrapper_dom_id
27
+
28
+ opts
29
+ end
30
+
31
+ def wrapper_dom_id
32
+ @wrapper_dom_id ||= "#{dom_id.to_s.gsub((association_primary_key || method).to_s, sanitized_method_name.to_s)}_input"
33
+ end
34
+
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,87 @@
1
+ module Formtastic
2
+ module Inputs
3
+ # Boolean inputs are used to render an input for a single checkbox, typically for attributes
4
+ # with a simple yes/no or true/false value. Boolean inputs are used by default for boolean
5
+ # database columns.
6
+ #
7
+ # @example Full form context and markup
8
+ # <%= semantic_form_for @post %>
9
+ # <%= f.inputs do %>
10
+ # <%= f.input :published, :as => :boolean %>
11
+ # <% end %>
12
+ # <% end %>
13
+ #
14
+ # <form...>
15
+ # <fieldset>
16
+ # <ol>
17
+ # <li class="boolean" id="post_published_input">
18
+ # <input type="hidden" name="post[published]" id="post_published" value="0">
19
+ # <label for="post_published">
20
+ # <input type="checkbox" name="post[published]" id="post_published" value="1">
21
+ # Published?
22
+ # </label>
23
+ # </li>
24
+ # </ol>
25
+ # </fieldset>
26
+ # </form>
27
+ #
28
+ # @example Set the values for the checked and unchecked states
29
+ # <%= f.input :published, :checked_value => "yes", :unchecked_value => "no" %>
30
+ #
31
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
32
+ class BooleanInput
33
+ include Base
34
+
35
+ def to_html
36
+ input_wrapping do
37
+ hidden_field_html <<
38
+ label_with_nested_checkbox
39
+ end
40
+ end
41
+
42
+ def hidden_field_html
43
+ template.hidden_field_tag(input_html_options[:name], unchecked_value, :id => nil, :disabled => input_html_options[:disabled] )
44
+ end
45
+
46
+ def label_with_nested_checkbox
47
+ builder.label(
48
+ method,
49
+ label_text_with_embedded_checkbox,
50
+ label_html_options
51
+ )
52
+ end
53
+
54
+ def label_html_options
55
+ input_html_options.merge(
56
+ :id => nil,
57
+ :for => input_html_options[:id]
58
+ )
59
+ end
60
+
61
+ def label_text_with_embedded_checkbox
62
+ label_text << " " << check_box_html
63
+ end
64
+
65
+ def check_box_html
66
+ template.check_box_tag("#{object_name}[#{method}]", checked_value, checked?, input_html_options)
67
+ end
68
+
69
+ def unchecked_value
70
+ options[:unchecked_value] || '0'
71
+ end
72
+
73
+ def checked_value
74
+ options[:checked_value] || '1'
75
+ end
76
+
77
+ def input_html_options
78
+ {:name => "#{object_name}[#{method}]"}.merge(super)
79
+ end
80
+
81
+ def checked?
82
+ object && ActionView::Helpers::InstanceTag.check_box_checked?(object.send(method), checked_value)
83
+ end
84
+
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,169 @@
1
+ module Formtastic
2
+ module Inputs
3
+
4
+ # A CheckBoxes input is used to render a series of checkboxes. This is an alternative input choice
5
+ # for `has_many` or `has_and_belongs_to_many` associations like a `Post` belonging to many
6
+ # `categories` (by default, a {SelectInput `:select`} input is used, allowing multiple selections).
7
+ #
8
+ # Within the standard `<li>` wrapper, the output is a `<fieldset>` with a `<legend>` to
9
+ # represent the "label" for the input, and an `<ol>` containing `<li>`s for each choice in
10
+ # the association. Each `<li>` choice contains a hidden `<input>` tag for the "unchecked"
11
+ # value (like Rails), and a `<label>` containing the checkbox `<input>` and the label text
12
+ # for each choice.
13
+ #
14
+ # @example Basic example with full form context
15
+ #
16
+ # <%= semantic_form_for @post do |f| %>
17
+ # <%= f.inputs do %>
18
+ # <%= f.input :categories, :as => :check_boxes %>
19
+ # <% end %>
20
+ # <% end %>
21
+ #
22
+ # <li class='check_boxes'>
23
+ # <fieldset>
24
+ # <legend class="label"><label>Categories</label></legend>
25
+ # <ol>
26
+ # <li>
27
+ # <input type="hidden" name="post[category_ids][1]" value="">
28
+ # <label for="post_category_ids_1"><input id="post_category_ids_1" name="post[category_ids][1]" type="checkbox" value="1" /> Ruby</label>
29
+ # </li>
30
+ # <li>
31
+ # <input type="hidden" name="post[category_ids][2]" value="">
32
+ # <label for="post_category_ids_2"><input id="post_category_ids_2" name="post[category_ids][2]" type="checkbox" value="2" /> Rails</label>
33
+ # </li>
34
+ # </ol>
35
+ # </fieldset>
36
+ # </li>
37
+ #
38
+ # @example `:collection` can be used to customize the choices
39
+ # <%= f.input :categories, :as => :check_boxes, :collection => @categories %>
40
+ # <%= f.input :categories, :as => :check_boxes, :collection => Category.all %>
41
+ # <%= f.input :categories, :as => :check_boxes, :collection => Category.some_named_scope %>
42
+ # <%= f.input :categories, :as => :check_boxes, :collection => [Category.find_by_name("Ruby"), Category.find_by_name("Rails")] %>
43
+ # <%= f.input :categories, :as => :check_boxes, :collection => ["Ruby", "Rails"] %>
44
+ # <%= f.input :categories, :as => :check_boxes, :collection => [["Ruby", "ruby"], ["Rails", "rails"]] %>
45
+ # <%= f.input :categories, :as => :check_boxes, :collection => [["Ruby", "1"], ["Rails", "2"]] %>
46
+ # <%= f.input :categories, :as => :check_boxes, :collection => [["Ruby", 1], ["Rails", 2]] %>
47
+ # <%= f.input :categories, :as => :check_boxes, :collection => 1..5 %>
48
+ #
49
+ # @example `:hidden_fields` can be used to skip Rails' rendering of a hidden field before every checkbox
50
+ # <%= f.input :categories, :as => :check_boxes, :hidden_fields => false %>
51
+ #
52
+ # @example `:disabled` can be used to disable any checkboxes with a value found in the given Array
53
+ # <%= f.input :categories, :as => :check_boxes, :collection => ["a", "b"], :disabled => ["a"] %>
54
+ #
55
+ # @example `:label_method` can be used to call a different method (or a Proc) on each object in the collection for rendering the label text (it'll try the methods like `to_s` in `collection_label_methods` config by default)
56
+ # <%= f.input :categories, :as => :check_boxes, :label_method => :name %>
57
+ # <%= f.input :categories, :as => :check_boxes, :label_method => :name_with_post_count
58
+ # <%= f.input :categories, :as => :check_boxes, :label_method => Proc.new { |c| "#{c.name} (#{pluralize("post", c.posts.count)})" }
59
+ #
60
+ # @example `:value_method` can be used to call a different method (or a Proc) on each object in the collection for rendering the value for each checkbox (it'll try the methods like `id` in `collection_value_methods` config by default)
61
+ # <%= f.input :categories, :as => :check_boxes, :value_method => :code %>
62
+ # <%= f.input :categories, :as => :check_boxes, :value_method => :isbn
63
+ # <%= f.input :categories, :as => :check_boxes, :value_method => Proc.new { |c| c.name.downcase.underscore }
64
+ #
65
+ # @example `:value_as_class` can be used to add a class to the `<li>` wrapped around each choice using the checkbox value for custom styling of each choice
66
+ # <%= f.input :categories, :as => :check_boxes, :value_as_class => true %>
67
+ #
68
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
69
+ # @see Formtastic::Inputs::BooleanInput BooleanInput for a single checkbox for boolean (checked = true) inputs
70
+ class CheckBoxesInput
71
+ include Base
72
+ include Base::Collections
73
+ include Base::Choices
74
+
75
+ def to_html
76
+ input_wrapping do
77
+ choices_wrapping do
78
+ legend_html <<
79
+ hidden_field_for_all <<
80
+ choices_group_wrapping do
81
+ collection.map { |choice|
82
+ choice_wrapping(choice_wrapping_html_options(choice)) do
83
+ choice_html(choice)
84
+ end
85
+ }.join("\n").html_safe
86
+ end
87
+ end
88
+ end
89
+ end
90
+
91
+ def choice_html(choice)
92
+ template.content_tag(:label,
93
+ hidden_fields? ?
94
+ check_box_with_hidden_input(choice) :
95
+ check_box_without_hidden_input(choice) <<
96
+ choice_label(choice),
97
+ label_html_options.merge(:for => choice_input_dom_id(choice))
98
+ )
99
+ end
100
+
101
+ def hidden_field_for_all
102
+ if hidden_fields?
103
+ ""
104
+ else
105
+ options = {}
106
+ options[:class] = [method.to_s.singularize, 'default'].join('_') if value_as_class?
107
+ options[:id] = [object_name, method, 'none'].join('_')
108
+ template.hidden_field_tag(input_name, '', options)
109
+ end
110
+ end
111
+
112
+ def hidden_fields?
113
+ options[:hidden_fields]
114
+ end
115
+
116
+ def check_box_with_hidden_input(choice)
117
+ value = choice_value(choice)
118
+ builder.check_box(
119
+ association_primary_key || method,
120
+ input_html_options.merge(:id => choice_input_dom_id(choice), :name => input_name, :disabled => disabled?(value)),
121
+ value,
122
+ unchecked_value
123
+ )
124
+ end
125
+
126
+ def check_box_without_hidden_input(choice)
127
+ value = choice_value(choice)
128
+ template.check_box_tag(
129
+ input_name,
130
+ value,
131
+ checked?(value),
132
+ input_html_options.merge(:id => choice_input_dom_id(choice), :disabled => disabled?(value))
133
+ )
134
+ end
135
+
136
+ def checked?(value)
137
+ selected_values.include?(value)
138
+ end
139
+
140
+ def disabled?(value)
141
+ disabled_values.include?(value)
142
+ end
143
+
144
+ def selected_values
145
+ if object.respond_to?(method)
146
+ selected_items = [object.send(method)].compact.flatten
147
+ [*selected_items.map { |o| send_or_call(value_method, o) }].compact
148
+ else
149
+ []
150
+ end
151
+ end
152
+
153
+ def disabled_values
154
+ vals = options[:disabled] || []
155
+ vals = [vals] unless vals.is_a?(Array)
156
+ vals
157
+ end
158
+
159
+ def unchecked_value
160
+ options[:unchecked_value] || ''
161
+ end
162
+
163
+ def input_name
164
+ "#{object_name}[#{association_primary_key || method}][]"
165
+ end
166
+
167
+ end
168
+ end
169
+ end
@@ -0,0 +1,66 @@
1
+ module Formtastic
2
+ module Inputs
3
+ # Outputs a country select input, wrapping around a regular country_select helper.
4
+ # Rails doesn't come with a `country_select` helper by default any more, so you'll need to do
5
+ # one of the following:
6
+ #
7
+ # * install [the official Rails plugin](http://github.com/rails/iso-3166-country-select)
8
+ # * install any other country_select plugin that behaves in a similar way
9
+ # * roll your own `country_select` helper with the same args and options as the Rails one
10
+ #
11
+ # By default, Formtastic includes a handfull of English-speaking countries as "priority
12
+ # counties", which can be set in the `priority_countries` configuration array in the
13
+ # formtastic.rb initializer to suit your market and user base (see README for more info on
14
+ # configuration). Additionally, it is possible to set the :priority_countries on a per-input
15
+ # basis through the `:priority_countries` option. These priority countries will be passed down
16
+ # to the `country_select` helper of your choice, and may or may not be used by the helper.
17
+ #
18
+ # @example Basic example with full form context using `priority_countries` from config
19
+ #
20
+ # <%= semantic_form_for @user do |f| %>
21
+ # <%= f.inputs do %>
22
+ # <%= f.input :nationality, :as => :country %>
23
+ # <% end %>
24
+ # <% end %>
25
+ #
26
+ # <li class='country'>
27
+ # <label for="user_nationality">Country</label>
28
+ # <select id="user_nationality" name="user[nationality]">
29
+ # <option value="...">...</option>
30
+ # # ...
31
+ # </li>
32
+ #
33
+ # @example `:priority_countries` set on a specific input
34
+ #
35
+ # <%= semantic_form_for @user do |f| %>
36
+ # <%= f.inputs do %>
37
+ # <%= f.input :nationality, :as => :country, :priority_countries => ["Australia", "New Zealand"] %>
38
+ # <% end %>
39
+ # <% end %>
40
+ #
41
+ # <li class='country'>
42
+ # <label for="user_nationality">Country</label>
43
+ # <select id="user_nationality" name="user[nationality]">
44
+ # <option value="...">...</option>
45
+ # # ...
46
+ # </li>
47
+ #
48
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
49
+ class CountryInput
50
+ include Base
51
+
52
+ def to_html
53
+ raise "To use the :country input, please install a country_select plugin, like this one: http://github.com/rails/iso-3166-country-select" unless builder.respond_to?(:country_select)
54
+ input_wrapping do
55
+ label_html <<
56
+ builder.country_select(method, priority_countries, input_options, input_html_options)
57
+ end
58
+ end
59
+
60
+ def priority_countries
61
+ options[:priority_countries] || builder.priority_countries
62
+ end
63
+
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,14 @@
1
+ module Formtastic
2
+ module Inputs
3
+ # @see Formtastic::Inputs::Timeish Timeish module for documetation of date, time and datetime input options.
4
+ class DateInput
5
+ include Base
6
+ include Base::Timeish
7
+
8
+ # We don't want hour and minute fragments on a date input
9
+ def time_fragments
10
+ []
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,9 @@
1
+ module Formtastic
2
+ module Inputs
3
+ # @see Formtastic::Inputs::Timeish Timeish module for documetation of date, time and datetime input options.
4
+ class DatetimeInput
5
+ include Base
6
+ include Base::Timeish
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,40 @@
1
+ module Formtastic
2
+ module Inputs
3
+
4
+ # Outputs a simple `<label>` with a HTML5 `<input type="email">` wrapped in the standard
5
+ # `<li>` wrapper. This is the default input choice for attributes with a name matching
6
+ # `/email/`, but can be applied to any text-like input with `:as => :email`.
7
+ #
8
+ # @example Full form context and output
9
+ #
10
+ # <%= semantic_form_for(@user) do |f| %>
11
+ # <%= f.inputs do %>
12
+ # <%= f.input :email_address, :as => :email %>
13
+ # <% end %>
14
+ # <% end %>
15
+ #
16
+ # <form...>
17
+ # <fieldset>
18
+ # <ol>
19
+ # <li class="email">
20
+ # <label for="user_email_address">Email address</label>
21
+ # <input type="email" id="user_email_address" name="user[email_address]">
22
+ # </li>
23
+ # </ol>
24
+ # </fieldset>
25
+ # </form>
26
+ #
27
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
28
+ class EmailInput
29
+ include Base
30
+ include Base::Stringish
31
+
32
+ def to_html
33
+ input_wrapping do
34
+ label_html <<
35
+ builder.email_field(method, input_html_options)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,42 @@
1
+ module Formtastic
2
+ module Inputs
3
+
4
+ # Outputs a simple `<label>` with a `<input type="file">` wrapped in the standard
5
+ # `<li>` wrapper. This is the default input choice for objects with attributes that appear
6
+ # to be for file uploads, by detecting some common method names used by popular file upload
7
+ # libraries such as Paperclip and CarrierWave. You can add to or alter these method names
8
+ # through the `file_methods` config, but can be applied to any input with `:as => :file`.
9
+ #
10
+ # Don't forget to set the multipart attribute in your `<form>` tag!
11
+ #
12
+ # @example Full form context and output
13
+ #
14
+ # <%= semantic_form_for(@user, :html => { :multipart => true }) do |f| %>
15
+ # <%= f.inputs do %>
16
+ # <%= f.input :email_address, :as => :email %>
17
+ # <% end %>
18
+ # <% end %>
19
+ #
20
+ # <form...>
21
+ # <fieldset>
22
+ # <ol>
23
+ # <li class="email">
24
+ # <label for="user_email_address">Email address</label>
25
+ # <input type="email" id="user_email_address" name="user[email_address]">
26
+ # </li>
27
+ # </ol>
28
+ # </fieldset>
29
+ # </form>
30
+ #
31
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
32
+ class FileInput
33
+ include Base
34
+ def to_html
35
+ input_wrapping do
36
+ label_html <<
37
+ builder.file_field(method, input_html_options)
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end