Skip to content

Commit 87d6ff9

Browse files
author
mmoyer
committed
merged in upstream changes
2 parents 543dd8c + b5b3d8f commit 87d6ff9

File tree

2 files changed

+187
-53
lines changed

2 files changed

+187
-53
lines changed

lib/formtastic.rb

+43-8
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,15 @@ class SemanticFormBuilder < ActionView::Helpers::FormBuilder
2020
@@inline_order = [ :input, :hints, :errors ]
2121
@@file_methods = [ :file?, :public_filename ]
2222
@@priority_countries = ["Australia", "Canada", "United Kingdom", "United States"]
23+
@@i18n_lookups_by_default = false
2324

2425
cattr_accessor :default_text_field_size, :all_fields_required_by_default, :required_string,
2526
:optional_string, :inline_errors, :label_str_method, :collection_label_methods,
26-
:inline_order, :file_methods, :priority_countries
27+
:inline_order, :file_methods, :priority_countries, :i18n_lookups_by_default
28+
29+
I18N_SCOPES = [ '{{model}}.{{action}}.{{attribute}}',
30+
'{{model}}.{{attribute}}',
31+
'{{attribute}}']
2732

2833
# Keeps simple mappings in a hash
2934
INPUT_MAPPINGS = {
@@ -347,6 +352,7 @@ def label(method, options_or_text=nil, options=nil)
347352
options ||= {}
348353
end
349354

355+
text = localized_attribute_string(method, text, :label)
350356
text ||= humanized_attribute_name(method)
351357
text << required_or_optional_string(options.delete(:required))
352358

@@ -458,7 +464,7 @@ def method_required?(attribute) #:nodoc:
458464
#
459465
def input_simple(type, method, options)
460466
html_options = options.delete(:input_html) || {}
461-
html_options = default_string_options(method).merge(html_options) if STRING_MAPPINGS.include?(type)
467+
html_options = default_string_options(method, type).merge(html_options) if STRING_MAPPINGS.include?(type)
462468

463469
self.label(method, options.slice(:label, :required)) +
464470
self.send(INPUT_MAPPINGS[type], method, html_options)
@@ -907,6 +913,7 @@ def inline_input_for(method, options)
907913
# Generates hints for the given method using the text supplied in :hint.
908914
#
909915
def inline_hints_for(method, options) #:nodoc:
916+
options[:hint] = localized_attribute_string(method, options[:hint], :hint)
910917
return if options[:hint].blank?
911918
template.content_tag(:p, options[:hint], :class => 'inline-hints')
912919
end
@@ -995,8 +1002,6 @@ def field_set_and_list_wrapping_for_method(method, options, contents)
9951002
# default is a :string, a similar behaviour to Rails' scaffolding.
9961003
#
9971004
def default_input_type(method) #:nodoc:
998-
return :string if @object.nil?
999-
10001005
column = @object.column_for_attribute(method) if @object.respond_to?(:column_for_attribute)
10011006

10021007
if column
@@ -1011,10 +1016,13 @@ def default_input_type(method) #:nodoc:
10111016
# otherwise assume the input name will be the same as the column type (eg string_input)
10121017
return column.type
10131018
else
1014-
obj = @object.send(method) if @object.respond_to?(method)
1019+
if @object
1020+
return :select if find_reflection(method)
1021+
1022+
file = @object.send(method) if @object.respond_to?(method)
1023+
return :file if file && @@file_methods.any? { |m| file.respond_to?(m) }
1024+
end
10151025

1016-
return :select if find_reflection(method)
1017-
return :file if obj && @@file_methods.any? { |m| obj.respond_to?(m) }
10181026
return :password if method.to_s =~ /password/
10191027
return :string
10201028
end
@@ -1108,7 +1116,7 @@ def find_reflection(method)
11081116
# Generates default_string_options by retrieving column information from
11091117
# the database.
11101118
#
1111-
def default_string_options(method) #:nodoc:
1119+
def default_string_options(method, type) #:nodoc:
11121120
column = @object.column_for_attribute(method) if @object.respond_to?(:column_for_attribute)
11131121

11141122
if column.nil? || column.limit.nil?
@@ -1166,6 +1174,33 @@ def humanized_attribute_name(method)
11661174
end
11671175
end
11681176

1177+
def localized_attribute_string(attr_name, attr_value, i18n_key)
1178+
if attr_value.is_a?(String)
1179+
attr_value
1180+
else
1181+
use_i18n = attr_value.nil? ? @@i18n_lookups_by_default : attr_value
1182+
if use_i18n
1183+
model_name = @object.class.name.underscore
1184+
action_name = template.params[:action].to_s rescue ''
1185+
attribute_name = attr_name.to_s
1186+
1187+
defaults = I18N_SCOPES.collect do |i18n_scope|
1188+
i18n_path = i18n_scope.dup
1189+
i18n_path.gsub!('{{action}}', action_name)
1190+
i18n_path.gsub!('{{model}}', model_name)
1191+
i18n_path.gsub!('{{attribute}}', attribute_name)
1192+
i18n_path.gsub!('..', '.')
1193+
i18n_path.to_sym
1194+
end
1195+
defaults << ''
1196+
1197+
i18n_value = ::I18n.t(defaults.shift, :default => defaults,
1198+
:scope => "formtastic.#{i18n_key.to_s.pluralize}")
1199+
i18n_value.blank? ? nil : i18n_value
1200+
end
1201+
end
1202+
end
1203+
11691204
end
11701205

11711206
# Wrappers around form_for (etc) with :builder => SemanticFormBuilder.

spec/formtastic_spec.rb

+144-45
Original file line numberDiff line numberDiff line change
@@ -573,13 +573,20 @@ def custom(arg1, arg2, options = {})
573573

574574
describe 'when not provided' do
575575

576-
it 'should default to a string for forms without objects' do
576+
it 'should default to a string for forms without objects unless column is password' do
577577
semantic_form_for(:project, :url => 'http://test.host') do |builder|
578578
concat(builder.input(:anything))
579579
end
580580
output_buffer.should have_tag('form li.string')
581581
end
582582

583+
it 'should default to password for forms without objects if column is password' do
584+
semantic_form_for(:project, :url => 'http://test.host') do |builder|
585+
concat(builder.input(:password))
586+
end
587+
output_buffer.should have_tag('form li.password')
588+
end
589+
583590
it 'should default to a string for methods on objects that don\'t respond to "column_for_attribute"' do
584591
@new_post.stub!(:method_without_a_database_column)
585592
@new_post.stub!(:column_for_attribute).and_return(nil)
@@ -688,45 +695,84 @@ def custom(arg1, arg2, options = {})
688695
end
689696

690697
describe ':label option' do
691-
698+
692699
describe 'when provided' do
693-
694700
it 'should be passed down to the label tag' do
695701
semantic_form_for(@new_post) do |builder|
696702
concat(builder.input(:title, :label => "Kustom"))
697703
end
698704
output_buffer.should have_tag("form li label", /Kustom/)
699705
end
700-
701706
end
702707

703708
describe 'when not provided' do
704-
describe 'and object is not given' do
705-
it 'should default the humanized method name, passing it down to the label tag' do
706-
Formtastic::SemanticFormBuilder.label_str_method = :humanize
707-
708-
semantic_form_for(:project, :url => 'http://test.host') do |builder|
709-
concat(builder.input(:meta_description))
709+
describe 'when localized label is NOT provided' do
710+
describe 'and object is not given' do
711+
it 'should default the humanized method name, passing it down to the label tag' do
712+
Formtastic::SemanticFormBuilder.label_str_method = :humanize
713+
714+
semantic_form_for(:project, :url => 'http://test.host') do |builder|
715+
concat(builder.input(:meta_description))
716+
end
717+
718+
output_buffer.should have_tag("form li label", /#{'meta_description'.humanize}/)
719+
end
720+
end
721+
722+
describe 'and object is given' do
723+
it 'should delegate the label logic to class human attribute name and pass it down to the label tag' do
724+
@new_post.stub!(:meta_description) # a two word method name
725+
@new_post.class.should_receive(:human_attribute_name).with('meta_description').and_return('meta_description'.humanize)
726+
727+
semantic_form_for(@new_post) do |builder|
728+
concat(builder.input(:meta_description))
729+
end
730+
731+
output_buffer.should have_tag("form li label", /#{'meta_description'.humanize}/)
710732
end
711-
712-
output_buffer.should have_tag("form li label", /#{'meta_description'.humanize}/)
713733
end
714734
end
715-
716-
describe 'and object is given' do
717-
it 'should delegate the label logic to class human attribute name and pass it down to the label tag' do
718-
@new_post.stub!(:meta_description) # a two word method name
719-
@new_post.class.should_receive(:human_attribute_name).with('meta_description').and_return('meta_description'.humanize)
720-
735+
736+
describe 'when localized label is provided' do
737+
before do
738+
@localized_label_text = 'Localized title'
739+
@default_localized_label_text = 'Default localized title'
740+
::I18n.backend.store_translations :en,
741+
:formtastic => {
742+
:labels => {
743+
:title => @default_localized_label_text,
744+
:post => {
745+
:title => @localized_label_text
746+
}
747+
}
748+
}
749+
::Formtastic::SemanticFormBuilder.i18n_lookups_by_default = false
750+
end
751+
752+
it 'should render a label with localized label (I18n)' do
721753
semantic_form_for(@new_post) do |builder|
722-
concat(builder.input(:meta_description))
754+
concat(builder.input(:title, :label => true))
723755
end
724-
725-
output_buffer.should have_tag("form li label", /#{'meta_description'.humanize}/)
756+
output_buffer.should have_tag('form li label', @localized_label_text)
757+
end
758+
759+
it 'should render a hint paragraph containing an optional localized label (I18n) if first is not set' do
760+
::I18n.backend.store_translations :en,
761+
:formtastic => {
762+
:labels => {
763+
:post => {
764+
:title => nil
765+
}
766+
}
767+
}
768+
semantic_form_for(@new_post) do |builder|
769+
concat(builder.input(:title, :label => true))
770+
end
771+
output_buffer.should have_tag('form li label', @default_localized_label_text)
726772
end
727773
end
728774
end
729-
775+
730776
end
731777

732778
describe ':hint option' do
@@ -742,12 +788,63 @@ def custom(arg1, arg2, options = {})
742788
end
743789

744790
describe 'when not provided' do
745-
it 'should not render a hint paragraph' do
746-
hint_text = "this is the title of the post"
747-
semantic_form_for(@new_post) do |builder|
748-
concat(builder.input(:title))
791+
describe 'when localized hint (I18n) is provided' do
792+
before do
793+
@localized_hint_text = "This is the localized hint."
794+
@default_localized_hint_text = "This is the default localized hint."
795+
::I18n.backend.store_translations :en,
796+
:formtastic => {
797+
:hints => {
798+
:title => @default_localized_hint_text,
799+
:post => {
800+
:title => @localized_hint_text
801+
}
802+
}
803+
}
804+
::Formtastic::SemanticFormBuilder.i18n_lookups_by_default = false
805+
end
806+
807+
describe 'when provided value (hint value) is set to TRUE' do
808+
it 'should render a hint paragraph containing a localized hint (I18n)' do
809+
semantic_form_for(@new_post) do |builder|
810+
concat(builder.input(:title, :hint => true))
811+
end
812+
output_buffer.should have_tag('form li p.inline-hints', @localized_hint_text)
813+
end
814+
815+
it 'should render a hint paragraph containing an optional localized hint (I18n) if first is not set' do
816+
::I18n.backend.store_translations :en,
817+
:formtastic => {
818+
:hints => {
819+
:post => {
820+
:title => nil
821+
}
822+
}
823+
}
824+
semantic_form_for(@new_post) do |builder|
825+
concat(builder.input(:title, :hint => true))
826+
end
827+
output_buffer.should have_tag('form li p.inline-hints', @default_localized_hint_text)
828+
end
829+
end
830+
831+
describe 'when provided value (label value) is set to FALSE' do
832+
it 'should not render a hint paragraph' do
833+
semantic_form_for(@new_post) do |builder|
834+
concat(builder.input(:title, :hint => false))
835+
end
836+
output_buffer.should_not have_tag('form li p.inline-hints', @localized_hint_text)
837+
end
838+
end
839+
end
840+
841+
describe 'when localized hint (I18n) is not provided' do
842+
it 'should not render a hint paragraph' do
843+
semantic_form_for(@new_post) do |builder|
844+
concat(builder.input(:title))
845+
end
846+
output_buffer.should_not have_tag('form li p.inline-hints')
749847
end
750-
output_buffer.should_not have_tag("form li p.inline-hints")
751848
end
752849
end
753850

@@ -922,31 +1019,33 @@ def custom(arg1, arg2, options = {})
9221019
output_buffer.should have_tag("form li input[@name=\"post[title]\"]")
9231020
end
9241021

925-
it 'should have a maxlength matching the column limit' do
926-
@new_post.column_for_attribute(:title).limit.should == 50
927-
output_buffer.should have_tag("form li input[@maxlength='50']")
928-
end
1022+
unless type == :numeric
1023+
it 'should have a maxlength matching the column limit' do
1024+
@new_post.column_for_attribute(:title).limit.should == 50
1025+
output_buffer.should have_tag("form li input[@maxlength='50']")
1026+
end
9291027

930-
it 'should use default_text_field_size for columns longer than default_text_field_size' do
931-
default_size = Formtastic::SemanticFormBuilder.default_text_field_size
932-
@new_post.stub!(:column_for_attribute).and_return(mock('column', :type => type, :limit => default_size * 2))
1028+
it 'should use default_text_field_size for columns longer than default_text_field_size' do
1029+
default_size = Formtastic::SemanticFormBuilder.default_text_field_size
1030+
@new_post.stub!(:column_for_attribute).and_return(mock('column', :type => type, :limit => default_size * 2))
9331031

934-
semantic_form_for(@new_post) do |builder|
935-
concat(builder.input(:title, :as => type))
1032+
semantic_form_for(@new_post) do |builder|
1033+
concat(builder.input(:title, :as => type))
1034+
end
1035+
1036+
output_buffer.should have_tag("form li input[@size='#{default_size}']")
9361037
end
9371038

938-
output_buffer.should have_tag("form li input[@size='#{default_size}']")
939-
end
1039+
it 'should use the column size for columns shorter than default_text_field_size' do
1040+
column_limit_shorted_than_default = 1
1041+
@new_post.stub!(:column_for_attribute).and_return(mock('column', :type => type, :limit => column_limit_shorted_than_default))
9401042

941-
it 'should use the column size for columns shorter than default_text_field_size' do
942-
column_limit_shorted_than_default = 1
943-
@new_post.stub!(:column_for_attribute).and_return(mock('column', :type => type, :limit => column_limit_shorted_than_default))
1043+
semantic_form_for(@new_post) do |builder|
1044+
concat(builder.input(:title, :as => type))
1045+
end
9441046

945-
semantic_form_for(@new_post) do |builder|
946-
concat(builder.input(:title, :as => type))
1047+
output_buffer.should have_tag("form li input[@size='#{column_limit_shorted_than_default}']")
9471048
end
948-
949-
output_buffer.should have_tag("form li input[@size='#{column_limit_shorted_than_default}']")
9501049
end
9511050

9521051
it 'should use default_text_field_size for methods without database columns' do

0 commit comments

Comments
 (0)