formtastic 2.0.2 → 2.1.0.beta1
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.
- data/.gitignore +4 -1
- data/Appraisals +11 -0
- data/CHANGELOG +2 -2
- data/Gemfile +0 -2
- data/README.textile +183 -151
- data/Rakefile +9 -5
- data/app/assets/stylesheets/formtastic.css +16 -3
- data/formtastic.gemspec +6 -2
- data/gemfiles/rails-3.0.gemfile +7 -0
- data/gemfiles/rails-3.1.gemfile +7 -0
- data/gemfiles/rails-3.2.gemfile +7 -0
- data/lib/formtastic.rb +10 -2
- data/lib/formtastic/actions.rb +11 -0
- data/lib/formtastic/actions/base.rb +156 -0
- data/lib/formtastic/actions/button_action.rb +72 -0
- data/lib/formtastic/actions/buttonish.rb +17 -0
- data/lib/formtastic/actions/input_action.rb +68 -0
- data/lib/formtastic/actions/link_action.rb +87 -0
- data/lib/formtastic/engine.rb +5 -1
- data/lib/formtastic/form_builder.rb +3 -0
- data/lib/formtastic/helpers.rb +2 -1
- data/lib/formtastic/helpers/action_helper.rb +109 -0
- data/lib/formtastic/helpers/actions_helper.rb +168 -0
- data/lib/formtastic/helpers/buttons_helper.rb +22 -9
- data/lib/formtastic/helpers/errors_helper.rb +0 -54
- data/lib/formtastic/helpers/fieldset_wrapper.rb +10 -5
- data/lib/formtastic/helpers/form_helper.rb +2 -2
- data/lib/formtastic/helpers/input_helper.rb +1 -10
- data/lib/formtastic/helpers/inputs_helper.rb +6 -3
- data/lib/formtastic/i18n.rb +3 -2
- data/lib/formtastic/inputs/base.rb +11 -3
- data/lib/formtastic/inputs/base/choices.rb +6 -1
- data/lib/formtastic/inputs/base/collections.rb +36 -13
- data/lib/formtastic/inputs/base/grouped_collections.rb +1 -1
- data/lib/formtastic/inputs/base/numeric.rb +50 -0
- data/lib/formtastic/inputs/base/options.rb +1 -1
- data/lib/formtastic/inputs/base/placeholder.rb +17 -0
- data/lib/formtastic/inputs/base/stringish.rb +2 -7
- data/lib/formtastic/inputs/base/timeish.rb +21 -5
- data/lib/formtastic/inputs/base/validations.rb +1 -1
- data/lib/formtastic/inputs/base/wrapping.rb +10 -3
- data/lib/formtastic/inputs/boolean_input.rb +10 -2
- data/lib/formtastic/inputs/check_boxes_input.rb +18 -9
- data/lib/formtastic/inputs/country_input.rb +2 -2
- data/lib/formtastic/inputs/date_input.rb +19 -1
- data/lib/formtastic/inputs/datetime_input.rb +1 -1
- data/lib/formtastic/inputs/email_input.rb +2 -1
- data/lib/formtastic/inputs/file_input.rb +1 -1
- data/lib/formtastic/inputs/hidden_input.rb +1 -1
- data/lib/formtastic/inputs/number_input.rb +6 -36
- data/lib/formtastic/inputs/password_input.rb +2 -1
- data/lib/formtastic/inputs/phone_input.rb +3 -2
- data/lib/formtastic/inputs/radio_input.rb +1 -1
- data/lib/formtastic/inputs/range_input.rb +8 -32
- data/lib/formtastic/inputs/search_input.rb +2 -1
- data/lib/formtastic/inputs/select_input.rb +56 -28
- data/lib/formtastic/inputs/string_input.rb +3 -1
- data/lib/formtastic/inputs/text_input.rb +5 -4
- data/lib/formtastic/inputs/time_input.rb +4 -8
- data/lib/formtastic/inputs/time_zone_input.rb +1 -1
- data/lib/formtastic/inputs/url_input.rb +2 -1
- data/lib/formtastic/localized_string.rb +6 -94
- data/lib/formtastic/localizer.rb +110 -0
- data/lib/formtastic/version.rb +1 -1
- data/lib/generators/formtastic/form/form_generator.rb +105 -0
- data/lib/generators/templates/_form.html.erb +2 -2
- data/lib/generators/templates/_form.html.haml +4 -4
- data/lib/generators/templates/_form.html.slim +2 -2
- data/lib/generators/templates/formtastic.rb +4 -0
- data/lib/locale/en.yml +2 -0
- data/sample/basic_inputs.html +22 -0
- data/spec/actions/button_action_spec.rb +63 -0
- data/spec/actions/generic_action_spec.rb +484 -0
- data/spec/actions/input_action_spec.rb +59 -0
- data/spec/actions/link_action_spec.rb +92 -0
- data/spec/builder/semantic_fields_for_spec.rb +14 -0
- data/spec/generators/formtastic/form/form_generator_spec.rb +118 -0
- data/spec/generators/formtastic/install/install_generator_spec.rb +47 -0
- data/spec/helpers/action_helper_spec.rb +365 -0
- data/spec/helpers/actions_helper_spec.rb +143 -0
- data/spec/helpers/buttons_helper_spec.rb +39 -23
- data/spec/helpers/commit_button_helper_spec.rb +153 -93
- data/spec/helpers/inputs_helper_spec.rb +14 -0
- data/spec/i18n_spec.rb +11 -0
- data/spec/inputs/boolean_input_spec.rb +31 -2
- data/spec/inputs/check_boxes_input_spec.rb +29 -1
- data/spec/inputs/date_input_spec.rb +95 -0
- data/spec/inputs/datetime_input_spec.rb +49 -0
- data/spec/inputs/email_input_spec.rb +28 -0
- data/spec/inputs/file_input_spec.rb +28 -0
- data/spec/inputs/hidden_input_spec.rb +28 -0
- data/spec/inputs/include_blank_spec.rb +53 -45
- data/spec/inputs/number_input_spec.rb +34 -4
- data/spec/inputs/password_input_spec.rb +28 -0
- data/spec/inputs/phone_input_spec.rb +28 -0
- data/spec/inputs/placeholder_spec.rb +10 -10
- data/spec/inputs/radio_input_spec.rb +51 -6
- data/spec/inputs/range_input_spec.rb +30 -2
- data/spec/inputs/search_input_spec.rb +27 -0
- data/spec/inputs/select_input_spec.rb +52 -6
- data/spec/inputs/string_input_spec.rb +28 -0
- data/spec/inputs/text_input_spec.rb +27 -0
- data/spec/inputs/time_input_spec.rb +67 -1
- data/spec/inputs/time_zone_input_spec.rb +28 -0
- data/spec/inputs/url_input_spec.rb +28 -0
- data/spec/inputs/with_options_spec.rb +43 -0
- data/spec/spec_helper.rb +22 -6
- data/spec/support/custom_macros.rb +6 -134
- data/spec/support/test_environment.rb +0 -1
- metadata +104 -17
- data/lib/formtastic/helpers/semantic_form_helper.rb +0 -11
- data/lib/formtastic/inputs/numeric_input.rb +0 -21
- data/lib/formtastic/railtie.rb +0 -12
- data/lib/formtastic/semantic_form_builder.rb +0 -11
- data/spec/builder/errors_spec.rb +0 -203
- data/spec/inputs/numeric_input_spec.rb +0 -41
|
@@ -25,10 +25,12 @@ module Formtastic
|
|
|
25
25
|
# </fieldset>
|
|
26
26
|
# </form>
|
|
27
27
|
#
|
|
28
|
-
# @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full
|
|
28
|
+
# @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
|
|
29
29
|
class StringInput
|
|
30
30
|
include Base
|
|
31
31
|
include Base::Stringish
|
|
32
|
+
include Base::Placeholder
|
|
33
|
+
|
|
32
34
|
end
|
|
33
35
|
end
|
|
34
36
|
end
|
|
@@ -9,24 +9,25 @@ module Formtastic
|
|
|
9
9
|
#
|
|
10
10
|
# <%= semantic_form_for(@user) do |f| %>
|
|
11
11
|
# <%= f.inputs do %>
|
|
12
|
-
# <%= f.input :first_name, :as => :
|
|
12
|
+
# <%= f.input :first_name, :as => :text %>
|
|
13
13
|
# <% end %>
|
|
14
14
|
# <% end %>
|
|
15
15
|
#
|
|
16
16
|
# <form...>
|
|
17
17
|
# <fieldset>
|
|
18
18
|
# <ol>
|
|
19
|
-
# <li class="
|
|
19
|
+
# <li class="text">
|
|
20
20
|
# <label for="user_first_name">First name</label>
|
|
21
|
-
# <
|
|
21
|
+
# <textarea cols="30" id="user_first_name" name="user[first_name]" rows="20"></textarea>
|
|
22
22
|
# </li>
|
|
23
23
|
# </ol>
|
|
24
24
|
# </fieldset>
|
|
25
25
|
# </form>
|
|
26
26
|
#
|
|
27
|
-
# @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full
|
|
27
|
+
# @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
|
|
28
28
|
class TextInput
|
|
29
29
|
include Base
|
|
30
|
+
include Base::Placeholder
|
|
30
31
|
|
|
31
32
|
def input_html_options
|
|
32
33
|
{
|
|
@@ -5,7 +5,7 @@ module Formtastic
|
|
|
5
5
|
# well, defaulting to `Time.current` if the form object doesn't have a value, much like Rails'
|
|
6
6
|
# own `time_select`.
|
|
7
7
|
#
|
|
8
|
-
# @see Formtastic::Inputs::Timeish Timeish module for
|
|
8
|
+
# @see Formtastic::Inputs::Base::Timeish Timeish module for documentation of date, time and datetime input options.
|
|
9
9
|
class TimeInput
|
|
10
10
|
include Base
|
|
11
11
|
include Base::Timeish
|
|
@@ -15,18 +15,14 @@ module Formtastic
|
|
|
15
15
|
time_fragments
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
-
def value_or_default_value
|
|
19
|
-
value ? value : Time.current
|
|
20
|
-
end
|
|
21
|
-
|
|
22
18
|
def fragment_value(fragment)
|
|
23
|
-
|
|
19
|
+
value ? value.send(fragment) : ""
|
|
24
20
|
end
|
|
25
21
|
|
|
26
22
|
def hidden_fragments
|
|
27
23
|
if !options[:ignore_date]
|
|
28
24
|
date_fragments.map do |fragment|
|
|
29
|
-
template.hidden_field_tag(
|
|
25
|
+
template.hidden_field_tag(hidden_field_name(fragment), fragment_value(fragment), :id => fragment_id(fragment), :disabled => input_html_options[:disabled] )
|
|
30
26
|
end.join.html_safe
|
|
31
27
|
else
|
|
32
28
|
super
|
|
@@ -35,4 +31,4 @@ module Formtastic
|
|
|
35
31
|
|
|
36
32
|
end
|
|
37
33
|
end
|
|
38
|
-
end
|
|
34
|
+
end
|
|
@@ -26,7 +26,7 @@ module Formtastic
|
|
|
26
26
|
# </fieldset>
|
|
27
27
|
# </form>
|
|
28
28
|
#
|
|
29
|
-
# @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full
|
|
29
|
+
# @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
|
|
30
30
|
#
|
|
31
31
|
# @todo document :priority_zones option
|
|
32
32
|
# @todo configurable default :priority_zones?
|
|
@@ -24,10 +24,11 @@ module Formtastic
|
|
|
24
24
|
# </fieldset>
|
|
25
25
|
# </form>
|
|
26
26
|
#
|
|
27
|
-
# @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full
|
|
27
|
+
# @see Formtastic::Helpers::InputsHelper#input InputsHelper#input for full documentation of all possible options.
|
|
28
28
|
class UrlInput
|
|
29
29
|
include Base
|
|
30
30
|
include Base::Stringish
|
|
31
|
+
include Base::Placeholder
|
|
31
32
|
|
|
32
33
|
def to_html
|
|
33
34
|
input_wrapping do
|
|
@@ -1,105 +1,17 @@
|
|
|
1
1
|
module Formtastic
|
|
2
|
-
# @private
|
|
3
2
|
module LocalizedString
|
|
4
3
|
|
|
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
|
-
|
|
39
|
-
action_name = template.params[:action].to_s rescue ''
|
|
40
|
-
attribute_name = key.to_s
|
|
41
|
-
|
|
42
|
-
defaults = Formtastic::I18n::SCOPES.reject do |i18n_scope|
|
|
43
|
-
nested_model_name.nil? && i18n_scope.match(/nested_model/)
|
|
44
|
-
end.collect do |i18n_scope|
|
|
45
|
-
i18n_path = i18n_scope.dup
|
|
46
|
-
i18n_path.gsub!('%{action}', action_name)
|
|
47
|
-
i18n_path.gsub!('%{model}', model_name)
|
|
48
|
-
i18n_path.gsub!('%{nested_model}', nested_model_name) unless nested_model_name.nil?
|
|
49
|
-
i18n_path.gsub!('%{attribute}', attribute_name)
|
|
50
|
-
i18n_path.gsub!('..', '.')
|
|
51
|
-
i18n_path.to_sym
|
|
52
|
-
end
|
|
53
|
-
defaults << ''
|
|
54
|
-
|
|
55
|
-
defaults.uniq!
|
|
56
|
-
|
|
57
|
-
default_key = defaults.shift
|
|
58
|
-
i18n_value = Formtastic::I18n.t(default_key,
|
|
59
|
-
options.merge(:default => defaults, :scope => type.to_s.pluralize.to_sym))
|
|
60
|
-
i18n_value = i18n_value.is_a?(::String) ? i18n_value : nil
|
|
61
|
-
if i18n_value.blank? && type == :label
|
|
62
|
-
# This is effectively what Rails label helper does for i18n lookup
|
|
63
|
-
options[:scope] = [:helpers, type]
|
|
64
|
-
options[:default] = defaults
|
|
65
|
-
i18n_value = ::I18n.t(default_key, options)
|
|
66
|
-
end
|
|
67
|
-
(i18n_value.is_a?(::String) && i18n_value.present?) ? escape_html_entities(i18n_value) : nil
|
|
68
|
-
end
|
|
69
|
-
end
|
|
70
|
-
end
|
|
71
|
-
|
|
72
4
|
def model_name
|
|
73
5
|
@object.present? ? @object.class.name : @object_name.to_s.classify
|
|
74
6
|
end
|
|
75
7
|
|
|
76
|
-
|
|
77
|
-
if !name =~ /\[/ && self.respond_to?(:builder) && builder.respond_to?(:parent_builder) && builder.parent_builder.object_name
|
|
78
|
-
# Rails 3.1 nested builder case
|
|
79
|
-
[builder.parent_builder.object_name.to_s, name]
|
|
80
|
-
elsif name =~ /(.+)\[(.+)\]/
|
|
81
|
-
# Rails 3 (and 3.1?) nested builder case with :post rather than @post
|
|
82
|
-
[$1, $2]
|
|
83
|
-
elsif self.respond_to?(:builder) && builder.respond_to?(:options) && builder.options.key?(:parent_builder)
|
|
84
|
-
# Rails 3.0 nested builder work-around case, where :parent_builder is provided by f.semantic_form_for
|
|
85
|
-
[builder.options[:parent_builder].object_name.to_s, name]
|
|
86
|
-
else
|
|
87
|
-
# Non-nested case
|
|
88
|
-
[name]
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def escape_html_entities(string) #:nodoc:
|
|
93
|
-
if (respond_to?(:builder) && builder.escape_html_entities_in_hints_and_labels) ||
|
|
94
|
-
(self.respond_to?(:escape_html_entities_in_hints_and_labels) && escape_html_entities_in_hints_and_labels)
|
|
95
|
-
string = template.escape_once(string) unless string.respond_to?(:html_safe?) && string.html_safe? == true # Acceppt html_safe flag as indicator to skip escaping
|
|
96
|
-
end
|
|
97
|
-
string
|
|
98
|
-
end
|
|
8
|
+
protected
|
|
99
9
|
|
|
100
|
-
def
|
|
101
|
-
respond_to?(:builder) ? builder
|
|
10
|
+
def localized_string(key, value, type, options = {}) #:nodoc:
|
|
11
|
+
current_builder = respond_to?(:builder) ? builder : self
|
|
12
|
+
localizer = Formtastic::FormBuilder.i18n_localizer.new(current_builder)
|
|
13
|
+
localizer.localize(key, value, type, options)
|
|
102
14
|
end
|
|
103
15
|
|
|
104
16
|
end
|
|
105
|
-
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
module Formtastic
|
|
2
|
+
# Implementation for looking up localized values within Formtastic using I18n, if no
|
|
3
|
+
# explicit value (like the `:label` option) is set and I18n-lookups are enabled in the
|
|
4
|
+
# configuration.
|
|
5
|
+
#
|
|
6
|
+
# You can subclass this to implement your own Localizer, and configure Formtastic to use this
|
|
7
|
+
# localizer with:
|
|
8
|
+
#
|
|
9
|
+
# Formtastic::FormBuilder.i18n_localizer
|
|
10
|
+
#
|
|
11
|
+
# Enabled/disable i18n lookups completely with:
|
|
12
|
+
#
|
|
13
|
+
# Formtastic::FormBuilder.i18n_lookups_by_default = true/false
|
|
14
|
+
#
|
|
15
|
+
# Lookup priority:
|
|
16
|
+
#
|
|
17
|
+
# 'formtastic.%{type}.%{model}.%{action}.%{attribute}'
|
|
18
|
+
# 'formtastic.%{type}.%{model}.%{attribute}'
|
|
19
|
+
# 'formtastic.%{type}.%{attribute}'
|
|
20
|
+
#
|
|
21
|
+
# Example:
|
|
22
|
+
#
|
|
23
|
+
# 'formtastic.labels.post.edit.title'
|
|
24
|
+
# 'formtastic.labels.post.title'
|
|
25
|
+
# 'formtastic.labels.title'
|
|
26
|
+
class Localizer
|
|
27
|
+
|
|
28
|
+
attr_accessor :builder
|
|
29
|
+
|
|
30
|
+
def initialize(current_builder)
|
|
31
|
+
self.builder = current_builder
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def localize(key, value, type, options = {}) #:nodoc:
|
|
35
|
+
key = value if value.is_a?(::Symbol)
|
|
36
|
+
|
|
37
|
+
if value.is_a?(::String)
|
|
38
|
+
escape_html_entities(value)
|
|
39
|
+
else
|
|
40
|
+
use_i18n = value.nil? ? i18n_lookups_by_default : (value != false)
|
|
41
|
+
|
|
42
|
+
if use_i18n
|
|
43
|
+
model_name, nested_model_name = normalize_model_name(builder.model_name.underscore)
|
|
44
|
+
|
|
45
|
+
action_name = builder.template.params[:action].to_s rescue ''
|
|
46
|
+
attribute_name = key.to_s
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
defaults = Formtastic::I18n::SCOPES.reject do |i18n_scope|
|
|
50
|
+
nested_model_name.nil? && i18n_scope.match(/nested_model/)
|
|
51
|
+
end.collect do |i18n_scope|
|
|
52
|
+
i18n_path = i18n_scope.dup
|
|
53
|
+
i18n_path.gsub!('%{action}', action_name)
|
|
54
|
+
i18n_path.gsub!('%{model}', model_name)
|
|
55
|
+
i18n_path.gsub!('%{nested_model}', nested_model_name) unless nested_model_name.nil?
|
|
56
|
+
i18n_path.gsub!('%{attribute}', attribute_name)
|
|
57
|
+
i18n_path.gsub!('..', '.')
|
|
58
|
+
i18n_path.to_sym
|
|
59
|
+
end
|
|
60
|
+
defaults << ''
|
|
61
|
+
|
|
62
|
+
defaults.uniq!
|
|
63
|
+
|
|
64
|
+
default_key = defaults.shift
|
|
65
|
+
i18n_value = Formtastic::I18n.t(default_key,
|
|
66
|
+
options.merge(:default => defaults, :scope => type.to_s.pluralize.to_sym))
|
|
67
|
+
i18n_value = i18n_value.is_a?(::String) ? i18n_value : nil
|
|
68
|
+
if i18n_value.blank? && type == :label
|
|
69
|
+
# This is effectively what Rails label helper does for i18n lookup
|
|
70
|
+
options[:scope] = [:helpers, type]
|
|
71
|
+
options[:default] = defaults
|
|
72
|
+
i18n_value = ::I18n.t(default_key, options)
|
|
73
|
+
end
|
|
74
|
+
(i18n_value.is_a?(::String) && i18n_value.present?) ? escape_html_entities(i18n_value) : nil
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
protected
|
|
80
|
+
|
|
81
|
+
def normalize_model_name(name)
|
|
82
|
+
if !name =~ /\[/ && builder.respond_to?(:parent_builder) && builder.parent_builder.object_name
|
|
83
|
+
# Rails 3.1 nested builder case
|
|
84
|
+
[builder.parent_builder.object_name.to_s, name]
|
|
85
|
+
elsif name =~ /(.+)\[(.+)\]/
|
|
86
|
+
# Rails 3 (and 3.1?) nested builder case with :post rather than @post
|
|
87
|
+
[$1, $2]
|
|
88
|
+
elsif builder.respond_to?(:options) && builder.options.key?(:parent_builder)
|
|
89
|
+
# Rails 3.0 nested builder work-around case, where :parent_builder is provided by f.semantic_form_for
|
|
90
|
+
[builder.options[:parent_builder].object_name.to_s, name]
|
|
91
|
+
else
|
|
92
|
+
# Non-nested case
|
|
93
|
+
[name]
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def escape_html_entities(string) #:nodoc:
|
|
98
|
+
if (builder.escape_html_entities_in_hints_and_labels) ||
|
|
99
|
+
(self.respond_to?(:escape_html_entities_in_hints_and_labels) && escape_html_entities_in_hints_and_labels)
|
|
100
|
+
string = builder.template.escape_once(string) unless string.respond_to?(:html_safe?) && string.html_safe? == true # Accept html_safe flag as indicator to skip escaping
|
|
101
|
+
end
|
|
102
|
+
string
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def i18n_lookups_by_default
|
|
106
|
+
builder.i18n_lookups_by_default
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
end
|
|
110
|
+
end
|
data/lib/formtastic/version.rb
CHANGED
|
@@ -0,0 +1,105 @@
|
|
|
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 default template engine
|
|
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 => 'MODEL_NAME'
|
|
20
|
+
argument :attributes, :type => :array, :default => [], :banner => 'attribute attribute'
|
|
21
|
+
|
|
22
|
+
source_root File.expand_path('../../../templates', __FILE__)
|
|
23
|
+
|
|
24
|
+
class_option :template_engine
|
|
25
|
+
|
|
26
|
+
class_option :copy, :type => :boolean, :default => false, :group => :formtastic,
|
|
27
|
+
:desc => 'Copy the generated code the clipboard instead of generating a partial file."'
|
|
28
|
+
|
|
29
|
+
class_option :controller, :type => :string, :default => false, :group => :formtastic,
|
|
30
|
+
:desc => 'Generate for custom controller/view path - in case model and controller namespace is different, i.e. "admin/posts"'
|
|
31
|
+
|
|
32
|
+
def create_or_show
|
|
33
|
+
@attributes = reflected_attributes if @attributes.empty?
|
|
34
|
+
|
|
35
|
+
engine = options[:template_engine]
|
|
36
|
+
|
|
37
|
+
if options[:copy]
|
|
38
|
+
template = File.read("#{self.class.source_root}/_form.html.#{engine}")
|
|
39
|
+
erb = ERB.new(template, nil, '-')
|
|
40
|
+
generated_code = erb.result(binding).strip rescue nil
|
|
41
|
+
puts "The following code has been copied to the clipboard, just paste it in your views:" if save_to_clipboard(generated_code)
|
|
42
|
+
puts generated_code || "Error: Nothing generated. Does the model exist?"
|
|
43
|
+
else
|
|
44
|
+
empty_directory "app/views/#{controller_path}"
|
|
45
|
+
template "_form.html.#{engine}", "app/views/#{controller_path}/_form.html.#{engine}"
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
protected
|
|
50
|
+
|
|
51
|
+
def controller_path
|
|
52
|
+
@controller_path ||= if options[:controller]
|
|
53
|
+
options[:controller].underscore
|
|
54
|
+
else
|
|
55
|
+
name.underscore.pluralize
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def reflected_attributes
|
|
60
|
+
columns = content_columns
|
|
61
|
+
columns += association_columns
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def model
|
|
65
|
+
@model ||= name.camelize.constantize
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Collects content columns (non-relation columns) for the current class.
|
|
69
|
+
# Skips Active Record Timestamps.
|
|
70
|
+
def content_columns
|
|
71
|
+
model.content_columns.select do |column|
|
|
72
|
+
!Formtastic::Helpers::InputsHelper::SKIPPED_COLUMNS.include? column.name.to_sym
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Collects association columns (relation columns) for the current class. Skips polymorphic
|
|
77
|
+
# associations because we can't guess which class to use for an automatically generated input.
|
|
78
|
+
def association_columns
|
|
79
|
+
model.reflect_on_all_associations(:belongs_to).select do |association_reflection|
|
|
80
|
+
association_reflection.options[:polymorphic] != true
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def save_to_clipboard(data)
|
|
85
|
+
return unless data
|
|
86
|
+
|
|
87
|
+
begin
|
|
88
|
+
case RUBY_PLATFORM
|
|
89
|
+
when /win32/
|
|
90
|
+
require 'win32/clipboard'
|
|
91
|
+
::Win32::Clipboard.data = data
|
|
92
|
+
when /darwin/ # mac
|
|
93
|
+
`echo "#{data}" | pbcopy`
|
|
94
|
+
else # linux/unix
|
|
95
|
+
`echo "#{data}" | xsel --clipboard` || `echo "#{data}" | xclip`
|
|
96
|
+
end
|
|
97
|
+
rescue LoadError
|
|
98
|
+
false
|
|
99
|
+
else
|
|
100
|
+
true
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
end
|
|
105
|
+
end
|