Browse files

Merge branch 'refactor' of https://github.com/okonski/formtastic into…

… okonski-refactor

This was a pretty epic merge. Ended up rewriting most of the tests and leveraging some existing code instead of wmerging in new code that does similar things, but I think the results are right.

Many thanks to Jakub Okoński.
  • Loading branch information...
2 parents 503b22d + a2ed3e7 commit d877b6b8edff322df1a102bc85ae33e887f3fc34 @justinfrench committed Apr 6, 2011
View
1 README.textile
@@ -313,6 +313,7 @@ The Formtastic input types:
* @:phone@ - a text field (just like string). Default for columns with name matching @"phone"@ or @"fax"@. New in HTML5.
* @:search@ - a text field (just like string). Default for columns with name matching @"search"@. New in HTML5. Works on Safari.
* @:hidden@ - a hidden field. Creates a hidden field (added for compatibility).
+* @:range@ - a slider field.
The comments in the code are pretty good for each of these (what it does, what the output is, what the options are, etc.) so go check it out.
View
6 lib/formtastic/form_builder.rb
@@ -30,14 +30,16 @@ def self.configure(name, value = nil)
configure :default_hint_class, 'inline-hints'
attr_reader :template
-
+
attr_reader :auto_index
include Formtastic::HtmlAttributes
include Formtastic::Helpers::InputsHelper
include Formtastic::Helpers::ButtonsHelper
include Formtastic::Helpers::ErrorsHelper
+
end
-end
+end
+
View
1 lib/formtastic/helpers.rb
@@ -10,3 +10,4 @@ module Helpers
autoload :SemanticFormHelper, 'formtastic/helpers/semantic_form_helper'
end
end
+
View
2 lib/formtastic/inputs.rb
@@ -17,6 +17,7 @@ module Inputs
autoload :PasswordInput
autoload :PhoneInput
autoload :RadioInput
+ autoload :RangeInput
autoload :SearchInput
autoload :SelectInput
autoload :StringInput
@@ -27,3 +28,4 @@ module Inputs
autoload :UrlInput
end
end
+
View
11 lib/formtastic/inputs/base/validations.rb
@@ -67,6 +67,17 @@ def validation_max
nil
end
end
+
+ def validation_step
+ validation = validations? && validations.find do |validation|
+ validation.kind == :numericality
+ end
+ if validation
+ validation.options[:step]
+ else
+ nil
+ end
+ end
def validation_integer_only?
validation = validations? && validations.find do |validation|
View
3 lib/formtastic/inputs/numeric_input.rb
@@ -17,4 +17,5 @@ def initialize(builder, template, object, object_name, method, options)
end
end
-end
+end
+
View
39 lib/formtastic/inputs/range_input.rb
@@ -0,0 +1,39 @@
+module Formtastic
+ module Inputs
+ class RangeInput
+ include Base
+ include Base::Stringish
+
+ def to_html
+ input_wrapping do
+ label_html <<
+ builder.range_field(method, input_html_options)
+ end
+ end
+
+ # options[:input_html][:max] trumps :input_html[:max] trumps validations trumps default
+ def input_html_options
+ input_html_options_from_validations.merge(input_html_options_from_options).merge(super)
+ end
+
+ def input_html_options_from_options
+ hash = {}
+ hash[:in] = options[:in] if options.key?(:in)
+ hash[:min] = options[:min] if options.key?(:min)
+ hash[:max] = options[:max] if options.key?(:max)
+ hash[:step] = options[:step] if options.key?(:step)
+ hash
+ end
+
+ def input_html_options_from_validations
+ {
+ :step => validation_step || 1,
+ :min => validation_min || 1,
+ :max => validation_max || 100
+ }
+ end
+
+ end
+ end
+end
+
View
132 spec/inputs/range_input_spec.rb
@@ -0,0 +1,132 @@
+# encoding: utf-8
+require 'spec_helper'
+require 'active_record'
+
+describe 'range input' do
+
+ include FormtasticSpecHelper
+
+ before do
+ @output_buffer = ''
+ mock_everything
+ end
+
+ describe "when object is provided" do
+ before do
+ concat(semantic_form_for(@bob) do |builder|
+ concat(builder.input(:age, :as => :range))
+ end)
+ end
+
+ it_should_have_input_wrapper_with_class(:range)
+ it_should_have_input_wrapper_with_id("author_age_input")
+ it_should_have_label_with_text(/Age/)
+ it_should_have_label_for("author_age")
+ it_should_have_input_with_id("author_age")
+ it_should_have_input_with_type(:range)
+ it_should_have_input_with_name("author[age]")
+
+ end
+
+ describe "when namespace is provided" do
+
+ before do
+ concat(semantic_form_for(@james, :namespace => "context2") do |builder|
+ concat(builder.input(:age, :as => :range))
+ end)
+ end
+
+ it_should_have_input_wrapper_with_id("context2_author_age_input")
+ it_should_have_label_and_input_with_id("context2_author_age")
+
+ end
+
+ describe "core processing" do
+
+ before { mock_everything }
+
+ it "uses min and max from greater_than_or_equal_to and less_than_or_equal_to validation options" do
+ @james.class.stub!(:validators_on).with(:age).and_return([
+ active_model_numericality_validator([:age], {:only_integer=>false, :allow_nil=>false, :greater_than_or_equal_to=>1930, :less_than_or_equal_to=>(Time.now.year-5)})
+ ])
+
+ concat(semantic_form_for(@james) do |builder|
+ concat(builder.input(:age, :as => :range))
+ end)
+
+ output_buffer.should have_tag("form li input[step=\"1\"][min=\"1930\"][max=\"#{Time.now.year - 5}\"]")
+ end
+
+ it "adjusts greater_than and less_than validation options by 1" do
+ @james.class.stub!(:validators_on).with(:age).and_return([
+ active_model_numericality_validator([:age], {:only_integer=>false, :allow_nil=>false, :greater_than=>0, :less_than=>6})
+ ])
+
+ concat(semantic_form_for(@james) do |builder|
+ concat(builder.input(:age, :as => :range))
+ end)
+
+ output_buffer.should have_tag("form li input[step=\"1\"][min=\"1\"][max=\"5\"]")
+ end
+
+ it "defaults the step to 1" do
+ concat(semantic_form_for(@james) do |builder|
+ concat(builder.input(:age, :as => :range))
+ end)
+ output_buffer.should have_tag("form li input[step=\"1\"]")
+ end
+
+ it "defaults the min to 1" do
+ concat(semantic_form_for(@james) do |builder|
+ concat(builder.input(:age, :as => :range))
+ end)
+ output_buffer.should have_tag("form li input[min=\"1\"]")
+ end
+
+ it "defaults the max to 100" do
+ concat(semantic_form_for(@james) do |builder|
+ concat(builder.input(:age, :as => :range))
+ end)
+ output_buffer.should have_tag("form li input[max=\"100\"]")
+ end
+
+ it "will look for the non-standard :step validation option" do
+ @james.class.stub!(:validators_on).with(:age).and_return([
+ active_model_numericality_validator([:age], {:only_integer=>false, :allow_nil=>false, :greater_than=>0, :less_than=>6, :step => 0.5})
+ ])
+
+ concat(semantic_form_for(@james) do |builder|
+ concat(builder.input(:age, :as => :range))
+ end)
+
+ output_buffer.should have_tag("form li input[step=\"0.5\"][min=\"1\"][max=\"5\"]")
+ end
+
+ it "allows options hash to override validations" do
+ @james.class.stub!(:validators_on).with(:age).and_return([
+ active_model_numericality_validator([:age], {:only_integer=>false, :allow_nil=>false, :greater_than=>5, :less_than=>50, :step => 0.5})
+ ])
+
+ concat(semantic_form_for(@james) do |builder|
+ concat(builder.input(:age, :as => :range, :in => 52..108, :step => 2))
+ end)
+
+ output_buffer.should have_tag("form li input[step=\"2\"][min=\"52\"][max=\"108\"]")
+ end
+
+ it "should allow input_html hash to override validations and options" do
+ @james.class.stub!(:validators_on).with(:age).and_return([
+ active_model_numericality_validator([:age], {:only_integer=>false, :allow_nil=>false, :greater_than=>5, :less_than=>50, :step => 0.5})
+ ])
+
+ concat(semantic_form_for(@james) do |builder|
+ concat(builder.input(:age, :as => :range, :in => 52..108, :step => 2, :input_html => { :in => 53..109, :step => 3 }))
+ end)
+
+ output_buffer.should have_tag("form li input[step=\"3\"][min=\"53\"][max=\"109\"]")
+ end
+
+ end
+
+end
+
View
3 spec/spec_helper.rb
@@ -125,6 +125,7 @@ def new_author_path; "/authors/new"; end
@fred.stub!(:class).and_return(::Author)
@fred.stub!(:to_label).and_return('Fred Smith')
@fred.stub!(:login).and_return('fred_smith')
+ @fred.stub!(:age).and_return(27)
@fred.stub!(:id).and_return(37)
@fred.stub!(:new_record?).and_return(false)
@fred.stub!(:errors).and_return(mock('errors', :[] => nil))
@@ -134,6 +135,7 @@ def new_author_path; "/authors/new"; end
@bob = ::Author.new
@bob.stub!(:to_label).and_return('Bob Rock')
@bob.stub!(:login).and_return('bob')
+ @bob.stub!(:age).and_return(43)
@bob.stub!(:created_at)
@bob.stub!(:id).and_return(42)
@bob.stub!(:posts).and_return([])
@@ -146,6 +148,7 @@ def new_author_path; "/authors/new"; end
@james = ::Author.new
@james.stub!(:to_label).and_return('James Shock')
@james.stub!(:login).and_return('james')
+ @james.stub!(:age).and_return(38)
@james.stub!(:id).and_return(75)
@james.stub!(:posts).and_return([])
@james.stub!(:post_ids).and_return([])

0 comments on commit d877b6b

Please sign in to comment.