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,34 @@
1
+ module Formtastic
2
+ module Inputs
3
+
4
+ # Outputs a simple `<label>` with a `<input type="text">` wrapped in the standard
5
+ # `<li>` wrapper. This is the default input choice for database columns of the `:string` type,
6
+ # and is the default choice for all inputs when no other logical input type can be inferred.
7
+ # You can force any input to be a string input with `:as => :string`.
8
+ #
9
+ # @example Full form context and output
10
+ #
11
+ # <%= semantic_form_for(@user) do |f| %>
12
+ # <%= f.inputs do %>
13
+ # <%= f.input :first_name, :as => :string %>
14
+ # <% end %>
15
+ # <% end %>
16
+ #
17
+ # <form...>
18
+ # <fieldset>
19
+ # <ol>
20
+ # <li class="string">
21
+ # <label for="user_first_name">First name</label>
22
+ # <input type="text" id="user_first_name" name="user[first_name]">
23
+ # </li>
24
+ # </ol>
25
+ # </fieldset>
26
+ # </form>
27
+ #
28
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
29
+ class StringInput
30
+ include Base
31
+ include Base::Stringish
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,47 @@
1
+ module Formtastic
2
+ module Inputs
3
+
4
+ # Outputs a simple `<label>` with a `<textarea>` wrapped in the standard
5
+ # `<li>` wrapper. This is the default input choice for database columns of the `:text` type,
6
+ # but can forced on any text-like input with `:as => :text`.
7
+ #
8
+ # @example Full form context and output
9
+ #
10
+ # <%= semantic_form_for(@user) do |f| %>
11
+ # <%= f.inputs do %>
12
+ # <%= f.input :first_name, :as => :string %>
13
+ # <% end %>
14
+ # <% end %>
15
+ #
16
+ # <form...>
17
+ # <fieldset>
18
+ # <ol>
19
+ # <li class="string">
20
+ # <label for="user_first_name">First name</label>
21
+ # <input type="text" id="user_first_name" name="user[first_name]">
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 TextInput
29
+ include Base
30
+
31
+ def input_html_options
32
+ {
33
+ :cols => builder.default_text_area_width,
34
+ :rows => builder.default_text_area_height
35
+ }.merge(super)
36
+ end
37
+
38
+ def to_html
39
+ input_wrapping do
40
+ label_html <<
41
+ builder.text_area(method, input_html_options)
42
+ end
43
+ end
44
+
45
+ end
46
+ end
47
+ 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 TimeInput
5
+ include Base
6
+ include Base::Timeish
7
+
8
+ # we don't want year / month / day fragments in a time select
9
+ def date_fragments
10
+ []
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,48 @@
1
+ module Formtastic
2
+ module Inputs
3
+
4
+ # Outputs a `<label>` with a `<select>` containing a series of time zones (using Rails' own
5
+ # `time_zone_select` helper), wrapped in the standard `<li>` wrapper.
6
+
7
+ # This is the default input choice for attributes matching /time_zone/, but can be applied to
8
+ # any text-like input with `:as => :time_zone`.
9
+ #
10
+ # @example Full form context and output
11
+ #
12
+ # <%= semantic_form_for(@user) do |f| %>
13
+ # <%= f.inputs do %>
14
+ # <%= f.input :time_zone, :as => :time_zone %>
15
+ # <% end %>
16
+ # <% end %>
17
+ #
18
+ # <form...>
19
+ # <fieldset>
20
+ # <ol>
21
+ # <li class="time_zone">
22
+ # <label for="user_time_zone">Time zone</label>
23
+ # <input type="text" id="user_time_zone" name="user[time_zone]">
24
+ # </li>
25
+ # </ol>
26
+ # </fieldset>
27
+ # </form>
28
+ #
29
+ # @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documetation of all possible options.
30
+ #
31
+ # @todo document :priority_zones option
32
+ # @todo configurable default :priority_zones?
33
+ class TimeZoneInput
34
+ include Base
35
+
36
+ def to_html
37
+ input_wrapping do
38
+ label_html <<
39
+ builder.time_zone_select(method, priority_zones, input_options, input_html_options)
40
+ end
41
+ end
42
+
43
+ def priority_zones
44
+ options[:priority_zones] || [] # TODO config?
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,40 @@
1
+ module Formtastic
2
+ module Inputs
3
+
4
+ # Outputs a simple `<label>` with a HTML5 `<input type="url">` wrapped in the standard
5
+ # `<li>` wrapper. This is the default input choice for all attributes matching
6
+ # `/^url$|^website$|_url$/`, but can be applied to any text-like input with `:as => :url`.
7
+ #
8
+ # @example Full form context and output
9
+ #
10
+ # <%= semantic_form_for(@user) do |f| %>
11
+ # <%= f.inputs do %>
12
+ # <%= f.input :home_page, :as => :url %>
13
+ # <% end %>
14
+ # <% end %>
15
+ #
16
+ # <form...>
17
+ # <fieldset>
18
+ # <ol>
19
+ # <li class="url">
20
+ # <label for="user_home_page">Home page</label>
21
+ # <input type="number" id="user_home_page" name="user[home_page]">
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 UrlInput
29
+ include Base
30
+ include Base::Stringish
31
+
32
+ def to_html
33
+ input_wrapping do
34
+ label_html <<
35
+ builder.url_field(method, input_html_options)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,96 @@
1
+ module Formtastic
2
+ # @private
3
+ module LocalizedString
4
+
5
+ protected
6
+
7
+ # Internal generic method for looking up localized values within Formtastic
8
+ # using I18n, if no explicit value is set and I18n-lookups are enabled.
9
+ #
10
+ # Enabled/Disable this by setting:
11
+ #
12
+ # Formtastic::FormBuilder.i18n_lookups_by_default = true/false
13
+ #
14
+ # Lookup priority:
15
+ #
16
+ # 'formtastic.%{type}.%{model}.%{action}.%{attribute}'
17
+ # 'formtastic.%{type}.%{model}.%{attribute}'
18
+ # 'formtastic.%{type}.%{attribute}'
19
+ #
20
+ # Example:
21
+ #
22
+ # 'formtastic.labels.post.edit.title'
23
+ # 'formtastic.labels.post.title'
24
+ # 'formtastic.labels.title'
25
+ #
26
+ # NOTE: Generic, but only used for form input titles/labels/hints/actions (titles = legends, actions = buttons).
27
+ #
28
+ def localized_string(key, value, type, options = {}) #:nodoc:
29
+ key = value if value.is_a?(::Symbol)
30
+
31
+ if value.is_a?(::String)
32
+ escape_html_entities(value)
33
+ else
34
+ use_i18n = value.nil? ? i18n_lookups_by_default : (value != false)
35
+
36
+ if use_i18n
37
+ model_name, nested_model_name = normalize_model_name(self.model_name.underscore)
38
+ action_name = template.params[:action].to_s rescue ''
39
+ attribute_name = key.to_s
40
+
41
+ defaults = Formtastic::I18n::SCOPES.reject do |i18n_scope|
42
+ nested_model_name.nil? && i18n_scope.match(/nested_model/)
43
+ end.collect do |i18n_scope|
44
+ i18n_path = i18n_scope.dup
45
+ i18n_path.gsub!('%{action}', action_name)
46
+ i18n_path.gsub!('%{model}', model_name)
47
+ i18n_path.gsub!('%{nested_model}', nested_model_name) unless nested_model_name.nil?
48
+ i18n_path.gsub!('%{attribute}', attribute_name)
49
+ i18n_path.gsub!('..', '.')
50
+ i18n_path.to_sym
51
+ end
52
+ defaults << ''
53
+
54
+ defaults.uniq!
55
+
56
+ default_key = defaults.shift
57
+ i18n_value = Formtastic::I18n.t(default_key,
58
+ options.merge(:default => defaults, :scope => type.to_s.pluralize.to_sym))
59
+ if i18n_value.blank? && type == :label
60
+ # This is effectively what Rails label helper does for i18n lookup
61
+ options[:scope] = [:helpers, type]
62
+ options[:default] = defaults
63
+ i18n_value = ::I18n.t(default_key, options)
64
+ end
65
+ i18n_value = escape_html_entities(i18n_value) if i18n_value.is_a?(::String)
66
+ i18n_value.blank? ? nil : i18n_value
67
+ end
68
+ end
69
+ end
70
+
71
+ def model_name
72
+ @object.present? ? @object.class.name : @object_name.to_s.classify
73
+ end
74
+
75
+ def normalize_model_name(name)
76
+ if name =~ /(.+)\[(.+)\]/
77
+ [$1, $2]
78
+ else
79
+ [name]
80
+ end
81
+ end
82
+
83
+ def escape_html_entities(string) #:nodoc:
84
+ if (respond_to?(:builder) && builder.escape_html_entities_in_hints_and_labels) ||
85
+ (self.respond_to?(:escape_html_entities_in_hints_and_labels) && escape_html_entities_in_hints_and_labels)
86
+ string = template.escape_once(string) unless string.respond_to?(:html_safe?) && string.html_safe? == true # Acceppt html_safe flag as indicator to skip escaping
87
+ end
88
+ string
89
+ end
90
+
91
+ def i18n_lookups_by_default
92
+ respond_to?(:builder) ? builder.i18n_lookups_by_default : i18n_lookups_by_default
93
+ end
94
+
95
+ end
96
+ end
@@ -0,0 +1,12 @@
1
+ # encoding: utf-8
2
+
3
+ module Formtastic
4
+ # @private
5
+ class Railtie < Rails::Railtie
6
+ initializer 'formtastic.initialize' do
7
+ ActiveSupport.on_load(:action_view) do
8
+ include Formtastic::Helpers::FormHelper
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ module Formtastic
2
+ # Quick hack/shim so that any code expecting the old SemanticFormBuilder class still works.
3
+ # TODO remove from 2.0 with a helpful upgrade path/warning.
4
+ # @private
5
+ class SemanticFormBuilder < Formtastic::FormBuilder
6
+ def initialize(*args)
7
+ ActiveSupport::Deprecation.warn('Formtastic::SemanticFormBuilder has been deprecated in favor of Formtastic::FormBuilder.', caller)
8
+ super
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ # encoding: utf-8
2
+
3
+ # Adapted from the rails3 compatibility shim in Haml 2.2
4
+ module Formtastic
5
+ # @private
6
+ module Util
7
+ extend self
8
+ ## Rails XSS Safety
9
+
10
+ # Returns the given text, marked as being HTML-safe.
11
+ # With older versions of the Rails XSS-safety mechanism,
12
+ # this destructively modifies the HTML-safety of `text`.
13
+ #
14
+ # @param text [String]
15
+ # @return [String] `text`, marked as HTML-safe
16
+ def html_safe(text)
17
+ if text.respond_to?(:html_safe)
18
+ text.html_safe
19
+ else
20
+ text
21
+ end
22
+ end
23
+
24
+ end
25
+ end
@@ -0,0 +1,95 @@
1
+ # encoding: utf-8
2
+ module Formtastic
3
+ # Generates a Formtastic form partial based on an existing model. It will not overwrite existing
4
+ # files without confirmation.
5
+ #
6
+ # @example
7
+ # $ rails generate formtastic:form Post
8
+ # @example Copy the partial code to the pasteboard rather than generating a partial
9
+ # $ rails generate formtastic:form Post --copy
10
+ # @example Return HAML output instead of ERB
11
+ # $ rails generate formtastic:form Post --haml
12
+ # @example Generate a form for specific model attributes
13
+ # $ rails generate formtastic:form Post title:string body:text
14
+ # @example Generate a form for a specific controller
15
+ # $ rails generate formtastic:form Post --controller admin/posts
16
+ class FormGenerator < Rails::Generators::NamedBase
17
+ desc "Generates a Formtastic form partial based on an existing model."
18
+
19
+ argument :name, :type => :string, :required => true, :banner => 'MyExistingModel'
20
+ argument :attributes, :type => :array, :default => [], :banner => 'attribute attribute'
21
+
22
+ class_option :haml, :type => :boolean, :default => false, :group => :formtastic,
23
+ :desc => "Generate HAML instead of ERB"
24
+
25
+ class_option :partial, :type => :boolean, :default => true, :group => :formtastic,
26
+ :desc => 'Generate a form partial in the model views path (eg `posts/_form.html.erb`)'
27
+
28
+ class_option :copy, :type => :boolean, :default => false, :group => :formtastic,
29
+ :desc => 'Copy the generated code the clipboard instead of generating a partial file."'
30
+
31
+ class_option :controller, :type => :string, :default => false, :group => :formtastic,
32
+ :desc => 'Generate for custom controller/view path - in case model and controller namespace is different, i.e. "admin/posts"'
33
+
34
+ source_root File.expand_path('../../../templates', __FILE__)
35
+
36
+ def create_or_show
37
+ @attributes = reflected_attributes if @attributes.empty?
38
+
39
+ if options[:copy]
40
+ template = File.read("#{self.class.source_root}/_form.html.#{template_type}")
41
+ erb = ERB.new(template, nil, '-')
42
+ generated_code = erb.result(binding).strip rescue nil
43
+ puts "The following code has been to the clipboard, just paste it in your views:" if save_to_clipboard(generated_code)
44
+ puts generated_code || "Error: Nothing generated. Does the model exist?"
45
+ else
46
+ empty_directory "app/views/#{controller_path}"
47
+ template "_form.html.#{template_type}", "app/views/#{controller_path}/_form.html.#{template_type}"
48
+ end
49
+ end
50
+
51
+ protected
52
+
53
+ def template_type
54
+ @template_type ||= options[:haml] ? :haml : :erb
55
+ end
56
+
57
+ def controller_path
58
+ @controller_path ||= if options[:controller]
59
+ options[:controller].underscore
60
+ else
61
+ name.underscore.pluralize
62
+ end
63
+ end
64
+
65
+ def reflected_attributes
66
+ columns = model.content_columns.map{|column| column.name}
67
+ columns += model.reflect_on_all_associations.map{|association| association.name.to_s}
68
+ columns -= %w(created_at updated_at)
69
+ end
70
+
71
+ def model
72
+ @model ||= name.camelize.constantize
73
+ end
74
+
75
+ def save_to_clipboard(data)
76
+ return unless data
77
+
78
+ begin
79
+ case RUBY_PLATFORM
80
+ when /win32/
81
+ require 'win32/clipboard'
82
+ ::Win32::Clipboard.data = data
83
+ when /darwin/ # mac
84
+ `echo "#{data}" | pbcopy`
85
+ else # linux/unix
86
+ `echo "#{data}" | xsel --clipboard` || `echo "#{data}" | xclip`
87
+ end
88
+ rescue LoadError
89
+ false
90
+ else
91
+ true
92
+ end
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,23 @@
1
+ # encoding: utf-8
2
+
3
+ module Formtastic
4
+ # Copies formtastic.css and formtastic_changes.css to public/stylesheets/ and a config initializer
5
+ # to config/initializers/formtastic_config.rb.
6
+ #
7
+ # @example
8
+ # $ rails generate formtastic:install
9
+ #
10
+ # @todo Revisit in Rails 3.1 where public assets are treated differently
11
+ class InstallGenerator < Rails::Generators::Base
12
+ desc "Copies formtastic.css and formtastic_changes.css to public/stylesheets/ and a config initializer to config/initializers/formtastic_config.rb"
13
+
14
+ source_root File.expand_path('../../../templates', __FILE__)
15
+
16
+ def copy_files
17
+ template 'formtastic.rb', 'config/initializers/formtastic.rb'
18
+
19
+ template 'formtastic.css', 'public/stylesheets/formtastic.css'
20
+ template 'formtastic_changes.css', 'public/stylesheets/formtastic_changes.css'
21
+ end
22
+ end
23
+ end