Skip to content

Commit

Permalink
:datetime, :date and :time selects now support a :default option in f…
Browse files Browse the repository at this point in the history
…avour of a deprecated :selected option (issue formtastic#152):

* if the object has a value, we should pre-select that at all times, ignoring the :default option
* if the object has no value, and :default is provided as a Date/Time then :default should be pre-selected
* if the object has no value, and :default is provided as nil then no option should be pre-selected (it will default to the first)
* if the object has no value, and :default is not provided then fallback to Time.now (or perhaps a configurable default)
* :selected invokes a deprecation warning, but is an alias of :default
* it's not a real deprecation, as the behaviour has changed with all the bugs and half-fixes
  • Loading branch information
justinfrench committed Jan 8, 2010
1 parent 0dafaf1 commit 326b556
Show file tree
Hide file tree
Showing 5 changed files with 198 additions and 134 deletions.
20 changes: 12 additions & 8 deletions lib/formtastic.rb
Expand Up @@ -944,6 +944,11 @@ def time_input(method, options)
# This is an absolute abomination, but so is the official Rails select_date().
#
def date_or_datetime_input(method, options)
if options.key?(:selected)
::ActiveSupport::Deprecation.warn(":selected is deprecated (and may still have changed behavior) in #{options[:as]} inputs, use :default instead, see commit 09fc6b4 and issue #152 on github.com/justinfrench/formtastic")
options[:default] = options[:selected]
end

position = { :year => 1, :month => 2, :day => 3, :hour => 4, :minute => 5, :second => 6 }
i18n_date_order = ::I18n.t(:order, :scope => [:date])
i18n_date_order = nil unless i18n_date_order.is_a?(Array)
Expand All @@ -954,12 +959,11 @@ def date_or_datetime_input(method, options)

list_items_capture = ""
hidden_fields_capture = ""

default_time = options.has_key?(:selected) ? options[:selected] : ::Time.now

# Gets the datetime object. It can be a Fixnum, Date or Time, or nil.
datetime = options[:selected] || (@object ? @object.send(method) : default_time) || default_time

options[:default] = ::Time.now unless options.key?(:default) # can't do an ||= because nil is an important value
options[:default] = @object.send(method) if @object && @object.respond_to?(method) && @object.send(method) # object trumps :default
datetime = options[:default]

html_options = options.delete(:input_html) || {}
input_ids = []

Expand All @@ -970,15 +974,15 @@ def date_or_datetime_input(method, options)
if options[:"discard_#{input}"]
break if time_inputs.include?(input)

hidden_value = datetime.respond_to?(input) ? datetime.send(input.to_sym) : datetime
hidden_value = options[:default].respond_to?(input.to_sym) ? options[:default].send(input.to_sym) : options[:default]
hidden_fields_capture << template.hidden_field_tag("#{@object_name}[#{field_name}]", (hidden_value || 1), :id => input_id)
else
opts = strip_formtastic_options(options).merge(:prefix => @object_name, :field_name => field_name, :default => datetime)
opts = strip_formtastic_options(options).merge(:prefix => @object_name, :field_name => field_name, :default => options[:default])
item_label_text = ::I18n.t(input.to_s, :default => input.to_s.humanize, :scope => [:datetime, :prompts])

list_items_capture << template.content_tag(:li,
template.content_tag(:label, item_label_text, :for => input_id) <<
template.send(:"select_#{input}", datetime, opts, html_options.merge(:id => input_id))
template.send(:"select_#{input}", options[:default], opts, html_options.merge(:id => input_id))
)
end
end
Expand Down
101 changes: 0 additions & 101 deletions spec/custom_macros.rb
Expand Up @@ -224,107 +224,6 @@ def it_should_call_find_on_association_class_when_no_collection_is_provided(as)
end
end

def it_should_select_existing_datetime_else_current(*datetime_parts)
describe "default value" do
before do
@new_post.should_receive(:publish_at=).any_number_of_times
end

describe "when attribute value is present" do
before do
@output_buffer = ''
publish_at_value = 1.year.ago + 2.month + 3.day + 4.hours + 5.minutes # No comment =)
@new_post.stub!(:publish_at).and_return(publish_at_value)

semantic_form_for(@new_post) do |builder|
concat(builder.input(:publish_at, :as => :datetime))
end
end

it "should select the present value by default" do
# puts output_buffer
output_buffer.should have_tag("form li select#post_publish_at_1i option[@selected='selected'][@value='#{@new_post.publish_at.year}']") if datetime_parts.include?(:year)
output_buffer.should have_tag("form li select#post_publish_at_2i option[@selected='selected'][@value='#{@new_post.publish_at.month}']") if datetime_parts.include?(:month)
output_buffer.should have_tag("form li select#post_publish_at_3i option[@selected='selected'][@value='#{@new_post.publish_at.day}']") if datetime_parts.include?(:day)
output_buffer.should have_tag("form li select#post_publish_at_4i option[@selected='selected'][@value='#{@new_post.publish_at.strftime("%H")}']") if datetime_parts.include?(:hour)
output_buffer.should have_tag("form li select#post_publish_at_5i option[@selected='selected'][@value='#{@new_post.publish_at.strftime("%M")}']") if datetime_parts.include?(:minute)
#output_buffer.should have_tag("form li select#post_publish_at_6i option[@selected='selected'][@value='#{@new_post.publish_at.sec}']") if datetime_parts.include?(:second)
end
end

describe "when no attribute value is present" do
before do
@output_buffer = ''
@new_post.stub!(:publish_at).and_return(nil)
@current_time = ::Time.now

semantic_form_for(@new_post) do |builder|
concat(builder.input(:publish_at, :as => :datetime))
end
end

it "should select the current day/time by default" do
# puts output_buffer
output_buffer.should have_tag("form li select#post_publish_at_1i option[@selected='selected'][@value='#{@current_time.year}']") if datetime_parts.include?(:year)
output_buffer.should have_tag("form li select#post_publish_at_2i option[@selected='selected'][@value='#{@current_time.month}']") if datetime_parts.include?(:month)
output_buffer.should have_tag("form li select#post_publish_at_3i option[@selected='selected'][@value='#{@current_time.day}']") if datetime_parts.include?(:day)
output_buffer.should have_tag("form li select#post_publish_at_4i option[@selected='selected'][@value='#{@current_time.strftime("%H")}']") if datetime_parts.include?(:hour)
output_buffer.should have_tag("form li select#post_publish_at_5i option[@selected='selected'][@value='#{@current_time.strftime("%M")}']") if datetime_parts.include?(:minute)
#output_buffer.should have_tag("form li select#post_publish_at_6i option[@selected='selected'][@value='#{@custom_default_time.sec}']") if datetime_parts.include?(:second)
end

# TODO: Scenario when current time is not a possible choice (because of specified date/time ranges)?
end
end
end

def it_should_select_explicit_default_value_if_set(*datetime_parts)
describe 'when :selected is set' do
before do
@output_buffer = ''
end

# Note: Not possible to override default selected value for time_zone input
# without overriding Rails core helper. This Rails helper works "a bit different". =/
#
describe "no selected items" do
before do
@default_time = 2.days.ago
@new_post.stub!(:publish_at).and_return(@default_time)

semantic_form_for(@new_post) do |builder|
concat(builder.input(:publish_at, :as => :time_zone, :selected => nil))
end
end

it 'should not have any selected item(s)' do
output_buffer.should_not have_tag("form li select#post_publish_at_1i option[@selected='selected']")
end
end

describe "single selected item" do
before do
@custom_default_time = 5.days.ago
@new_post.stub!(:publish_at).and_return(2.days.ago)

semantic_form_for(@new_post) do |builder|
concat(builder.input(:publish_at, :as => :datetime, :selected => @custom_default_time))
end
end

it "should select the specified value" do
output_buffer.should have_tag("form li select#post_publish_at_1i option[@selected='selected'][@value='#{@custom_default_time.year}']") if datetime_parts.include?(:year)
output_buffer.should have_tag("form li select#post_publish_at_2i option[@selected='selected'][@value='#{@custom_default_time.month}']") if datetime_parts.include?(:month)
output_buffer.should have_tag("form li select#post_publish_at_3i option[@selected='selected'][@value='#{@custom_default_time.day}']") if datetime_parts.include?(:day)
output_buffer.should have_tag("form li select#post_publish_at_4i option[@selected='selected'][@value='#{@custom_default_time.strftime("%H")}']") if datetime_parts.include?(:hour)
output_buffer.should have_tag("form li select#post_publish_at_5i option[@selected='selected'][@value='#{@custom_default_time.strftime("%M")}']") if datetime_parts.include?(:minute)
#output_buffer.should have_tag("form li select#post_publish_at_6i option[@selected='selected'][@value='#{@custom_default_time.sec}']") if datetime_parts.include?(:second)
end
end

end
end

def it_should_use_the_collection_when_provided(as, countable)
describe 'when the :collection option is provided' do

Expand Down
71 changes: 60 additions & 11 deletions spec/inputs/date_input_spec.rb
Expand Up @@ -38,23 +38,72 @@
it 'should have three selects for year, month and day' do
output_buffer.should have_tag('form li.date fieldset ol li select', :count => 3)
end

it_should_select_existing_datetime_else_current(:year, :month, :day)
it_should_select_explicit_default_value_if_set(:year, :month, :day)

describe "when :selected is nil" do

before(:each) do
output_buffer.replace ''
semantic_form_for(:project, :url => 'http://test.host') do |builder|
concat(builder.input(:publish_at, :as => :datetime, :selected => nil))
describe ':default option' do

describe "when the object has a value" do
it "should select the object value (ignoring :default)" do
output_buffer.replace ''
@new_post.stub!(:created_at => Time.mktime(2012))
semantic_form_for(@new_post) do |builder|
concat(builder.input(:created_at, :as => :date, :default => Time.mktime(1999)))
end
output_buffer.should have_tag("form li ol li select#post_created_at_1i option[@selected]", :count => 1)
output_buffer.should have_tag("form li ol li select#post_created_at_1i option[@value='2012'][@selected]", :count => 1)
end
end

it "should not pre-select any options" do
output_buffer.should_not have_tag("form li.datetime li select option[@selected]")
describe 'when the object has no value' do
it "should select the :default if provided as a Date" do
output_buffer.replace ''
@new_post.stub!(:created_at => nil)
semantic_form_for(@new_post) do |builder|
concat(builder.input(:created_at, :as => :date, :default => Date.new(1999)))
end
output_buffer.should have_tag("form li ol li select#post_created_at_1i option[@selected]", :count => 1)
output_buffer.should have_tag("form li ol li select#post_created_at_1i option[@value='1999'][@selected]", :count => 1)
end

it "should select the :default if provided as a Time" do
output_buffer.replace ''
@new_post.stub!(:created_at => nil)
semantic_form_for(@new_post) do |builder|
concat(builder.input(:created_at, :as => :date, :default => Time.mktime(1999)))
end
output_buffer.should have_tag("form li ol li select#post_created_at_1i option[@selected]", :count => 1)
output_buffer.should have_tag("form li ol li select#post_created_at_1i option[@value='1999'][@selected]", :count => 1)
end

it "should not select an option if the :default is provided as nil" do
output_buffer.replace ''
@new_post.stub!(:created_at => nil)
semantic_form_for(@new_post) do |builder|
concat(builder.input(:created_at, :as => :date, :default => nil))
end
output_buffer.should_not have_tag("form li ol li select#post_created_at_1i option[@selected]")
end

it "should select Time.now if a :default is not provided" do
output_buffer.replace ''
@new_post.stub!(:created_at => nil)
semantic_form_for(@new_post) do |builder|
concat(builder.input(:created_at, :as => :date))
end
output_buffer.should have_tag("form li ol li select#post_created_at_1i option[@selected]", :count => 1)
output_buffer.should have_tag("form li ol li select#post_created_at_1i option[@value='#{Time.now.year}'][@selected]", :count => 1)

end
end

end

it 'should warn about :selected deprecation' do
with_deprecation_silenced do
::ActiveSupport::Deprecation.should_receive(:warn)
semantic_form_for(@new_post) do |builder|
concat(builder.input(:created_at, :as => :date, :selected => Date.new(1999)))
end
end
end

end
81 changes: 70 additions & 11 deletions spec/inputs/datetime_input_spec.rb
Expand Up @@ -59,9 +59,6 @@
end
end

it_should_select_existing_datetime_else_current(:year, :month, :day, :hour, :minute, :second)
it_should_select_explicit_default_value_if_set(:year, :month, :day, :hour, :minute, :second)

describe 'when :discard_input => true is set' do
it 'should use default attribute value when it is not nil' do
@new_post.stub!(:publish_at).and_return(Date.new(2007,12,27))
Expand Down Expand Up @@ -150,20 +147,82 @@
output_buffer.should have_tag('form li.datetime fieldset ol li select', :count => 5)
end
end

describe "when :selected is nil" do
describe ':default option' do

before(:each) do
output_buffer.replace ''
semantic_form_for(:project, :url => 'http://test.host') do |builder|
concat(builder.input(:publish_at, :as => :datetime, :selected => nil))
describe "when the object has a value" do
it "should select the object value (ignoring :default)" do
output_buffer.replace ''
@new_post.stub!(:created_at => Time.mktime(2012))
semantic_form_for(@new_post) do |builder|
concat(builder.input(:created_at, :as => :datetime, :default => Time.mktime(1999)))
end
output_buffer.should have_tag("form li ol li select#post_created_at_1i option[@selected]", :count => 1)
output_buffer.should have_tag("form li ol li select#post_created_at_1i option[@value='2012'][@selected]", :count => 1)
end
end

it "should not pre-select any options" do
output_buffer.should_not have_tag("form li.datetime li select option[@selected]")
describe 'when the object has no value' do
it "should select the :default if provided as a Date" do
output_buffer.replace ''
@new_post.stub!(:created_at => nil)
semantic_form_for(@new_post) do |builder|
concat(builder.input(:created_at, :as => :datetime, :default => Date.new(1999)))
end
output_buffer.should have_tag("form li ol li select#post_created_at_1i option[@selected]", :count => 1)
output_buffer.should have_tag("form li ol li select#post_created_at_1i option[@value='1999'][@selected]", :count => 1)
end

it "should select the :default if provided as a Time" do
output_buffer.replace ''
@new_post.stub!(:created_at => nil)
semantic_form_for(@new_post) do |builder|
concat(builder.input(:created_at, :as => :datetime, :default => Time.mktime(1999)))
end
output_buffer.should have_tag("form li ol li select#post_created_at_1i option[@selected]", :count => 1)
output_buffer.should have_tag("form li ol li select#post_created_at_1i option[@value='1999'][@selected]", :count => 1)
end

it "should not select an option if the :default is provided as nil" do
output_buffer.replace ''
@new_post.stub!(:created_at => nil)
semantic_form_for(@new_post) do |builder|
concat(builder.input(:created_at, :as => :datetime, :default => nil))
end
output_buffer.should_not have_tag("form li ol li select#post_created_at_1i option[@selected]")
end

it "should select Time.now if a :default is not provided" do
output_buffer.replace ''
@new_post.stub!(:created_at => nil)
semantic_form_for(@new_post) do |builder|
concat(builder.input(:created_at, :as => :datetime))
end
output_buffer.should have_tag("form li ol li select#post_created_at_1i option[@selected]", :count => 1)
output_buffer.should have_tag("form li ol li select#post_created_at_1i option[@value='#{Time.now.year}'][@selected]", :count => 1)

end
end

it 'should warn about :selected deprecation' do
with_deprecation_silenced do
::ActiveSupport::Deprecation.should_receive(:warn)
semantic_form_for(@new_post) do |builder|
concat(builder.input(:created_at, :as => :date, :selected => Time.mktime(1999)))
end
end
end

end

it 'should warn about :selected deprecation' do
with_deprecation_silenced do
::ActiveSupport::Deprecation.should_receive(:warn)
semantic_form_for(@new_post) do |builder|
concat(builder.input(:created_at, :as => :datetime, :selected => Time.mktime(1999)))
end
end
end

end

0 comments on commit 326b556

Please sign in to comment.