justinfrench-formtastic 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -72,6 +72,11 @@ h2. Opinions
72
72
  * make the common things we do easy, yet still ensure uncommon things are still possible
73
73
 
74
74
 
75
+ h2. Documentation
76
+
77
+ RDoc documentation _should_ be automatically generated after each commit and made available on the "rdoc.info website":http://rdoc.info/projects/justinfrench/formtastic.
78
+
79
+
75
80
  h2. Installation
76
81
 
77
82
  You can (and should) get it as a gem:
@@ -245,6 +250,123 @@ h2. The Available Inputs
245
250
 
246
251
  The documentation is pretty good for each of these (what it does, what the output is, what the options are, etc) so go check it out.
247
252
 
253
+ h2. Internationalization (I18n)
254
+
255
+ Formtastic got some neat I18n-features. ActiveRecord object names and attributes are, by default, taken from calling @object.human_name and @object.human_attribute_name(attr) respectively. There are a few words specific to Formtastic that can be translated. See lib/locale/en.yml for more information.
256
+
257
+ h3. Label/Hint-localization
258
+
259
+ Formtastic supports localized *labels* and *hints* using the I18n API for more advanced usage. Your forms can now be DRYer and more flexible than ever, and still fully localized. This is how:
260
+
261
+ Basic localization (labels only):
262
+
263
+ <pre>
264
+ <% semantic_form_for @post do |form| %>
265
+ <%= form.input :title %> # => :label => I18n.t('activerecord.attributes.user.title') or 'Title'
266
+ <%= form.input :body %> # => :label => I18n.t('activerecord.attributes.user.body') or 'Body'
267
+ <%= form.input :section %> # => :label => I18n.t('activerecord.attributes.user.section') or 'Section'
268
+ <% end %>
269
+ </pre>
270
+
271
+ *Note:* This is perfectly fine if you just want your labels to be translated using *ActiveRecord I18n attribute translations*, and you don't use input hints. But what if you do? And what if you don't want same labels in all forms?
272
+
273
+ Enhanced localization (labels and hints):
274
+
275
+ 1. Enable I18n lookups by default (@config/initializers/formtastic.rb@):
276
+
277
+ <pre>
278
+ Formtastic::SemanticFormBuilder.i18n_lookups_by_default = true
279
+ </pre>
280
+
281
+ 2. Add some cool label-translations/variants (@config/locale/en.yml@):
282
+
283
+ <pre>
284
+ en:
285
+ formtastic:
286
+ labels:
287
+ post:
288
+ title: "Choose a title..."
289
+ body: "Write something..."
290
+ hints:
291
+ post:
292
+ title: "Choose a good title for you post."
293
+ body: "Write something inspiring here."
294
+ </pre>
295
+
296
+ *Note:* We are using English here still, but you get the point.
297
+
298
+ 3. ...and now you'll get:
299
+
300
+ <pre>
301
+ <% semantic_form_for @post do |form| %>
302
+ <%= form.input :title %> # => :label => "Choose a title...", :hint => "Choose a good title for you post."
303
+ <%= form.input :body %> # => :label => "Write something...", :hint => "Write something inspiring here."
304
+ <%= form.input :section %> # => :label => I18n.t('activerecord.attributes.user.section') or 'Section'
305
+ <% end %>
306
+ </pre>
307
+
308
+ 4. Override I18n settings:
309
+
310
+ <pre>
311
+ <% semantic_form_for @post do |form| %>
312
+ <%= form.input :title %> # => :label => "Choose a title...", :hint => "Choose a good title for you post."
313
+ <%= form.input :body, :hint => false %> # => :label => "Write something..."
314
+ <%= form.input :section %> # => :label => I18n.t('activerecord.attributes.user.section') or 'Section'
315
+ <% end %>
316
+ </pre>
317
+
318
+ If I18n-lookups is disabled, i.e.:
319
+
320
+ <pre>
321
+ Formtastic::SemanticFormBuilder.i18n_lookups_by_default = false
322
+ </pre>
323
+
324
+ ...then you can enable I18n within the forms instead:
325
+
326
+ <pre>
327
+ <% semantic_form_for @post do |form| %>
328
+ <%= form.input :title, :label => true %> # => :label => "Choose a title..."
329
+ <%= form.input :body, :label => true %> # => :label => "Write something..."
330
+ <%= form.input :section, :label => true %> # => :label => I18n.t('activerecord.attributes.user.section') or 'Section'
331
+ <% end %>
332
+ </pre>
333
+
334
+ 5. Advanced I18n lookups
335
+
336
+ For more flexible forms; Formtastic find translations using a bottom-up approach taking the following variables in account:
337
+
338
+ * @model@, e.g. "post"
339
+ * @action@, e.g. "edit"
340
+ * @attribute@, e.g. "title"
341
+
342
+ ...in the following order:
343
+
344
+ 1. @formtastic.{labels,hints}.MODEL.ACTION.ATTRIBUTE@ # By model and action
345
+ 2. @formtastic.{labels,hints}.MODEL.ATTRIBUTE@ # By model
346
+ 3. @formtastic.{labels,hints}.ATTRIBUTE@ # Global default
347
+
348
+ ...which means that you can define translations like this:
349
+
350
+ <pre>
351
+ en:
352
+ formtastic:
353
+ labels:
354
+ title: "Title" # Default global value
355
+ article:
356
+ body: "Article content"
357
+ post:
358
+ new:
359
+ title: "Choose a title..."
360
+ body: "Write something..."
361
+ edit:
362
+ title: "Edit title"
363
+ body: "Edit body"
364
+ ...
365
+ </pre>
366
+
367
+ h2. ValidationReflection plugin
368
+
369
+ If you have the "ValidationReflection":http://github.com/redinger/validation_reflection plugin installed, you won't have to specify the :required option (it checks the validations on the model instead).
248
370
 
249
371
  h2. Configuration
250
372
 
@@ -291,19 +413,13 @@ If you wish, put something like this in config/initializers/formtastic_config.rb
291
413
 
292
414
  # Set the default "priority countries" to suit your user base when using :as => :country
293
415
  Formtastic::SemanticFormBuilder.priority_countries = ["Australia", "New Zealand"]
416
+
417
+ # Specifies if labels/hints for input fields automatically be looked up using I18n.
418
+ # Default value: false. Overridden for specific fields by setting value to true,
419
+ # i.e. :label => true, or :hint => true (or opposite depending on initialized value)
420
+ # Formtastic::SemanticFormBuilder.i18n_lookups_by_default = false
294
421
  </pre>
295
422
 
296
-
297
- h2. Internationalization (I18n)
298
-
299
- Supports I18n! ActiveRecord object names and attributes are, by default, taken from calling @object.human_name and @object.human_attribute_name(attr) respectively. There are a few words specific to Formtastic that can be translated. See lib/locale/en.yml for more information.
300
-
301
-
302
- h2. ValidationReflection plugin
303
-
304
- If you have the "ValidationReflection":http://github.com/redinger/validation_reflection plugin installed, you won't have to specify the :required option (it checks the validations on the model instead).
305
-
306
-
307
423
  h2. Status
308
424
 
309
425
  *THINGS ARE GOING TO CHANGE A BIT BEFORE WE HIT 1.0.*
@@ -83,6 +83,7 @@ form.formtastic fieldset ol li ul.errors li { padding:0; border:none; display:li
83
83
  /* STRING & NUMERIC OVERRIDES
84
84
  --------------------------------------------------------------------------------------------------*/
85
85
  form.formtastic fieldset ol li.string input { width:74%; }
86
+ form.formtastic fieldset ol li.password input { width:74%; }
86
87
  form.formtastic fieldset ol li.numeric input { width:74%; }
87
88
 
88
89
 
data/lib/formtastic.rb CHANGED
@@ -19,10 +19,15 @@ module Formtastic #:nodoc:
19
19
  @@inline_order = [ :input, :hints, :errors ]
20
20
  @@file_methods = [ :file?, :public_filename ]
21
21
  @@priority_countries = ["Australia", "Canada", "United Kingdom", "United States"]
22
+ @@i18n_lookups_by_default = false
22
23
 
23
24
  cattr_accessor :default_text_field_size, :all_fields_required_by_default, :required_string,
24
25
  :optional_string, :inline_errors, :label_str_method, :collection_label_methods,
25
- :inline_order, :file_methods, :priority_countries
26
+ :inline_order, :file_methods, :priority_countries, :i18n_lookups_by_default
27
+
28
+ I18N_SCOPES = [ '{{model}}.{{action}}.{{attribute}}',
29
+ '{{model}}.{{attribute}}',
30
+ '{{attribute}}']
26
31
 
27
32
  # Keeps simple mappings in a hash
28
33
  INPUT_MAPPINGS = {
@@ -85,7 +90,7 @@ module Formtastic #:nodoc:
85
90
  options[:as] ||= default_input_type(method)
86
91
 
87
92
  html_class = [ options[:as], (options[:required] ? :required : :optional) ]
88
- html_class << 'error' if @object && @object.respond_to?(:errors) && @object.errors.on(method.to_s)
93
+ html_class << 'error' if @object && @object.respond_to?(:errors) && @object.errors[method.to_sym]
89
94
 
90
95
  wrapper_html = options.delete(:wrapper_html) || {}
91
96
  wrapper_html[:id] ||= generate_html_id(method)
@@ -95,7 +100,15 @@ module Formtastic #:nodoc:
95
100
  ::ActiveSupport::Deprecation.warn(":as => :#{options[:as]} is deprecated, use :as => :#{options[:as].to_s[8..-1]} instead", caller[3..-1])
96
101
  end
97
102
 
98
- list_item_content = @@inline_order.map do |type|
103
+ if options[:input_html] && options[:input_html][:id]
104
+ options[:label_html] ||= {}
105
+ options[:label_html][:for] ||= options[:input_html][:id]
106
+ end
107
+
108
+ input_parts = @@inline_order.dup
109
+ input_parts.delete(:errors) if options[:as] == :hidden
110
+
111
+ list_item_content = input_parts.map do |type|
99
112
  send(:"inline_#{type}_for", method, options)
100
113
  end.compact.join("\n")
101
114
 
@@ -325,6 +338,8 @@ module Formtastic #:nodoc:
325
338
  # * :label - An alternative form to give the label content. Whenever label
326
339
  # is false, a blank string is returned.
327
340
  # * :as_span - When true returns a span tag with class label instead of a label element
341
+ # * :input_name - Gives the input to match for. This is needed when you want to
342
+ # to call f.label :authors but it should match :author_ids.
328
343
  #
329
344
  # == Examples
330
345
  #
@@ -337,23 +352,23 @@ module Formtastic #:nodoc:
337
352
  #
338
353
  def label(method, options_or_text=nil, options=nil)
339
354
  if options_or_text.is_a?(Hash)
340
- return if options_or_text[:label] == false
341
-
355
+ return "" if options_or_text[:label] == false
342
356
  options = options_or_text
343
- text = options.delete(:label)
357
+ text = options.delete(:label)
344
358
  else
345
- text = options_or_text
359
+ text = options_or_text
346
360
  options ||= {}
347
361
  end
348
362
 
349
- text ||= humanized_attribute_name(method)
350
- text << required_or_optional_string(options.delete(:required))
363
+ text = localized_attribute_string(method, text, :label) || humanized_attribute_name(method)
364
+ text += required_or_optional_string(options.delete(:required))
351
365
 
366
+ input_name = options.delete(:input_name) || method
352
367
  if options.delete(:as_span)
353
368
  options[:class] ||= 'label'
354
369
  template.content_tag(:span, text, options)
355
370
  else
356
- super(method, text, options)
371
+ super(input_name, text, options)
357
372
  end
358
373
  end
359
374
 
@@ -371,13 +386,19 @@ module Formtastic #:nodoc:
371
386
  def inline_errors_for(method, options=nil) #:nodoc:
372
387
  return nil unless @object && @object.respond_to?(:errors) && [:sentence, :list].include?(@@inline_errors)
373
388
 
374
- errors = @object.errors.on(method.to_s)
389
+ errors = @object.errors[method.to_sym]
375
390
  send("error_#{@@inline_errors}", Array(errors)) unless errors.blank?
376
391
  end
377
392
  alias :errors_on :inline_errors_for
378
393
 
379
394
  protected
380
395
 
396
+ # Prepare options to be sent to label
397
+ #
398
+ def options_for_label(options)
399
+ options.slice(:label, :required).merge!(options.fetch(:label_html, {}))
400
+ end
401
+
381
402
  # Deals with :for option when it's supplied to inputs methods. Additional
382
403
  # options to be passed down to :for should be supplied using :for_options
383
404
  # key.
@@ -457,9 +478,9 @@ module Formtastic #:nodoc:
457
478
  #
458
479
  def input_simple(type, method, options)
459
480
  html_options = options.delete(:input_html) || {}
460
- html_options = default_string_options(method).merge(html_options) if STRING_MAPPINGS.include?(type)
481
+ html_options = default_string_options(method, type).merge(html_options) if STRING_MAPPINGS.include?(type)
461
482
 
462
- self.label(method, options.slice(:label, :required)) +
483
+ self.label(method, options_for_label(options)) +
463
484
  self.send(INPUT_MAPPINGS[type], method, html_options)
464
485
  end
465
486
 
@@ -510,10 +531,10 @@ module Formtastic #:nodoc:
510
531
  # </select>
511
532
  #
512
533
  #
513
- # You can customize the options available in the select by passing in a collection (Array) of
514
- # ActiveRecord objects through the :collection option. If not provided, the choices are found
515
- # by inferring the parent's class name from the method name and simply calling find(:all) on
516
- # it (VehicleOwner.find(:all) in the example above).
534
+ # You can customize the options available in the select by passing in a collection (an Array or
535
+ # Hash) through the :collection option. If not provided, the choices are found by inferring the
536
+ # parent's class name from the method name and simply calling find(:all) on it
537
+ # (VehicleOwner.find(:all) in the example above).
517
538
  #
518
539
  # Examples:
519
540
  #
@@ -521,6 +542,7 @@ module Formtastic #:nodoc:
521
542
  # f.input :author, :collection => Author.find(:all)
522
543
  # f.input :author, :collection => [@justin, @kate]
523
544
  # f.input :author, :collection => {@justin.name => @justin.id, @kate.name => @kate.id}
545
+ # f.input :author, :collection => ["Justin", "Kate", "Amelia", "Gus", "Meg"]
524
546
  #
525
547
  # Note: This input looks for a label method in the parent association.
526
548
  #
@@ -565,12 +587,13 @@ module Formtastic #:nodoc:
565
587
 
566
588
  reflection = find_reflection(method)
567
589
  if reflection && [ :has_many, :has_and_belongs_to_many ].include?(reflection.macro)
590
+ options[:include_blank] = false
568
591
  html_options[:multiple] ||= true
569
592
  html_options[:size] ||= 5
570
593
  end
571
594
 
572
595
  input_name = generate_association_input_name(method)
573
- self.label(input_name, options.slice(:label, :required)) +
596
+ self.label(method, options_for_label(options).merge(:input_name => input_name)) +
574
597
  self.select(input_name, collection, set_options(options), html_options)
575
598
  end
576
599
  alias :boolean_select_input :select_input
@@ -585,7 +608,7 @@ module Formtastic #:nodoc:
585
608
  def time_zone_input(method, options)
586
609
  html_options = options.delete(:input_html) || {}
587
610
 
588
- self.label(method, options.slice(:label, :required)) +
611
+ self.label(method, options_for_label(options)) +
589
612
  self.time_zone_select(method, options.delete(:priority_zones), set_options(options), html_options)
590
613
  end
591
614
 
@@ -611,16 +634,17 @@ module Formtastic #:nodoc:
611
634
  # </ol>
612
635
  # </fieldset>
613
636
  #
614
- # You can customize the options available in the set by passing in a collection (Array) of
615
- # ActiveRecord objects through the :collection option. If not provided, the choices are found
616
- # by inferring the parent's class name from the method name and simply calling find(:all) on
617
- # it (Author.find(:all) in the example above).
637
+ # You can customize the options available in the select by passing in a collection (an Array or
638
+ # Hash) through the :collection option. If not provided, the choices are found by inferring the
639
+ # parent's class name from the method name and simply calling find(:all) on it
640
+ # (Author.find(:all) in the example above).
618
641
  #
619
642
  # Examples:
620
643
  #
621
644
  # f.input :author, :as => :radio, :collection => @authors
622
645
  # f.input :author, :as => :radio, :collection => Author.find(:all)
623
646
  # f.input :author, :as => :radio, :collection => [@justin, @kate]
647
+ # f.input :author, :collection => ["Justin", "Kate", "Amelia", "Gus", "Meg"]
624
648
  #
625
649
  # You can also customize the text label inside each option tag, by naming the correct method
626
650
  # (:full_name, :display_name, :account_number, etc) to call on each object in the collection
@@ -865,7 +889,7 @@ module Formtastic #:nodoc:
865
889
  html_options = options.delete(:input_html) || {}
866
890
  priority_countries = options.delete(:priority_countries) || @@priority_countries
867
891
 
868
- self.label(method, options.slice(:label, :required)) +
892
+ self.label(method, options_for_label(options)) +
869
893
  self.country_select(method, priority_countries, set_options(options), html_options)
870
894
  end
871
895
 
@@ -881,7 +905,7 @@ module Formtastic #:nodoc:
881
905
  options.delete(:checked_value) || '1', options.delete(:unchecked_value) || '0')
882
906
 
883
907
  label = options.delete(:label) || humanized_attribute_name(method)
884
- self.label(method, input + label, options.slice(:required))
908
+ self.label(method, input + label, options_for_label(options))
885
909
  end
886
910
 
887
911
  # Generates an input for the given method using the type supplied with :as.
@@ -904,6 +928,7 @@ module Formtastic #:nodoc:
904
928
  # Generates hints for the given method using the text supplied in :hint.
905
929
  #
906
930
  def inline_hints_for(method, options) #:nodoc:
931
+ options[:hint] = localized_attribute_string(method, options[:hint], :hint)
907
932
  return if options[:hint].blank?
908
933
  template.content_tag(:p, options[:hint], :class => 'inline-hints')
909
934
  end
@@ -978,7 +1003,7 @@ module Formtastic #:nodoc:
978
1003
  contents = contents.join if contents.respond_to?(:join)
979
1004
 
980
1005
  template.content_tag(:fieldset,
981
- %{<legend>#{self.label(method, options.slice(:label, :required).merge!(:as_span => true))}</legend>} +
1006
+ %{<legend>#{self.label(method, options_for_label(options).merge!(:as_span => true))}</legend>} +
982
1007
  template.content_tag(:ol, contents)
983
1008
  )
984
1009
  end
@@ -992,8 +1017,6 @@ module Formtastic #:nodoc:
992
1017
  # default is a :string, a similar behaviour to Rails' scaffolding.
993
1018
  #
994
1019
  def default_input_type(method) #:nodoc:
995
- return :string if @object.nil?
996
-
997
1020
  column = @object.column_for_attribute(method) if @object.respond_to?(:column_for_attribute)
998
1021
 
999
1022
  if column
@@ -1008,10 +1031,13 @@ module Formtastic #:nodoc:
1008
1031
  # otherwise assume the input name will be the same as the column type (eg string_input)
1009
1032
  return column.type
1010
1033
  else
1011
- obj = @object.send(method) if @object.respond_to?(method)
1034
+ if @object
1035
+ return :select if find_reflection(method)
1036
+
1037
+ file = @object.send(method) if @object.respond_to?(method)
1038
+ return :file if file && @@file_methods.any? { |m| file.respond_to?(m) }
1039
+ end
1012
1040
 
1013
- return :select if find_reflection(method)
1014
- return :file if obj && @@file_methods.any? { |m| obj.respond_to?(m) }
1015
1041
  return :password if method.to_s =~ /password/
1016
1042
  return :string
1017
1043
  end
@@ -1073,13 +1099,14 @@ module Formtastic #:nodoc:
1073
1099
  options[:false] ||= I18n.t('no', :default => 'No', :scope => [:formtastic])
1074
1100
  options[:value_as_class] = true unless options.key?(:value_as_class)
1075
1101
 
1076
- { options.delete(:true) => true, options.delete(:false) => false }
1102
+ [ [ options.delete(:true), true], [ options.delete(:false), false ] ]
1077
1103
  end
1078
1104
 
1079
1105
  # Used by association inputs (select, radio) to generate the name that should
1080
1106
  # be used for the input
1081
1107
  #
1082
1108
  # belongs_to :author; f.input :author; will generate 'author_id'
1109
+ # belongs_to :entity, :foreign_key = :owner_id; f.input :author; will generate 'owner_id'
1083
1110
  # has_many :authors; f.input :authors; will generate 'author_ids'
1084
1111
  # has_and_belongs_to_many will act like has_many
1085
1112
  #
@@ -1088,7 +1115,7 @@ module Formtastic #:nodoc:
1088
1115
  if [:has_and_belongs_to_many, :has_many].include?(reflection.macro)
1089
1116
  "#{method.to_s.singularize}_ids"
1090
1117
  else
1091
- "#{method}_id"
1118
+ reflection.options[:foreign_key] || "#{method}_id"
1092
1119
  end
1093
1120
  else
1094
1121
  method
@@ -1105,10 +1132,10 @@ module Formtastic #:nodoc:
1105
1132
  # Generates default_string_options by retrieving column information from
1106
1133
  # the database.
1107
1134
  #
1108
- def default_string_options(method) #:nodoc:
1135
+ def default_string_options(method, type) #:nodoc:
1109
1136
  column = @object.column_for_attribute(method) if @object.respond_to?(:column_for_attribute)
1110
1137
 
1111
- if column.nil? || column.limit.nil?
1138
+ if type == :numeric || column.nil? || column.limit.nil?
1112
1139
  { :size => @@default_text_field_size }
1113
1140
  else
1114
1141
  { :maxlength => column.limit, :size => [column.limit, @@default_text_field_size].min }
@@ -1128,8 +1155,8 @@ module Formtastic #:nodoc:
1128
1155
  else
1129
1156
  index = ""
1130
1157
  end
1131
- sanitized_method_name = method_name.to_s.sub(/\?$/,"")
1132
-
1158
+ sanitized_method_name = method_name.to_s.gsub(/[\?\/\-]$/, '')
1159
+
1133
1160
  "#{sanitized_object_name}#{index}_#{sanitized_method_name}_#{value}"
1134
1161
  end
1135
1162
 
@@ -1161,6 +1188,54 @@ module Formtastic #:nodoc:
1161
1188
  end
1162
1189
  end
1163
1190
 
1191
+ # Internal generic method for looking up localized values within Formtastic
1192
+ # using I18n, if no explicit value is set and I18n-lookups are enabled.
1193
+ #
1194
+ # Enabled/Disable this by setting:
1195
+ #
1196
+ # Formtastic::SemanticFormBuilder.i18n_lookups_by_default = true/false
1197
+ #
1198
+ # Lookup priority:
1199
+ #
1200
+ # 'formtastic.{{type}}.{{model}}.{{action}}.{{attribute}}'
1201
+ # 'formtastic.{{type}}.{{model}}.{{attribute}}'
1202
+ # 'formtastic.{{type}}.{{attribute}}'
1203
+ #
1204
+ # Example:
1205
+ #
1206
+ # 'formtastic.labels.post.edit.title'
1207
+ # 'formtastic.labels.post.title'
1208
+ # 'formtastic.labels.title'
1209
+ #
1210
+ # NOTE: Generic, but only used for form input labels/hints.
1211
+ #
1212
+ def localized_attribute_string(attr_name, attr_value, i18n_key)
1213
+ if attr_value.is_a?(String)
1214
+ attr_value
1215
+ else
1216
+ use_i18n = attr_value.nil? ? @@i18n_lookups_by_default : attr_value
1217
+ if use_i18n
1218
+ model_name = @object.class.name.underscore
1219
+ action_name = template.params[:action].to_s rescue ''
1220
+ attribute_name = attr_name.to_s
1221
+
1222
+ defaults = I18N_SCOPES.collect do |i18n_scope|
1223
+ i18n_path = i18n_scope.dup
1224
+ i18n_path.gsub!('{{action}}', action_name)
1225
+ i18n_path.gsub!('{{model}}', model_name)
1226
+ i18n_path.gsub!('{{attribute}}', attribute_name)
1227
+ i18n_path.gsub!('..', '.')
1228
+ i18n_path.to_sym
1229
+ end
1230
+ defaults << ''
1231
+
1232
+ i18n_value = ::I18n.t(defaults.shift, :default => defaults,
1233
+ :scope => "formtastic.#{i18n_key.to_s.pluralize}")
1234
+ i18n_value.blank? ? nil : i18n_value
1235
+ end
1236
+ end
1237
+ end
1238
+
1164
1239
  end
1165
1240
 
1166
1241
  # Wrappers around form_for (etc) with :builder => SemanticFormBuilder.
@@ -1,6 +1,6 @@
1
- module JustinFrench
2
- module Formtastic
3
- class SemanticFormBuilder < ::Formtastic::SemanticFormBuilder
1
+ module JustinFrench #:nodoc:
2
+ module Formtastic #:nodoc:
3
+ class SemanticFormBuilder < ::Formtastic::SemanticFormBuilder #:nodoc:
4
4
  def initialize(*args)
5
5
  ::ActiveSupport::Deprecation.warn("JustinFrench::Formtastic::SemanticFormBuilder is deprecated. User Formtastic::SemanticFormBuilder instead", caller)
6
6
  super
@@ -62,7 +62,7 @@ describe 'Formtastic' do
62
62
  @fred.stub!(:login).and_return('fred_smith')
63
63
  @fred.stub!(:id).and_return(37)
64
64
  @fred.stub!(:new_record?).and_return(false)
65
- @fred.stub!(:errors).and_return(mock('errors', :on => nil))
65
+ @fred.stub!(:errors).and_return(mock('errors', :[] => nil))
66
66
 
67
67
  @bob = mock('user')
68
68
  @bob.stub!(:class).and_return(Author)
@@ -72,20 +72,20 @@ describe 'Formtastic' do
72
72
  @bob.stub!(:posts).and_return([])
73
73
  @bob.stub!(:post_ids).and_return([])
74
74
  @bob.stub!(:new_record?).and_return(false)
75
- @bob.stub!(:errors).and_return(mock('errors', :on => nil))
75
+ @bob.stub!(:errors).and_return(mock('errors', :[] => nil))
76
76
 
77
77
  Author.stub!(:find).and_return([@fred, @bob])
78
78
  Author.stub!(:human_attribute_name).and_return { |column_name| column_name.humanize }
79
79
  Author.stub!(:human_name).and_return('Author')
80
80
  Author.stub!(:reflect_on_all_validations).and_return([])
81
- Author.stub!(:reflect_on_association).and_return { |column_name| mock('reflection', :klass => Post, :macro => :has_many) if column_name == :posts }
81
+ Author.stub!(:reflect_on_association).and_return { |column_name| mock('reflection', :options => {}, :klass => Post, :macro => :has_many) if column_name == :posts }
82
82
 
83
83
  # Sometimes we need a mock @post object and some Authors for belongs_to
84
84
  @new_post = mock('post')
85
85
  @new_post.stub!(:class).and_return(Post)
86
86
  @new_post.stub!(:id).and_return(nil)
87
87
  @new_post.stub!(:new_record?).and_return(true)
88
- @new_post.stub!(:errors).and_return(mock('errors', :on => nil))
88
+ @new_post.stub!(:errors).and_return(mock('errors', :[] => nil))
89
89
  @new_post.stub!(:author).and_return(nil)
90
90
 
91
91
  @freds_post = mock('post')
@@ -97,7 +97,7 @@ describe 'Formtastic' do
97
97
  @freds_post.stub!(:authors).and_return([@fred])
98
98
  @freds_post.stub!(:author_ids).and_return([@fred.id])
99
99
  @freds_post.stub!(:new_record?).and_return(false)
100
- @freds_post.stub!(:errors).and_return(mock('errors', :on => nil))
100
+ @freds_post.stub!(:errors).and_return(mock('errors', :[] => nil))
101
101
  @fred.stub!(:posts).and_return([@freds_post])
102
102
  @fred.stub!(:post_ids).and_return([@freds_post.id])
103
103
 
@@ -107,9 +107,9 @@ describe 'Formtastic' do
107
107
  Post.stub!(:reflect_on_association).and_return do |column_name|
108
108
  case column_name
109
109
  when :author, :author_status
110
- mock('reflection', :klass => Author, :macro => :belongs_to)
110
+ mock('reflection', :options => {}, :klass => Author, :macro => :belongs_to)
111
111
  when :authors
112
- mock('reflection', :klass => Author, :macro => :has_and_belongs_to_many)
112
+ mock('reflection', :options => {}, :klass => Author, :macro => :has_and_belongs_to_many)
113
113
  end
114
114
  end
115
115
  Post.stub!(:find).and_return([@freds_post])
@@ -326,7 +326,7 @@ describe 'Formtastic' do
326
326
 
327
327
  it 'should return nil if label is false' do
328
328
  semantic_form_for(@new_post) do |builder|
329
- builder.label(:login, :label => false).should be_nil
329
+ builder.label(:login, :label => false).should be_blank
330
330
  end
331
331
  end
332
332
  end
@@ -336,8 +336,8 @@ describe 'Formtastic' do
336
336
  before(:each) do
337
337
  @title_errors = ['must not be blank', 'must be longer than 10 characters', 'must be awesome']
338
338
  @errors = mock('errors')
339
- @errors.stub!(:on).with('title').and_return(@title_errors)
340
- @errors.stub!(:on).with('body').and_return(nil)
339
+ @errors.stub!(:[]).with(:title).and_return(@title_errors)
340
+ @errors.stub!(:[]).with(:body).and_return(nil)
341
341
  @new_post.stub!(:errors).and_return(@errors)
342
342
  end
343
343
 
@@ -573,13 +573,22 @@ describe 'Formtastic' do
573
573
 
574
574
  describe 'when not provided' do
575
575
 
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
577
577
  semantic_form_for(:project, :url => 'http://test.host') do |builder|
578
578
  concat(builder.input(:anything))
579
579
  end
580
580
  output_buffer.should have_tag('form li.string')
581
581
  end
582
582
 
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
+ concat(builder.input(:password_confirmation))
587
+ concat(builder.input(:confirm_password))
588
+ end
589
+ output_buffer.should have_tag('form li.password', :count => 3)
590
+ end
591
+
583
592
  it 'should default to a string for methods on objects that don\'t respond to "column_for_attribute"' do
584
593
  @new_post.stub!(:method_without_a_database_column)
585
594
  @new_post.stub!(:column_for_attribute).and_return(nil)
@@ -688,9 +697,8 @@ describe 'Formtastic' do
688
697
  end
689
698
 
690
699
  describe ':label option' do
691
-
700
+
692
701
  describe 'when provided' do
693
-
694
702
  it 'should be passed down to the label tag' do
695
703
  semantic_form_for(@new_post) do |builder|
696
704
  concat(builder.input(:title, :label => "Kustom"))
@@ -698,35 +706,89 @@ describe 'Formtastic' do
698
706
  output_buffer.should have_tag("form li label", /Kustom/)
699
707
  end
700
708
 
709
+ it 'should not generate a label if false' do
710
+ semantic_form_for(@new_post) do |builder|
711
+ concat(builder.input(:title, :label => false))
712
+ end
713
+ output_buffer.should_not have_tag("form li label")
714
+ end
715
+
716
+ it 'should be dupped if frozen' do
717
+ semantic_form_for(@new_post) do |builder|
718
+ concat(builder.input(:title, :label => "Kustom".freeze))
719
+ end
720
+ output_buffer.should have_tag("form li label", /Kustom/)
721
+ end
701
722
  end
702
723
 
703
724
  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))
725
+ describe 'when localized label is NOT provided' do
726
+ describe 'and object is not given' do
727
+ it 'should default the humanized method name, passing it down to the label tag' do
728
+ Formtastic::SemanticFormBuilder.label_str_method = :humanize
729
+
730
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
731
+ concat(builder.input(:meta_description))
732
+ end
733
+
734
+ output_buffer.should have_tag("form li label", /#{'meta_description'.humanize}/)
735
+ end
736
+ end
737
+
738
+ describe 'and object is given' do
739
+ it 'should delegate the label logic to class human attribute name and pass it down to the label tag' do
740
+ @new_post.stub!(:meta_description) # a two word method name
741
+ @new_post.class.should_receive(:human_attribute_name).with('meta_description').and_return('meta_description'.humanize)
742
+
743
+ semantic_form_for(@new_post) do |builder|
744
+ concat(builder.input(:meta_description))
745
+ end
746
+
747
+ output_buffer.should have_tag("form li label", /#{'meta_description'.humanize}/)
710
748
  end
711
-
712
- output_buffer.should have_tag("form li label", /#{'meta_description'.humanize}/)
713
749
  end
714
750
  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
-
751
+
752
+ describe 'when localized label is provided' do
753
+ before do
754
+ @localized_label_text = 'Localized title'
755
+ @default_localized_label_text = 'Default localized title'
756
+ ::I18n.backend.store_translations :en,
757
+ :formtastic => {
758
+ :labels => {
759
+ :title => @default_localized_label_text,
760
+ :post => {
761
+ :title => @localized_label_text
762
+ }
763
+ }
764
+ }
765
+ ::Formtastic::SemanticFormBuilder.i18n_lookups_by_default = false
766
+ end
767
+
768
+ it 'should render a label with localized label (I18n)' do
721
769
  semantic_form_for(@new_post) do |builder|
722
- concat(builder.input(:meta_description))
770
+ concat(builder.input(:title, :label => true))
723
771
  end
724
-
725
- output_buffer.should have_tag("form li label", /#{'meta_description'.humanize}/)
772
+ output_buffer.should have_tag('form li label', @localized_label_text)
773
+ end
774
+
775
+ it 'should render a hint paragraph containing an optional localized label (I18n) if first is not set' do
776
+ ::I18n.backend.store_translations :en,
777
+ :formtastic => {
778
+ :labels => {
779
+ :post => {
780
+ :title => nil
781
+ }
782
+ }
783
+ }
784
+ semantic_form_for(@new_post) do |builder|
785
+ concat(builder.input(:title, :label => true))
786
+ end
787
+ output_buffer.should have_tag('form li label', @default_localized_label_text)
726
788
  end
727
789
  end
728
790
  end
729
-
791
+
730
792
  end
731
793
 
732
794
  describe ':hint option' do
@@ -742,12 +804,63 @@ describe 'Formtastic' do
742
804
  end
743
805
 
744
806
  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))
807
+ describe 'when localized hint (I18n) is provided' do
808
+ before do
809
+ @localized_hint_text = "This is the localized hint."
810
+ @default_localized_hint_text = "This is the default localized hint."
811
+ ::I18n.backend.store_translations :en,
812
+ :formtastic => {
813
+ :hints => {
814
+ :title => @default_localized_hint_text,
815
+ :post => {
816
+ :title => @localized_hint_text
817
+ }
818
+ }
819
+ }
820
+ ::Formtastic::SemanticFormBuilder.i18n_lookups_by_default = false
821
+ end
822
+
823
+ describe 'when provided value (hint value) is set to TRUE' do
824
+ it 'should render a hint paragraph containing a localized hint (I18n)' do
825
+ semantic_form_for(@new_post) do |builder|
826
+ concat(builder.input(:title, :hint => true))
827
+ end
828
+ output_buffer.should have_tag('form li p.inline-hints', @localized_hint_text)
829
+ end
830
+
831
+ it 'should render a hint paragraph containing an optional localized hint (I18n) if first is not set' do
832
+ ::I18n.backend.store_translations :en,
833
+ :formtastic => {
834
+ :hints => {
835
+ :post => {
836
+ :title => nil
837
+ }
838
+ }
839
+ }
840
+ semantic_form_for(@new_post) do |builder|
841
+ concat(builder.input(:title, :hint => true))
842
+ end
843
+ output_buffer.should have_tag('form li p.inline-hints', @default_localized_hint_text)
844
+ end
845
+ end
846
+
847
+ describe 'when provided value (label value) is set to FALSE' do
848
+ it 'should not render a hint paragraph' do
849
+ semantic_form_for(@new_post) do |builder|
850
+ concat(builder.input(:title, :hint => false))
851
+ end
852
+ output_buffer.should_not have_tag('form li p.inline-hints', @localized_hint_text)
853
+ end
854
+ end
855
+ end
856
+
857
+ describe 'when localized hint (I18n) is not provided' do
858
+ it 'should not render a hint paragraph' do
859
+ semantic_form_for(@new_post) do |builder|
860
+ concat(builder.input(:title))
861
+ end
862
+ output_buffer.should_not have_tag('form li p.inline-hints')
749
863
  end
750
- output_buffer.should_not have_tag("form li p.inline-hints")
751
864
  end
752
865
  end
753
866
 
@@ -809,7 +922,7 @@ describe 'Formtastic' do
809
922
  before do
810
923
  @title_errors = ['must not be blank', 'must be longer than 10 characters', 'must be awesome']
811
924
  @errors = mock('errors')
812
- @errors.stub!(:on).with('title').and_return(@title_errors)
925
+ @errors.stub!(:[]).with(:title).and_return(@title_errors)
813
926
  @new_post.stub!(:errors).and_return(@errors)
814
927
  end
815
928
 
@@ -922,31 +1035,33 @@ describe 'Formtastic' do
922
1035
  output_buffer.should have_tag("form li input[@name=\"post[title]\"]")
923
1036
  end
924
1037
 
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
1038
+ unless type == :numeric
1039
+ it 'should have a maxlength matching the column limit' do
1040
+ @new_post.column_for_attribute(:title).limit.should == 50
1041
+ output_buffer.should have_tag("form li input[@maxlength='50']")
1042
+ end
929
1043
 
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))
1044
+ it 'should use default_text_field_size for columns longer than default_text_field_size' do
1045
+ default_size = Formtastic::SemanticFormBuilder.default_text_field_size
1046
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => type, :limit => default_size * 2))
933
1047
 
934
- semantic_form_for(@new_post) do |builder|
935
- concat(builder.input(:title, :as => type))
1048
+ semantic_form_for(@new_post) do |builder|
1049
+ concat(builder.input(:title, :as => type))
1050
+ end
1051
+
1052
+ output_buffer.should have_tag("form li input[@size='#{default_size}']")
936
1053
  end
937
1054
 
938
- output_buffer.should have_tag("form li input[@size='#{default_size}']")
939
- end
1055
+ it 'should use the column size for columns shorter than default_text_field_size' do
1056
+ column_limit_shorted_than_default = 1
1057
+ @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => type, :limit => column_limit_shorted_than_default))
940
1058
 
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))
1059
+ semantic_form_for(@new_post) do |builder|
1060
+ concat(builder.input(:title, :as => type))
1061
+ end
944
1062
 
945
- semantic_form_for(@new_post) do |builder|
946
- concat(builder.input(:title, :as => type))
1063
+ output_buffer.should have_tag("form li input[@size='#{column_limit_shorted_than_default}']")
947
1064
  end
948
-
949
- output_buffer.should have_tag("form li input[@size='#{column_limit_shorted_than_default}']")
950
1065
  end
951
1066
 
952
1067
  it 'should use default_text_field_size for methods without database columns' do
@@ -967,6 +1082,13 @@ describe 'Formtastic' do
967
1082
  output_buffer.should have_tag("form li input.myclass")
968
1083
  end
969
1084
 
1085
+ it 'should consider input_html :id in labels' do
1086
+ semantic_form_for(@new_post) do |builder|
1087
+ concat(builder.input(:title, :as => type, :input_html => { :id => 'myid' }))
1088
+ end
1089
+ output_buffer.should have_tag('form li label[@for="myid"]')
1090
+ end
1091
+
970
1092
  it 'should generate input and labels even if no object is given' do
971
1093
  semantic_form_for(:project, :url => 'http://test.host/') do |builder|
972
1094
  concat(builder.input(:title, :as => type))
@@ -1080,11 +1202,11 @@ describe 'Formtastic' do
1080
1202
 
1081
1203
  describe ":as => :hidden" do
1082
1204
  before do
1083
- @new_post.stub!(:hidden)
1205
+ @new_post.stub!(:secret)
1084
1206
  @new_post.stub!(:column_for_attribute).and_return(mock('column', :type => :string))
1085
1207
 
1086
1208
  semantic_form_for(@new_post) do |builder|
1087
- concat(builder.input(:hidden, :as => :hidden))
1209
+ concat(builder.input(:secret, :as => :hidden))
1088
1210
  end
1089
1211
  end
1090
1212
 
@@ -1093,7 +1215,7 @@ describe 'Formtastic' do
1093
1215
  end
1094
1216
 
1095
1217
  it 'should have a post_hidden_input id on the wrapper' do
1096
- output_buffer.should have_tag('form li#post_hidden_input')
1218
+ output_buffer.should have_tag('form li#post_secret_input')
1097
1219
  end
1098
1220
 
1099
1221
  it 'should not generate a label for the input' do
@@ -1101,10 +1223,24 @@ describe 'Formtastic' do
1101
1223
  end
1102
1224
 
1103
1225
  it "should generate a input field" do
1104
- output_buffer.should have_tag("form li input#post_hidden")
1226
+ output_buffer.should have_tag("form li input#post_secret")
1105
1227
  output_buffer.should have_tag("form li input[@type=\"hidden\"]")
1106
- output_buffer.should have_tag("form li input[@name=\"post[hidden]\"]")
1228
+ output_buffer.should have_tag("form li input[@name=\"post[secret]\"]")
1229
+ end
1230
+
1231
+ it "should not render inline errors" do
1232
+ @errors = mock('errors')
1233
+ @errors.stub!(:[]).with(:secret).and_return(["foo", "bah"])
1234
+ @new_post.stub!(:errors).and_return(@errors)
1235
+
1236
+ semantic_form_for(@new_post) do |builder|
1237
+ concat(builder.input(:secret, :as => :hidden))
1238
+ end
1239
+
1240
+ output_buffer.should_not have_tag("form li p.inline-errors")
1241
+ output_buffer.should_not have_tag("form li ul.errors")
1107
1242
  end
1243
+
1108
1244
  end
1109
1245
 
1110
1246
  describe ":as => :time_zone" do
@@ -1247,7 +1383,7 @@ describe 'Formtastic' do
1247
1383
  before do
1248
1384
  @new_post.stub!(:author).and_return(@bob)
1249
1385
  @new_post.stub!(:author_id).and_return(@bob.id)
1250
- Post.stub!(:reflect_on_association).and_return { |column_name| mock('reflection', :klass => Author, :macro => :belongs_to) }
1386
+ Post.stub!(:reflect_on_association).and_return { |column_name| mock('reflection', :options => {}, :klass => Author, :macro => :belongs_to) }
1251
1387
  end
1252
1388
 
1253
1389
  describe 'for belongs_to association' do
@@ -1393,7 +1529,11 @@ describe 'Formtastic' do
1393
1529
  it 'should create a select without size' do
1394
1530
  output_buffer.should_not have_tag('form li select[@size]')
1395
1531
  end
1396
-
1532
+
1533
+ it 'should have a blank option' do
1534
+ output_buffer.should have_tag("form li select option[@value='']")
1535
+ end
1536
+
1397
1537
  it 'should have a select option for each Author' do
1398
1538
  output_buffer.should have_tag('form li select option', :count => Author.find(:all).size + 1)
1399
1539
  Author.find(:all).each do |author|
@@ -1435,7 +1575,7 @@ describe 'Formtastic' do
1435
1575
 
1436
1576
  it 'should have a label inside the wrapper' do
1437
1577
  output_buffer.should have_tag('form li label')
1438
- output_buffer.should have_tag('form li label', /Post ids/)
1578
+ output_buffer.should have_tag('form li label', /Post/)
1439
1579
  output_buffer.should have_tag("form li label[@for='author_post_ids']")
1440
1580
  end
1441
1581
 
@@ -1449,11 +1589,15 @@ describe 'Formtastic' do
1449
1589
  end
1450
1590
 
1451
1591
  it 'should have a select option for each Post' do
1452
- output_buffer.should have_tag('form li select option', :count => Post.find(:all).size + 1)
1592
+ output_buffer.should have_tag('form li select option', :count => Post.find(:all).size)
1453
1593
  Post.find(:all).each do |post|
1454
1594
  output_buffer.should have_tag("form li select option[@value='#{post.id}']", /#{post.to_label}/)
1455
1595
  end
1456
1596
  end
1597
+
1598
+ it 'should not have a blank option' do
1599
+ output_buffer.should_not have_tag("form li select option[@value='']")
1600
+ end
1457
1601
 
1458
1602
  it 'should have one option with a "selected" attribute' do
1459
1603
  output_buffer.should have_tag('form li select option[@selected]', :count => 1)
@@ -1477,7 +1621,7 @@ describe 'Formtastic' do
1477
1621
 
1478
1622
  it 'should have a label inside the wrapper' do
1479
1623
  output_buffer.should have_tag('form li label')
1480
- output_buffer.should have_tag('form li label', /Author ids/)
1624
+ output_buffer.should have_tag('form li label', /Author/)
1481
1625
  output_buffer.should have_tag("form li label[@for='post_author_ids']")
1482
1626
  end
1483
1627
 
@@ -1491,11 +1635,15 @@ describe 'Formtastic' do
1491
1635
  end
1492
1636
 
1493
1637
  it 'should have a select option for each Author' do
1494
- output_buffer.should have_tag('form li select option', :count => Author.find(:all).size + 1)
1638
+ output_buffer.should have_tag('form li select option', :count => Author.find(:all).size)
1495
1639
  Author.find(:all).each do |author|
1496
1640
  output_buffer.should have_tag("form li select option[@value='#{author.id}']", /#{author.to_label}/)
1497
1641
  end
1498
1642
  end
1643
+
1644
+ it 'should not have a blank option' do
1645
+ output_buffer.should_not have_tag("form li select option[@value='']")
1646
+ end
1499
1647
 
1500
1648
  it 'should have one option with a "selected" attribute' do
1501
1649
  output_buffer.should have_tag('form li select option[@selected]', :count => 1)
@@ -2463,8 +2611,8 @@ describe 'Formtastic' do
2463
2611
  describe 'without a block' do
2464
2612
 
2465
2613
  before do
2466
- Post.stub!(:reflections).and_return({:author => mock('reflection', :macro => :belongs_to),
2467
- :comments => mock('reflection', :macro => :has_many) })
2614
+ Post.stub!(:reflections).and_return({:author => mock('reflection', :options => {}, :macro => :belongs_to),
2615
+ :comments => mock('reflection', :options => {}, :macro => :has_many) })
2468
2616
  Post.stub!(:content_columns).and_return([mock('column', :name => 'title'), mock('column', :name => 'body'), mock('column', :name => 'created_at')])
2469
2617
  Author.stub!(:find).and_return([@fred, @bob])
2470
2618
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: justinfrench-formtastic
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin French
@@ -9,7 +9,7 @@ autorequire: formtastic
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-17 00:00:00 -07:00
12
+ date: 2009-08-30 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
@@ -36,6 +36,7 @@ files:
36
36
  - spec/test_helper.rb
37
37
  has_rdoc: false
38
38
  homepage: http://github.com/justinfrench/formtastic/tree/master
39
+ licenses:
39
40
  post_install_message:
40
41
  rdoc_options:
41
42
  - --charset=UTF-8
@@ -56,7 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
56
57
  requirements: []
57
58
 
58
59
  rubyforge_project:
59
- rubygems_version: 1.2.0
60
+ rubygems_version: 1.3.5
60
61
  signing_key:
61
62
  specification_version: 3
62
63
  summary: A Rails form builder plugin/gem with semantically rich and accessible markup