nuatt-formtastic 0.2.2 → 0.2.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 (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