formtastic 0.9.8 → 0.9.9

Sign up to get free protection for your applications and to get access to all the features.
data/README.textile CHANGED
@@ -206,7 +206,7 @@ When working in has many association, you can even supply @"%i"@ in your fieldse
206
206
  </pre>
207
207
 
208
208
 
209
- Customize HTML attributes for any input using the @:input_html@ option. Typically his is used to disable the input, change the size of a text field, change the rows in a textarea, or even to add a special class to an input to attach special behavior like "autogrow":http://plugins.jquery.com/project/autogrow textareas:
209
+ Customize HTML attributes for any input using the @:input_html@ option. Typically this is used to disable the input, change the size of a text field, change the rows in a textarea, or even to add a special class to an input to attach special behavior like "autogrow":http://plugins.jquery.com/project/autogrow textareas:
210
210
 
211
211
  <pre>
212
212
  <% semantic_form_for @post do |form| %>
@@ -337,7 +337,7 @@ Formtastic supports localized *labels*, *hints*, *legends*, *actions* using the
337
337
  title: "Choose a good title for you post."
338
338
  body: "Write something inspiring here."
339
339
  actions:
340
- create: "Create my {{model}}"
340
+ create: "Create my %{model}"
341
341
  update: "Save changes"
342
342
  dummie: "Launch!"
343
343
  </pre>
@@ -354,7 +354,7 @@ Formtastic supports localized *labels*, *hints*, *legends*, *actions* using the
354
354
  <%= form.input :section %> # => :label => I18n.t('activerecord.attributes.user.section') or 'Section'
355
355
  <% end %>
356
356
  <% form.buttons do %>
357
- <%= form.commit_button %> # => "Create my {{model}}"
357
+ <%= form.commit_button %> # => "Create my %{model}"
358
358
  <% end %>
359
359
  <% end %>
360
360
  </pre>
@@ -28,7 +28,7 @@
28
28
  # Formtastic::SemanticFormBuilder.inline_errors = :sentence
29
29
 
30
30
  # Set the method to call on label text to transform or format it for human-friendly
31
- # reading when formtastic is user without object. Defaults to :humanize.
31
+ # reading when formtastic is used without object. Defaults to :humanize.
32
32
  # Formtastic::SemanticFormBuilder.label_str_method = :humanize
33
33
 
34
34
  # Set the array of methods to try calling on parent objects in :select and :radio inputs
data/lib/formtastic.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  # coding: utf-8
2
2
  require File.join(File.dirname(__FILE__), *%w[formtastic i18n])
3
+ require File.join(File.dirname(__FILE__), *%w[formtastic util])
3
4
 
4
5
  module Formtastic #:nodoc:
5
6
 
@@ -9,7 +10,7 @@ module Formtastic #:nodoc:
9
10
  @@default_text_area_height = 20
10
11
  @@all_fields_required_by_default = true
11
12
  @@include_blank_for_select_by_default = true
12
- @@required_string = proc { %{<abbr title="#{::Formtastic::I18n.t(:required)}">*</abbr>} }
13
+ @@required_string = proc { ::Formtastic::Util.html_safe(%{<abbr title="#{::Formtastic::I18n.t(:required)}">*</abbr>}) }
13
14
  @@optional_string = ''
14
15
  @@inline_errors = :sentence
15
16
  @@label_str_method = :humanize
@@ -105,7 +106,7 @@ module Formtastic #:nodoc:
105
106
  send(:"inline_#{type}_for", method, options)
106
107
  end.compact.join("\n")
107
108
 
108
- return template.content_tag(:li, list_item_content, wrapper_html)
109
+ return template.content_tag(:li, Formtastic::Util.html_safe(list_item_content), wrapper_html)
109
110
  end
110
111
 
111
112
  # Creates an input fieldset and ol tag wrapping for use around a set of inputs. It can be
@@ -342,7 +343,7 @@ module Formtastic #:nodoc:
342
343
  element_class = ['commit', options.delete(:class)].compact.join(' ') # TODO: Add class reflecting on form action.
343
344
  accesskey = (options.delete(:accesskey) || @@default_commit_button_accesskey) unless button_html.has_key?(:accesskey)
344
345
  button_html = button_html.merge(:accesskey => accesskey) if accesskey
345
- template.content_tag(:li, self.submit(text, button_html), :class => element_class)
346
+ template.content_tag(:li, Formtastic::Util.html_safe(self.submit(text, button_html)), :class => element_class)
346
347
  end
347
348
 
348
349
  # A thin wrapper around #fields_for to set :builder => Formtastic::SemanticFormBuilder
@@ -399,11 +400,15 @@ module Formtastic #:nodoc:
399
400
  text = options_or_text
400
401
  options ||= {}
401
402
  end
403
+
402
404
  text = localized_string(method, text, :label) || humanized_attribute_name(method)
403
405
  text += required_or_optional_string(options.delete(:required))
406
+ text = Formtastic::Util.html_safe(text)
404
407
 
405
408
  # special case for boolean (checkbox) labels, which have a nested input
406
- text = (options.delete(:label_prefix_for_nested_input) || "") + text
409
+ if options.key?(:label_prefix_for_nested_input)
410
+ text = options.delete(:label_prefix_for_nested_input) + text
411
+ end
407
412
 
408
413
  input_name = options.delete(:input_name) || method
409
414
  super(input_name, text, options)
@@ -422,8 +427,10 @@ module Formtastic #:nodoc:
422
427
  #
423
428
  def inline_errors_for(method, options = nil) #:nodoc:
424
429
  if render_inline_errors?
425
- errors = @object.errors[method.to_sym]
426
- send(:"error_#{@@inline_errors}", [*errors]) if errors.present?
430
+ errors = [@object.errors[method.to_sym]]
431
+ errors << [@object.errors[association_primary_key(method)]] if association_macro_for_method(method) == :belongs_to
432
+ errors = errors.flatten.compact.uniq
433
+ send(:"error_#{@@inline_errors}", [*errors]) if errors.any?
427
434
  else
428
435
  nil
429
436
  end
@@ -452,7 +459,7 @@ module Formtastic #:nodoc:
452
459
  return nil if full_errors.blank?
453
460
  html_options[:class] ||= "errors"
454
461
  template.content_tag(:ul, html_options) do
455
- full_errors.map { |error| template.content_tag(:li, error) }.join
462
+ Formtastic::Util.html_safe(full_errors.map { |error| template.content_tag(:li, Formtastic::Util.html_safe(error)) }.join)
456
463
  end
457
464
  end
458
465
 
@@ -483,6 +490,18 @@ module Formtastic #:nodoc:
483
490
  []
484
491
  end
485
492
  end
493
+
494
+ # Returns nil, or a symbol like :belongs_to or :has_many
495
+ def association_macro_for_method(method) #:nodoc:
496
+ reflection = self.reflection_for(method)
497
+ reflection.macro if reflection
498
+ end
499
+
500
+ def association_primary_key(method)
501
+ reflection = self.reflection_for(method)
502
+ reflection.options[:foreign_key] if reflection && !reflection.options[:foreign_key].blank?
503
+ :"#{method}_id"
504
+ end
486
505
 
487
506
  # Prepare options to be sent to label
488
507
  #
@@ -504,9 +523,9 @@ module Formtastic #:nodoc:
504
523
  raise ArgumentError, 'You gave :for option with a block to inputs method, ' <<
505
524
  'but the block does not accept any argument.' if block.arity <= 0
506
525
 
507
- proc { |f| f.inputs(*args){ block.call(f) } }
526
+ proc { |f| return f.inputs(*args){ block.call(f) } }
508
527
  else
509
- proc { |f| f.inputs(*args) }
528
+ proc { |f| return f.inputs(*args) }
510
529
  end
511
530
 
512
531
  fields_for_args = [options.delete(:for), options.delete(:for_options) || {}].flatten
@@ -859,12 +878,12 @@ module Formtastic #:nodoc:
859
878
  html_options[:checked] = selected_value == value if selected_option_is_present
860
879
 
861
880
  li_content = template.content_tag(:label,
862
- "#{self.radio_button(input_name, value, html_options)} #{label}",
881
+ Formtastic::Util.html_safe("#{self.radio_button(input_name, value, html_options)} #{label}"),
863
882
  :for => input_id
864
883
  )
865
884
 
866
885
  li_options = value_as_class ? { :class => [method.to_s.singularize, value.to_s.downcase].join('_') } : {}
867
- template.content_tag(:li, li_content, li_options)
886
+ template.content_tag(:li, Formtastic::Util.html_safe(li_content), li_options)
868
887
  end
869
888
 
870
889
  field_set_and_list_wrapping_for_method(method, options, list_item_content)
@@ -994,8 +1013,8 @@ module Formtastic #:nodoc:
994
1013
  list_items_capture = ""
995
1014
  hidden_fields_capture = ""
996
1015
 
997
- datetime = options.key?(:selected) ? options[:selected] : Time.now # can't do an || because nil is an important value
998
- datetime = @object.send(method) if @object && @object.send(method) # object trumps :selected
1016
+ datetime = options[:selected]
1017
+ datetime = @object.send(method) if @object && @object.send(method) # object value trumps :selected value
999
1018
 
1000
1019
  html_options = options.delete(:input_html) || {}
1001
1020
  input_ids = []
@@ -1013,10 +1032,10 @@ module Formtastic #:nodoc:
1013
1032
  opts = strip_formtastic_options(options).merge(:prefix => @object_name, :field_name => field_name, :default => datetime)
1014
1033
  item_label_text = labels[input] || ::I18n.t(input.to_s, :default => input.to_s.humanize, :scope => [:datetime, :prompts])
1015
1034
 
1016
- list_items_capture << template.content_tag(:li, [
1017
- !item_label_text.blank? ? template.content_tag(:label, item_label_text, :for => input_id) : "",
1035
+ list_items_capture << template.content_tag(:li, Formtastic::Util.html_safe([
1036
+ !item_label_text.blank? ? template.content_tag(:label, Formtastic::Util.html_safe(item_label_text), :for => input_id) : "",
1018
1037
  template.send(:"select_#{input}", datetime, opts, html_options.merge(:id => input_id))
1019
- ].join("")
1038
+ ].join(""))
1020
1039
  )
1021
1040
  end
1022
1041
  end
@@ -1110,7 +1129,10 @@ module Formtastic #:nodoc:
1110
1129
  selected_option_is_present = [:selected, :checked].any? { |k| options.key?(k) }
1111
1130
  selected_values = (options.key?(:checked) ? options[:checked] : options[:selected]) if selected_option_is_present
1112
1131
  selected_values = [*selected_values].compact
1113
-
1132
+
1133
+ disabled_option_is_present = options.key?(:disabled)
1134
+ disabled_values = [*options[:disabled]] if disabled_option_is_present
1135
+
1114
1136
  list_item_content = collection.map do |c|
1115
1137
  label = c.is_a?(Array) ? c.first : c
1116
1138
  value = c.is_a?(Array) ? c.last : c
@@ -1118,15 +1140,16 @@ module Formtastic #:nodoc:
1118
1140
  input_ids << input_id
1119
1141
 
1120
1142
  html_options[:checked] = selected_values.include?(value) if selected_option_is_present
1143
+ html_options[:disabled] = disabled_values.include?(value) if disabled_option_is_present
1121
1144
  html_options[:id] = input_id
1122
1145
 
1123
1146
  li_content = template.content_tag(:label,
1124
- "#{self.check_box(input_name, html_options, value, unchecked_value)} #{label}",
1147
+ Formtastic::Util.html_safe("#{self.check_box(input_name, html_options, value, unchecked_value)} #{label}"),
1125
1148
  :for => input_id
1126
1149
  )
1127
1150
 
1128
1151
  li_options = value_as_class ? { :class => [method.to_s.singularize, value.to_s.downcase].join('_') } : {}
1129
- template.content_tag(:li, li_content, li_options)
1152
+ template.content_tag(:li, Formtastic::Util.html_safe(li_content), li_options)
1130
1153
  end
1131
1154
 
1132
1155
  field_set_and_list_wrapping_for_method(method, options, list_item_content)
@@ -1191,13 +1214,13 @@ module Formtastic #:nodoc:
1191
1214
  def inline_hints_for(method, options) #:nodoc:
1192
1215
  options[:hint] = localized_string(method, options[:hint], :hint)
1193
1216
  return if options[:hint].blank?
1194
- template.content_tag(:p, options[:hint], :class => 'inline-hints')
1217
+ template.content_tag(:p, Formtastic::Util.html_safe(options[:hint]), :class => 'inline-hints')
1195
1218
  end
1196
1219
 
1197
1220
  # Creates an error sentence by calling to_sentence on the errors array.
1198
1221
  #
1199
1222
  def error_sentence(errors) #:nodoc:
1200
- template.content_tag(:p, errors.to_sentence.untaint, :class => 'inline-errors')
1223
+ template.content_tag(:p, Formtastic::Util.html_safe(errors.to_sentence.untaint), :class => 'inline-errors')
1201
1224
  end
1202
1225
 
1203
1226
  # Creates an error li list.
@@ -1205,15 +1228,15 @@ module Formtastic #:nodoc:
1205
1228
  def error_list(errors) #:nodoc:
1206
1229
  list_elements = []
1207
1230
  errors.each do |error|
1208
- list_elements << template.content_tag(:li, error.untaint)
1231
+ list_elements << template.content_tag(:li, Formtastic::Util.html_safe(error.untaint))
1209
1232
  end
1210
- template.content_tag(:ul, list_elements.join("\n"), :class => 'errors')
1233
+ template.content_tag(:ul, Formtastic::Util.html_safe(list_elements.join("\n")), :class => 'errors')
1211
1234
  end
1212
1235
 
1213
1236
  # Creates an error sentence containing only the first error
1214
1237
  #
1215
1238
  def error_first(errors) #:nodoc:
1216
- template.content_tag(:p, errors.first.untaint, :class => 'inline-errors')
1239
+ template.content_tag(:p, Formtastic::Util.html_safe(errors.first.untaint), :class => 'inline-errors')
1217
1240
  end
1218
1241
 
1219
1242
  # Generates the required or optional string. If the value set is a proc,
@@ -1260,7 +1283,7 @@ module Formtastic #:nodoc:
1260
1283
 
1261
1284
  legend = html_options.delete(:name).to_s
1262
1285
  legend %= parent_child_index(html_options[:parent]) if html_options[:parent]
1263
- legend = template.content_tag(:legend, template.content_tag(:span, legend)) unless legend.blank?
1286
+ legend = template.content_tag(:legend, template.content_tag(:span, Formtastic::Util.html_safe(legend))) unless legend.blank?
1264
1287
 
1265
1288
  if block_given?
1266
1289
  contents = if template.respond_to?(:is_haml?) && template.is_haml?
@@ -1273,11 +1296,11 @@ module Formtastic #:nodoc:
1273
1296
  # Ruby 1.9: String#to_s behavior changed, need to make an explicit join.
1274
1297
  contents = contents.join if contents.respond_to?(:join)
1275
1298
  fieldset = template.content_tag(:fieldset,
1276
- legend << template.content_tag(:ol, contents),
1299
+ Formtastic::Util.html_safe(legend) << template.content_tag(:ol, Formtastic::Util.html_safe(contents)),
1277
1300
  html_options.except(:builder, :parent)
1278
1301
  )
1279
1302
 
1280
- template.concat(fieldset) if block_given?
1303
+ template.concat(fieldset) if block_given? && (!defined?(Rails::VERSION) || Rails::VERSION::MAJOR == 2)
1281
1304
  fieldset
1282
1305
  end
1283
1306
 
@@ -1305,7 +1328,7 @@ module Formtastic #:nodoc:
1305
1328
  template.content_tag(:legend,
1306
1329
  self.label(method, options_for_label(options).merge(:for => options.delete(:label_for))), :class => 'label'
1307
1330
  ) <<
1308
- template.content_tag(:ol, contents)
1331
+ template.content_tag(:ol, Formtastic::Util.html_safe(contents))
1309
1332
  )
1310
1333
  end
1311
1334
 
@@ -1323,7 +1346,7 @@ module Formtastic #:nodoc:
1323
1346
  case column.type
1324
1347
  when :string
1325
1348
  return :password if method.to_s =~ /password/
1326
- return :country if method.to_s =~ /country/
1349
+ return :country if method.to_s =~ /country$/
1327
1350
  return :time_zone if method.to_s =~ /time_zone/
1328
1351
  when :integer
1329
1352
  return :select if method.to_s =~ /_id$/
@@ -1380,7 +1403,13 @@ module Formtastic #:nodoc:
1380
1403
  collection = if options[:collection]
1381
1404
  options.delete(:collection)
1382
1405
  elsif reflection = self.reflection_for(column)
1383
- reflection.klass.find(:all, options[:find_options] || {})
1406
+ options[:find_options] ||= {}
1407
+
1408
+ if conditions = reflection.options[:conditions]
1409
+ options[:find_options][:conditions] = reflection.klass.merge_conditions(conditions, options[:find_options][:conditions])
1410
+ end
1411
+
1412
+ reflection.klass.find(:all, options[:find_options])
1384
1413
  else
1385
1414
  create_boolean_collection(options)
1386
1415
  end
@@ -1471,7 +1500,7 @@ module Formtastic #:nodoc:
1471
1500
  if [:has_and_belongs_to_many, :has_many].include?(reflection.macro)
1472
1501
  "#{method.to_s.singularize}_ids"
1473
1502
  else
1474
- reflection.options[:foreign_key] || reflection.options[:class_name].try(:foreign_key) || "#{method}_id"
1503
+ reflection.options[:foreign_key] || "#{method}_id"
1475
1504
  end
1476
1505
  else
1477
1506
  method
@@ -1566,9 +1595,9 @@ module Formtastic #:nodoc:
1566
1595
  #
1567
1596
  # Lookup priority:
1568
1597
  #
1569
- # 'formtastic.{{type}}.{{model}}.{{action}}.{{attribute}}'
1570
- # 'formtastic.{{type}}.{{model}}.{{attribute}}'
1571
- # 'formtastic.{{type}}.{{attribute}}'
1598
+ # 'formtastic.%{type}.%{model}.%{action}.%{attribute}'
1599
+ # 'formtastic.%{type}.%{model}.%{attribute}'
1600
+ # 'formtastic.%{type}.%{attribute}'
1572
1601
  #
1573
1602
  # Example:
1574
1603
  #
@@ -1587,15 +1616,16 @@ module Formtastic #:nodoc:
1587
1616
  use_i18n = value.nil? ? @@i18n_lookups_by_default : (value != false)
1588
1617
 
1589
1618
  if use_i18n
1590
- model_name = self.model_name.underscore
1619
+ model_name, nested_model_name = normalize_model_name(self.model_name.underscore)
1591
1620
  action_name = template.params[:action].to_s rescue ''
1592
1621
  attribute_name = key.to_s
1593
1622
 
1594
1623
  defaults = ::Formtastic::I18n::SCOPES.collect do |i18n_scope|
1595
1624
  i18n_path = i18n_scope.dup
1596
- i18n_path.gsub!('{{action}}', action_name)
1597
- i18n_path.gsub!('{{model}}', model_name)
1598
- i18n_path.gsub!('{{attribute}}', attribute_name)
1625
+ i18n_path.gsub!('%{action}', action_name)
1626
+ i18n_path.gsub!('%{model}', model_name)
1627
+ i18n_path.gsub!('%{nested_model}', nested_model_name) unless nested_model_name.nil?
1628
+ i18n_path.gsub!('%{attribute}', attribute_name)
1599
1629
  i18n_path.gsub!('..', '.')
1600
1630
  i18n_path.to_sym
1601
1631
  end
@@ -1612,6 +1642,14 @@ module Formtastic #:nodoc:
1612
1642
  @object.present? ? @object.class.name : @object_name.to_s.classify
1613
1643
  end
1614
1644
 
1645
+ def normalize_model_name(name)
1646
+ if name =~ /(.+)\[(.+)\]/
1647
+ [$1, $2]
1648
+ else
1649
+ [name]
1650
+ end
1651
+ end
1652
+
1615
1653
  def send_or_call(duck, object)
1616
1654
  if duck.is_a?(Proc)
1617
1655
  duck.call(object)
@@ -1,3 +1,4 @@
1
+ # coding: utf-8
1
2
  module Formtastic
2
3
  module I18n
3
4
 
@@ -6,13 +7,16 @@ module Formtastic
6
7
  :required => 'required',
7
8
  :yes => 'Yes',
8
9
  :no => 'No',
9
- :create => 'Create {{model}}',
10
- :update => 'Update {{model}}'
10
+ :create => 'Create %{model}',
11
+ :update => 'Update %{model}'
11
12
  }.freeze
12
13
  SCOPES = [
13
- '{{model}}.{{action}}.{{attribute}}',
14
- '{{model}}.{{attribute}}',
15
- '{{attribute}}'
14
+ '%{model}.%{nested_model}.%{action}.%{attribute}',
15
+ '%{model}.%{action}.%{attribute}',
16
+ '%{model}.%{nested_model}.%{attribute}',
17
+ '%{model}.%{attribute}',
18
+ '%{nested_model}.%{attribute}',
19
+ '%{attribute}'
16
20
  ]
17
21
 
18
22
  class << self
@@ -1,3 +1,4 @@
1
+ # coding: utf-8
1
2
  module Formtastic
2
3
  module LayoutHelper
3
4
 
@@ -0,0 +1,25 @@
1
+ # Adapted from the rails3 compatibility shim in Haml 2.2
2
+ module Formtastic
3
+ module Util
4
+ extend self
5
+ ## Rails XSS Safety
6
+
7
+ # Returns the given text, marked as being HTML-safe.
8
+ # With older versions of the Rails XSS-safety mechanism,
9
+ # this destructively modifies the HTML-safety of `text`.
10
+ #
11
+ # @param text [String]
12
+ # @return [String] `text`, marked as HTML-safe
13
+ def html_safe(text)
14
+ return text if text.nil?
15
+ return text.html_safe if defined?(ActiveSupport::SafeBuffer)
16
+ return text.html_safe!
17
+ end
18
+
19
+ def rails_safe_buffer_class
20
+ return ActionView::SafeBuffer if defined?(ActionView::SafeBuffer)
21
+ ActiveSupport::SafeBuffer
22
+ end
23
+
24
+ end
25
+ end
@@ -136,11 +136,11 @@ describe 'SemanticFormBuilder#commit_button' do
136
136
  describe 'when no explicit label is provided' do
137
137
  describe 'when no I18n-localized label is provided' do
138
138
  before do
139
- ::I18n.backend.store_translations :en, :formtastic => {:submit => 'Submit {{model}}'}
139
+ ::I18n.backend.store_translations :en, :formtastic => {:submit => 'Submit %{model}'}
140
140
  end
141
141
 
142
142
  after do
143
- ::I18n.backend.store_translations :en, :formtastic => {:submit => nil}
143
+ ::I18n.backend.reload!
144
144
  end
145
145
 
146
146
  it 'should render an input with default I18n-localized label (fallback)' do
@@ -157,33 +157,34 @@ describe 'SemanticFormBuilder#commit_button' do
157
157
  :formtastic => {
158
158
  :actions => {
159
159
  :submit => 'Custom Submit',
160
- :post => {
161
- :submit => 'Custom Submit {{model}}'
162
- }
163
160
  }
164
161
  }
165
162
  ::Formtastic::SemanticFormBuilder.i18n_lookups_by_default = true
166
163
  end
167
164
 
168
- it 'should render an input with localized label (I18n)' do
169
- semantic_form_for(:post, :url => 'http://example.com') do |builder|
170
- concat(builder.commit_button)
171
- end
172
- output_buffer.should have_tag(%Q{li.commit input[@value="Custom Submit Post"][@class~="submit"]})
165
+ after do
166
+ ::I18n.backend.reload!
173
167
  end
174
168
 
175
- it 'should render an input with anoptional localized label (I18n) - if first is not set' do
169
+ it 'should render an input with localized label (I18n)' do
176
170
  ::I18n.backend.store_translations :en,
177
171
  :formtastic => {
178
172
  :actions => {
179
173
  :post => {
180
- :submit => nil
174
+ :submit => 'Custom Submit %{model}'
181
175
  }
182
176
  }
183
177
  }
184
178
  semantic_form_for(:post, :url => 'http://example.com') do |builder|
185
179
  concat(builder.commit_button)
186
180
  end
181
+ output_buffer.should have_tag(%Q{li.commit input[@value="Custom Submit Post"][@class~="submit"]})
182
+ end
183
+
184
+ it 'should render an input with anoptional localized label (I18n) - if first is not set' do
185
+ semantic_form_for(:post, :url => 'http://example.com') do |builder|
186
+ concat(builder.commit_button)
187
+ end
187
188
  output_buffer.should have_tag(%Q{li.commit input[@value="Custom Submit"][@class~="submit"]})
188
189
  end
189
190
 
@@ -209,11 +210,11 @@ describe 'SemanticFormBuilder#commit_button' do
209
210
  describe 'when no explicit label is provided' do
210
211
  describe 'when no I18n-localized label is provided' do
211
212
  before do
212
- ::I18n.backend.store_translations :en, :formtastic => {:create => 'Create {{model}}'}
213
+ ::I18n.backend.store_translations :en, :formtastic => {:create => 'Create %{model}'}
213
214
  end
214
215
 
215
216
  after do
216
- ::I18n.backend.store_translations :en, :formtastic => {:create => nil}
217
+ ::I18n.backend.reload!
217
218
  end
218
219
 
219
220
  it 'should render an input with default I18n-localized label (fallback)' do
@@ -230,40 +231,35 @@ describe 'SemanticFormBuilder#commit_button' do
230
231
  :formtastic => {
231
232
  :actions => {
232
233
  :create => 'Custom Create',
233
- :post => {
234
- :create => 'Custom Create {{model}}'
235
- }
236
234
  }
237
235
  }
238
236
  ::Formtastic::SemanticFormBuilder.i18n_lookups_by_default = true
239
237
  end
240
238
 
241
239
  after do
242
- ::I18n.backend.store_translations :en, :formtastic => nil
240
+ ::I18n.backend.reload!
243
241
  end
244
242
 
245
243
  it 'should render an input with localized label (I18n)' do
246
- semantic_form_for(@new_post) do |builder|
247
- concat(builder.commit_button)
248
- end
249
- output_buffer.should have_tag(%Q{li.commit input[@value="Custom Create Post"][@class~="create"]})
250
- end
251
-
252
- it 'should render an input with anoptional localized label (I18n) - if first is not set' do
253
244
  ::I18n.backend.store_translations :en,
254
245
  :formtastic => {
255
246
  :actions => {
256
247
  :post => {
257
- :create => nil
248
+ :create => 'Custom Create %{model}'
258
249
  }
259
250
  }
260
251
  }
261
252
  semantic_form_for(@new_post) do |builder|
262
253
  concat(builder.commit_button)
263
254
  end
264
- output_buffer.should have_tag(%Q{li.commit input[@value="Custom Create"][@class~="create"]})
265
- ::I18n.backend.store_translations :en, :formtastic => nil
266
-
255
+ output_buffer.should have_tag(%Q{li.commit input[@value="Custom Create Post"][@class~="create"]})
256
+ end
257
+
258
+ it 'should render an input with anoptional localized label (I18n) - if first is not set' do
259
+ semantic_form_for(@new_post) do |builder|
260
+ concat(builder.commit_button)
261
+ end
262
+ output_buffer.should have_tag(%Q{li.commit input[@value="Custom Create"][@class~="create"]})
267
263
  end
268
264
 
269
265
  end
@@ -288,11 +284,11 @@ describe 'SemanticFormBuilder#commit_button' do
288
284
  describe 'when no explicit label is provided' do
289
285
  describe 'when no I18n-localized label is provided' do
290
286
  before do
291
- ::I18n.backend.store_translations :en, :formtastic => {:update => 'Save {{model}}'}
287
+ ::I18n.backend.store_translations :en, :formtastic => {:update => 'Save %{model}'}
292
288
  end
293
289
 
294
290
  after do
295
- ::I18n.backend.store_translations :en, :formtastic => {:update => nil}
291
+ ::I18n.backend.reload!
296
292
  end
297
293
 
298
294
  it 'should render an input with default I18n-localized label (fallback)' do
@@ -309,33 +305,34 @@ describe 'SemanticFormBuilder#commit_button' do
309
305
  :formtastic => {
310
306
  :actions => {
311
307
  :update => 'Custom Save',
312
- :post => {
313
- :update => 'Custom Save {{model}}'
314
- }
315
308
  }
316
309
  }
317
310
  ::Formtastic::SemanticFormBuilder.i18n_lookups_by_default = true
318
311
  end
319
312
 
320
- it 'should render an input with localized label (I18n)' do
321
- semantic_form_for(@new_post) do |builder|
322
- concat(builder.commit_button)
323
- end
324
- output_buffer.should have_tag(%Q{li.commit input[@value="Custom Save Post"][@class~="update"]})
313
+ after do
314
+ ::I18n.backend.reload!
325
315
  end
326
-
327
- it 'should render an input with anoptional localized label (I18n) - if first is not set' do
316
+
317
+ it 'should render an input with localized label (I18n)' do
328
318
  ::I18n.backend.store_translations :en,
329
319
  :formtastic => {
330
320
  :actions => {
331
321
  :post => {
332
- :update => nil
322
+ :update => 'Custom Save %{model}'
333
323
  }
334
324
  }
335
325
  }
336
326
  semantic_form_for(@new_post) do |builder|
337
327
  concat(builder.commit_button)
338
328
  end
329
+ output_buffer.should have_tag(%Q{li.commit input[@value="Custom Save Post"][@class~="update"]})
330
+ end
331
+
332
+ it 'should render an input with anoptional localized label (I18n) - if first is not set' do
333
+ semantic_form_for(@new_post) do |builder|
334
+ concat(builder.commit_button)
335
+ end
339
336
  output_buffer.should have_tag(%Q{li.commit input[@value="Custom Save"][@class~="update"]})
340
337
  ::I18n.backend.store_translations :en, :formtastic => {}
341
338
  end
data/spec/errors_spec.rb CHANGED
@@ -81,5 +81,24 @@ describe 'SemanticFormBuilder#errors_on' do
81
81
  end
82
82
  end
83
83
 
84
+
85
+ describe 'when there are errors on the association and column' do
86
+
87
+ it "should list all unique errors" do
88
+ ::Formtastic::SemanticFormBuilder.inline_errors = :list
89
+ ::Post.stub!(:reflections).and_return({:author => mock('reflection', :options => {}, :macro => :belongs_to)})
90
+
91
+ @errors.stub!(:[]).with(:author).and_return(['must not be blank'])
92
+ @errors.stub!(:[]).with(:author_id).and_return(['is already taken', 'must not be blank']) # note the duplicate of association
93
+
94
+ semantic_form_for(@new_post) do |builder|
95
+ concat(builder.input(:author))
96
+ end
97
+ output_buffer.should have_tag("ul.errors li", /must not be blank/, :count => 1)
98
+ output_buffer.should have_tag("ul.errors li", /is already taken/, :count => 1)
99
+ end
100
+
101
+ end
102
+
84
103
  end
85
104
 
data/spec/i18n_spec.rb CHANGED
@@ -24,20 +24,24 @@ describe 'Formtastic::I18n' do
24
24
 
25
25
  before do
26
26
  @formtastic_strings = {
27
- :required => 'Default Required',
28
27
  :yes => 'Default Yes',
29
28
  :no => 'Default No',
30
- :create => 'Default Create {{model}}',
31
- :update => 'Default Update {{model}}',
29
+ :create => 'Default Create %{model}',
30
+ :update => 'Default Update %{model}',
32
31
  :custom_scope => {
33
32
  :duck => 'Duck',
34
- :duck_pond => '{{ducks}} ducks in a pond'
33
+ :duck_pond => '%{ducks} ducks in a pond'
35
34
  }
36
35
  }
37
36
  ::I18n.backend.store_translations :en, :formtastic => @formtastic_strings
38
37
  end
38
+
39
+ after do
40
+ ::I18n.backend.reload!
41
+ end
39
42
 
40
43
  it "should translate core strings correctly" do
44
+ ::I18n.backend.store_translations :en, {:formtastic => {:required => 'Default Required'}}
41
45
  ::Formtastic::I18n.t(:required).should == "Default Required"
42
46
  ::Formtastic::I18n.t(:yes).should == "Default Yes"
43
47
  ::Formtastic::I18n.t(:no).should == "Default No"
@@ -54,7 +58,6 @@ describe 'Formtastic::I18n' do
54
58
  end
55
59
 
56
60
  it "should be possible to override default values" do
57
- ::I18n.backend.store_translations :en, {:formtastic => {:required => nil}}
58
61
  ::Formtastic::I18n.t(:required, :default => 'Nothing found!').should == 'Nothing found!'
59
62
  end
60
63
 
@@ -63,18 +66,12 @@ describe 'Formtastic::I18n' do
63
66
  describe "when no I18n locales are available" do
64
67
 
65
68
  before do
66
- ::I18n.backend.store_translations :en, :formtastic => {
67
- :required => nil,
68
- :yes => nil,
69
- :no => nil,
70
- :create => nil,
71
- :update => nil
72
- }
69
+ ::I18n.backend.reload!
73
70
  end
74
-
71
+
75
72
  it "should use default strings" do
76
73
  (::Formtastic::I18n::DEFAULT_VALUES.keys).each do |key|
77
- ::Formtastic::I18n.t(key, :model => '{{model}}').should == ::Formtastic::I18n::DEFAULT_VALUES[key]
74
+ ::Formtastic::I18n.t(key, :model => '%{model}').should == ::Formtastic::I18n::DEFAULT_VALUES[key]
78
75
  end
79
76
  end
80
77
 
@@ -92,7 +89,8 @@ describe 'Formtastic::I18n' do
92
89
  :labels => {
93
90
  :title => "Hello world!",
94
91
  :post => {:title => "Hello post!"},
95
- :project => {:title => "Hello project!"}
92
+ :project => {:title => "Hello project!", :task => {:name => "Hello task name!"}},
93
+ :line_item => {:name => "Hello line item name!"}
96
94
  }
97
95
  }
98
96
  ::Formtastic::SemanticFormBuilder.i18n_lookups_by_default = true
@@ -102,7 +100,7 @@ describe 'Formtastic::I18n' do
102
100
  end
103
101
 
104
102
  after do
105
- ::I18n.backend.store_translations :en, :formtastic => nil
103
+ ::I18n.backend.reload!
106
104
  ::Formtastic::SemanticFormBuilder.i18n_lookups_by_default = false
107
105
  end
108
106
 
@@ -124,6 +122,25 @@ describe 'Formtastic::I18n' do
124
122
  output_buffer.should have_tag("form label", /Hello project!/)
125
123
  end
126
124
 
125
+ it 'should be able to translate nested objects with nested translations' do
126
+ semantic_form_for(:project, :url => 'http://test.host') do |builder|
127
+ builder.semantic_fields_for(:task) do |f|
128
+ concat(f.input(:name))
129
+ end
130
+ end
131
+ output_buffer.should have_tag("form label", /Hello task name!/)
132
+ end
133
+
134
+ it 'should be able to translated nested objects with top level translations' do
135
+ semantic_form_for(:order, :url => 'http://test.host') do |builder|
136
+ builder.semantic_fields_for(:line_item) do |f|
137
+ concat(f.input(:name))
138
+ end
139
+ end
140
+ output_buffer.should have_tag("form label", /Hello line item name!/)
141
+ end
142
+
143
+
127
144
  # TODO: Add spec for namespaced models?
128
145
 
129
146
  end
data/spec/input_spec.rb CHANGED
@@ -528,34 +528,35 @@ describe 'SemanticFormBuilder#input' do
528
528
  :formtastic => {
529
529
  :hints => {
530
530
  :title => @default_localized_hint_text,
531
- :post => {
532
- :title => @localized_hint_text
533
- }
534
531
  }
535
532
  }
536
533
  ::Formtastic::SemanticFormBuilder.i18n_lookups_by_default = false
537
534
  end
538
535
 
536
+ after do
537
+ ::I18n.backend.reload!
538
+ end
539
+
539
540
  describe 'when provided value (hint value) is set to TRUE' do
540
541
  it 'should render a hint paragraph containing a localized hint (I18n)' do
541
- semantic_form_for(@new_post) do |builder|
542
- concat(builder.input(:title, :hint => true))
543
- end
544
- output_buffer.should have_tag('form li p.inline-hints', @localized_hint_text)
545
- end
546
-
547
- it 'should render a hint paragraph containing an optional localized hint (I18n) if first is not set' do
548
542
  ::I18n.backend.store_translations :en,
549
543
  :formtastic => {
550
544
  :hints => {
551
545
  :post => {
552
- :title => nil
546
+ :title => @localized_hint_text
553
547
  }
554
548
  }
555
549
  }
556
550
  semantic_form_for(@new_post) do |builder|
557
551
  concat(builder.input(:title, :hint => true))
558
552
  end
553
+ output_buffer.should have_tag('form li p.inline-hints', @localized_hint_text)
554
+ end
555
+
556
+ it 'should render a hint paragraph containing an optional localized hint (I18n) if first is not set' do
557
+ semantic_form_for(@new_post) do |builder|
558
+ concat(builder.input(:title, :hint => true))
559
+ end
559
560
  output_buffer.should have_tag('form li p.inline-hints', @default_localized_hint_text)
560
561
  end
561
562
  end
@@ -177,6 +177,61 @@ describe 'check_boxes input' do
177
177
  end
178
178
 
179
179
 
180
+ describe 'when :disabled is set' do
181
+ before do
182
+ @output_buffer = ''
183
+ end
184
+
185
+ describe "no disabled items" do
186
+ before do
187
+ @new_post.stub!(:author_ids).and_return(nil)
188
+
189
+ semantic_form_for(@new_post) do |builder|
190
+ concat(builder.input(:authors, :as => :check_boxes, :disabled => nil))
191
+ end
192
+ end
193
+
194
+ it 'should not have any disabled item(s)' do
195
+ output_buffer.should_not have_tag("form li fieldset ol li label input[@disabled='disabled']")
196
+ end
197
+ end
198
+
199
+ describe "single disabled item" do
200
+ before do
201
+ @new_post.stub!(:author_ids).and_return(nil)
202
+
203
+ semantic_form_for(@new_post) do |builder|
204
+ concat(builder.input(:authors, :as => :check_boxes, :disabled => @fred.id))
205
+ end
206
+ end
207
+
208
+ it "should have one item disabled; the specified one" do
209
+ output_buffer.should have_tag("form li fieldset ol li label input[@disabled='disabled']", :count => 1)
210
+ output_buffer.should have_tag("form li fieldset ol li label[@for='post_author_ids_#{@fred.id}']", /fred/i)
211
+ output_buffer.should have_tag("form li fieldset ol li label input[@disabled='disabled'][@value='#{@fred.id}']")
212
+ end
213
+ end
214
+
215
+ describe "multiple disabled items" do
216
+ before do
217
+ @new_post.stub!(:author_ids).and_return(nil)
218
+
219
+ semantic_form_for(@new_post) do |builder|
220
+ concat(builder.input(:authors, :as => :check_boxes, :disabled => [@bob.id, @fred.id]))
221
+ end
222
+ end
223
+
224
+ it "should have multiple items disabled; the specified ones" do
225
+ output_buffer.should have_tag("form li fieldset ol li label input[@disabled='disabled']", :count => 2)
226
+ output_buffer.should have_tag("form li fieldset ol li label[@for='post_author_ids_#{@bob.id}']", /bob/i)
227
+ output_buffer.should have_tag("form li fieldset ol li label input[@disabled='disabled'][@value='#{@bob.id}']")
228
+ output_buffer.should have_tag("form li fieldset ol li label[@for='post_author_ids_#{@fred.id}']", /fred/i)
229
+ output_buffer.should have_tag("form li fieldset ol li label input[@disabled='disabled'][@value='#{@fred.id}']")
230
+ end
231
+ end
232
+
233
+ end
234
+
180
235
  end
181
236
 
182
237
 
@@ -26,7 +26,7 @@ describe 'country input' do
26
26
 
27
27
  before do
28
28
  semantic_form_for(@new_post) do |builder|
29
- builder.stub!(:country_select).and_return("<select><option>...</option></select>")
29
+ builder.stub!(:country_select).and_return(Formtastic::Util.html_safe("<select><option>...</option></select>"))
30
30
  concat(builder.input(:country, :as => :country))
31
31
  end
32
32
  end
@@ -54,8 +54,8 @@ describe 'country input' do
54
54
  it "should be passed down to the country_select helper when provided" do
55
55
  priority_countries = ["Foo", "Bah"]
56
56
  semantic_form_for(@new_post) do |builder|
57
- builder.stub!(:country_select).and_return("<select><option>...</option></select>")
58
- builder.should_receive(:country_select).with(:country, priority_countries, {}, {}).and_return("<select><option>...</option></select>")
57
+ builder.stub!(:country_select).and_return(Formtastic::Util.html_safe("<select><option>...</option></select>"))
58
+ builder.should_receive(:country_select).with(:country, priority_countries, {}, {}).and_return(Formtastic::Util.html_safe("<select><option>...</option></select>"))
59
59
 
60
60
  concat(builder.input(:country, :as => :country, :priority_countries => priority_countries))
61
61
  end
@@ -68,13 +68,47 @@ describe 'country input' do
68
68
 
69
69
  semantic_form_for(@new_post) do |builder|
70
70
  builder.stub!(:country_select).and_return("<select><option>...</option></select>")
71
- builder.should_receive(:country_select).with(:country, priority_countries, {}, {}).and_return("<select><option>...</option></select>")
71
+ builder.should_receive(:country_select).with(:country, priority_countries, {}, {}).and_return(Formtastic::Util.html_safe("<select><option>...</option></select>"))
72
72
 
73
73
  concat(builder.input(:country, :as => :country))
74
74
  end
75
75
  end
76
76
 
77
77
  end
78
-
78
+
79
+ describe "matching" do
80
+
81
+ describe "when the attribute is 'country'" do
82
+
83
+ before do
84
+ semantic_form_for(@new_post) do |builder|
85
+ builder.stub!(:country_select).and_return(Formtastic::Util.html_safe("<select><option>...</option></select>"))
86
+ concat(builder.input(:country))
87
+ end
88
+ end
89
+
90
+ it "should render a country input" do
91
+ output_buffer.should have_tag "form li.country"
92
+ end
93
+ end
94
+
95
+ describe "whent the attribute is 'country_something'" do
96
+
97
+ before do
98
+ semantic_form_for(@new_post) do |builder|
99
+ concat(builder.input(:country_subdivision))
100
+ concat(builder.input(:country_code))
101
+ end
102
+ end
103
+
104
+ it "should render a country input" do
105
+ output_buffer.should_not have_tag "form li.country"
106
+ output_buffer.should have_tag "form li.string", :count => 2
107
+ end
108
+
109
+ end
110
+
111
+ end
112
+
79
113
  end
80
114
 
@@ -68,7 +68,7 @@ describe 'date input' do
68
68
  end
69
69
  end
70
70
 
71
- describe 'when the object has no value' do
71
+ describe 'when the object has no value (nil)' do
72
72
  it "should select the :selected if provided as a Date" do
73
73
  output_buffer.replace ''
74
74
  @new_post.stub!(:created_at => nil)
@@ -104,14 +104,13 @@ describe 'date input' do
104
104
  output_buffer.should_not have_tag("form li ol li select#post_created_at_1i option[@selected]")
105
105
  end
106
106
 
107
- it "should select Time.now if a :selected is not provided" do
107
+ it "should select nothing if a :selected is not provided" do
108
108
  output_buffer.replace ''
109
109
  @new_post.stub!(:created_at => nil)
110
110
  semantic_form_for(@new_post) do |builder|
111
111
  concat(builder.input(:created_at, :as => :date))
112
112
  end
113
- output_buffer.should have_tag("form li ol li select#post_created_at_1i option[@selected]", :count => 1)
114
- output_buffer.should have_tag("form li ol li select#post_created_at_1i option[@value='#{Time.now.year}'][@selected]", :count => 1)
113
+ output_buffer.should_not have_tag("form li ol li select option[@selected]")
115
114
 
116
115
  end
117
116
  end
@@ -163,6 +163,21 @@ describe 'datetime input' do
163
163
  end
164
164
  end
165
165
 
166
+ #describe 'when an object is given and the attribute value is nil' do
167
+ # before(:each) do
168
+ # @new_post.stub!(:publish_at).and_return(nil)
169
+ # output_buffer.replace ''
170
+ # semantic_form_for(@new_post) do |builder|
171
+ # concat(builder.input(:publish_at, :as => :datetime))
172
+ # end
173
+ # end
174
+ #
175
+ # it 'should not select a value' do
176
+ # output_buffer.should_not have_tag('form li.datetime option[@selected]')
177
+ # end
178
+ #
179
+ #end
180
+
166
181
  describe ':selected option' do
167
182
 
168
183
  describe "when the object has a value" do
@@ -215,15 +230,13 @@ describe 'datetime input' do
215
230
  output_buffer.should_not have_tag("form li ol li select#post_created_at_1i option[@selected]")
216
231
  end
217
232
 
218
- it "should select Time.now if a :selected is not provided" do
233
+ it "should select nothing if a :selected is not provided" do
219
234
  output_buffer.replace ''
220
235
  @new_post.stub!(:created_at => nil)
221
236
  semantic_form_for(@new_post) do |builder|
222
237
  concat(builder.input(:created_at, :as => :datetime))
223
238
  end
224
- output_buffer.should have_tag("form li ol li select#post_created_at_1i option[@selected]", :count => 1)
225
- output_buffer.should have_tag("form li ol li select#post_created_at_1i option[@value='#{Time.now.year}'][@selected]", :count => 1)
226
-
239
+ output_buffer.should_not have_tag("form li ol li select option[@selected]")
227
240
  end
228
241
  end
229
242
 
@@ -94,6 +94,7 @@ describe 'select input' do
94
94
  before do
95
95
  semantic_form_for(@new_post) do |builder|
96
96
  concat(builder.input(:author, :as => :select))
97
+ concat(builder.input(:reviewer, :as => :select))
97
98
  end
98
99
  end
99
100
 
@@ -108,11 +109,13 @@ describe 'select input' do
108
109
  it 'should have a select inside the wrapper' do
109
110
  output_buffer.should have_tag('form li select')
110
111
  output_buffer.should have_tag('form li select#post_author_id')
112
+ output_buffer.should have_tag('form li select#post_reviewer_id')
111
113
  end
112
114
 
113
115
  it 'should have a valid name' do
114
116
  output_buffer.should have_tag("form li select[@name='post[author_id]']")
115
117
  output_buffer.should_not have_tag("form li select[@name='post[author_id][]']")
118
+ output_buffer.should_not have_tag("form li select[@name='post[reviewer_id][]']")
116
119
  end
117
120
 
118
121
  it 'should not create a multi-select' do
@@ -128,14 +131,14 @@ describe 'select input' do
128
131
  end
129
132
 
130
133
  it 'should have a select option for each Author' do
131
- output_buffer.should have_tag('form li select option', :count => ::Author.find(:all).size + 1)
134
+ output_buffer.should have_tag("form li select[@name='post[author_id]'] option", :count => ::Author.find(:all).size + 1)
132
135
  ::Author.find(:all).each do |author|
133
136
  output_buffer.should have_tag("form li select option[@value='#{author.id}']", /#{author.to_label}/)
134
137
  end
135
138
  end
136
139
 
137
140
  it 'should have one option with a "selected" attribute' do
138
- output_buffer.should have_tag('form li select option[@selected]', :count => 1)
141
+ output_buffer.should have_tag("form li select[@name='post[author_id]'] option[@selected]", :count => 1)
139
142
  end
140
143
 
141
144
  it 'should not singularize the association name' do
@@ -149,20 +152,6 @@ describe 'select input' do
149
152
 
150
153
  output_buffer.should have_tag('form li select#post_author_status_id')
151
154
  end
152
-
153
- it 'should use the "class_name" option' do
154
- @new_post.stub!(:status).and_return(@bob)
155
- @new_post.stub!(:author_status_id).and_return(@bob.id)
156
-
157
- ::Post.stub!(:reflect_on_association).with(:status).and_return(
158
- mock('reflection', :options => {:class_name => 'AuthorStatus'}, :klass => ::Author, :macro => :belongs_to))
159
-
160
- semantic_form_for(@new_post) do |builder|
161
- concat(builder.input(:status, :as => :select))
162
- end
163
-
164
- output_buffer.should have_tag('form li select#post_author_status_id')
165
- end
166
155
  end
167
156
 
168
157
  describe "for a belongs_to association with :group_by => :author" do
@@ -176,6 +165,34 @@ describe 'select input' do
176
165
  end
177
166
  end
178
167
 
168
+ describe "for a belongs_to association with :conditions" do
169
+ before do
170
+ ::Post.stub!(:reflect_on_association).with(:author).and_return do
171
+ mock = mock('reflection', :options => {:conditions => {:active => true}}, :klass => ::Author, :macro => :belongs_to)
172
+ mock.stub!(:[]).with(:class_name).and_return("Author")
173
+ mock
174
+ end
175
+ end
176
+
177
+ it "should call author.find with association conditions" do
178
+ ::Author.should_receive(:merge_conditions).with({:active => true}, nil).and_return(:active => true)
179
+ ::Author.should_receive(:find).with(:all, :conditions => {:active => true})
180
+
181
+ semantic_form_for(@new_post) do |builder|
182
+ concat(builder.input(:author, :as => :select))
183
+ end
184
+ end
185
+
186
+ it "should call author.find with association conditions and find_options conditions" do
187
+ ::Author.should_receive(:merge_conditions).with({:active => true}, {:publisher => true}).and_return(:active => true, :publisher => true)
188
+ ::Author.should_receive(:find).with(:all, :conditions => {:active => true, :publisher => true})
189
+
190
+ semantic_form_for(@new_post) do |builder|
191
+ concat(builder.input(:author, :as => :select, :find_options => {:conditions => {:publisher => true}}))
192
+ end
193
+ end
194
+ end
195
+
179
196
  describe 'for a belongs_to association with :group_by => :continent' do
180
197
  before do
181
198
  @authors = [@bob, @fred, @fred, @fred]
@@ -12,6 +12,7 @@ describe 'time input' do
12
12
 
13
13
  describe "general" do
14
14
  before do
15
+ ::I18n.backend.reload!
15
16
  output_buffer.replace ''
16
17
  end
17
18
 
@@ -94,7 +95,7 @@ describe 'time input' do
94
95
  end
95
96
  end
96
97
 
97
- describe 'when the object has no value' do
98
+ describe 'when the object has no value (nil)' do
98
99
  it "should select the :selected if provided as a Time" do
99
100
  output_buffer.replace ''
100
101
  @new_post.stub!(:created_at => nil)
@@ -118,14 +119,13 @@ describe 'time input' do
118
119
  output_buffer.should_not have_tag("form li ol li select#post_created_at_4i option[@selected]")
119
120
  end
120
121
 
121
- it "should select Time.now if a :selected is not provided" do
122
+ it "should select nothing if a :selected is not provided" do
122
123
  output_buffer.replace ''
123
124
  @new_post.stub!(:created_at => nil)
124
125
  semantic_form_for(@new_post) do |builder|
125
126
  concat(builder.input(:created_at, :as => :time))
126
127
  end
127
- output_buffer.should have_tag("form li ol li select#post_created_at_4i option[@selected]", :count => 1)
128
- output_buffer.should have_tag("form li ol li select#post_created_at_4i option[@value='#{Time.now.hour.to_s.rjust(2,'0')}'][@selected]", :count => 1)
128
+ output_buffer.should_not have_tag("form li ol li select option[@selected]")
129
129
  end
130
130
  end
131
131
 
data/spec/inputs_spec.rb CHANGED
@@ -391,5 +391,4 @@ describe 'SemanticFormBuilder#inputs' do
391
391
  end
392
392
 
393
393
  end
394
-
395
394
  end
data/spec/spec_helper.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  # coding: utf-8
2
2
  require 'rubygems'
3
3
 
4
- gem 'activesupport', '2.3.5'
5
- gem 'actionpack', '2.3.5'
4
+ gem 'activesupport', '2.3.7'
5
+ gem 'actionpack', '2.3.7'
6
6
  require 'active_support'
7
7
  require 'action_pack'
8
8
  require 'action_view'
@@ -22,6 +22,7 @@ Spec::Runner.configure do |config|
22
22
  end
23
23
 
24
24
  require File.expand_path(File.join(File.dirname(__FILE__), '../lib/formtastic'))
25
+ require File.expand_path(File.join(File.dirname(__FILE__), '../lib/formtastic/util'))
25
26
  require File.expand_path(File.join(File.dirname(__FILE__), '../lib/formtastic/layout_helper'))
26
27
 
27
28
 
@@ -126,6 +127,7 @@ module FormtasticSpecHelper
126
127
  @new_post.stub!(:new_record?).and_return(true)
127
128
  @new_post.stub!(:errors).and_return(mock('errors', :[] => nil))
128
129
  @new_post.stub!(:author).and_return(nil)
130
+ @new_post.stub!(:reviewer).and_return(nil)
129
131
  @new_post.stub!(:main_post).and_return(nil)
130
132
  @new_post.stub!(:sub_posts).and_return([]) #TODO should be a mock with methods for adding sub posts
131
133
 
@@ -146,11 +148,16 @@ module FormtasticSpecHelper
146
148
  ::Post.stub!(:human_name).and_return('Post')
147
149
  ::Post.stub!(:reflect_on_all_validations).and_return([])
148
150
  ::Post.stub!(:reflect_on_validations_for).and_return([])
151
+ ::Post.stub!(:reflections).and_return({})
149
152
  ::Post.stub!(:reflect_on_association).and_return do |column_name|
150
153
  case column_name
151
154
  when :author, :author_status
152
155
  mock = mock('reflection', :options => {}, :klass => ::Author, :macro => :belongs_to)
153
156
  mock.stub!(:[]).with(:class_name).and_return("Author")
157
+ mock
158
+ when :reviewer
159
+ mock = mock('reflection', :options => {:class_name => 'Author'}, :klass => ::Author, :macro => :belongs_to)
160
+ mock.stub!(:[]).with(:class_name).and_return("Author")
154
161
  mock
155
162
  when :authors
156
163
  mock('reflection', :options => {}, :klass => ::Author, :macro => :has_and_belongs_to_many)
@@ -173,6 +180,9 @@ module FormtasticSpecHelper
173
180
  @new_post.stub!(:time_zone)
174
181
  @new_post.stub!(:category_name)
175
182
  @new_post.stub!(:allow_comments)
183
+ @new_post.stub!(:country)
184
+ @new_post.stub!(:country_subdivision)
185
+ @new_post.stub!(:country_code)
176
186
  @new_post.stub!(:column_for_attribute).with(:meta_description).and_return(mock('column', :type => :string, :limit => 255))
177
187
  @new_post.stub!(:column_for_attribute).with(:title).and_return(mock('column', :type => :string, :limit => 50))
178
188
  @new_post.stub!(:column_for_attribute).with(:body).and_return(mock('column', :type => :text))
@@ -180,10 +190,17 @@ module FormtasticSpecHelper
180
190
  @new_post.stub!(:column_for_attribute).with(:publish_at).and_return(mock('column', :type => :date))
181
191
  @new_post.stub!(:column_for_attribute).with(:time_zone).and_return(mock('column', :type => :string))
182
192
  @new_post.stub!(:column_for_attribute).with(:allow_comments).and_return(mock('column', :type => :boolean))
193
+ @new_post.stub!(:column_for_attribute).with(:author).and_return(mock('column', :type => :integer))
194
+ @new_post.stub!(:column_for_attribute).with(:country).and_return(mock('column', :type => :string, :limit => 255))
195
+ @new_post.stub!(:column_for_attribute).with(:country_subdivision).and_return(mock('column', :type => :string, :limit => 255))
196
+ @new_post.stub!(:column_for_attribute).with(:country_code).and_return(mock('column', :type => :string, :limit => 255))
183
197
 
184
198
  @new_post.stub!(:author).and_return(@bob)
185
199
  @new_post.stub!(:author_id).and_return(@bob.id)
186
200
 
201
+ @new_post.stub!(:reviewer).and_return(@fred)
202
+ @new_post.stub!(:reviewer_id).and_return(@fred.id)
203
+
187
204
  @new_post.should_receive(:publish_at=).any_number_of_times
188
205
  @new_post.should_receive(:title=).any_number_of_times
189
206
  @new_post.stub!(:main_post_id).and_return(nil)
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 9
8
- - 8
9
- version: 0.9.8
8
+ - 9
9
+ version: 0.9.9
10
10
  platform: ruby
11
11
  authors:
12
12
  - Justin French
@@ -14,7 +14,7 @@ autorequire:
14
14
  bindir: bin
15
15
  cert_chain: []
16
16
 
17
- date: 2010-03-31 00:00:00 +11:00
17
+ date: 2010-05-25 00:00:00 +10:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
@@ -97,6 +97,7 @@ files:
97
97
  - lib/formtastic.rb
98
98
  - lib/formtastic/i18n.rb
99
99
  - lib/formtastic/layout_helper.rb
100
+ - lib/formtastic/util.rb
100
101
  - lib/locale/en.yml
101
102
  - rails/init.rb
102
103
  - spec/buttons_spec.rb