Browse files

Merge remote-tracking branch 'okonski/range_helper' into refactor. Give

numeric functionality for inputs and ValidationHelper#range_options_for.

Conflicts:
	lib/formtastic/form_builder.rb
	lib/formtastic/inputs/numeric_input.rb
  • Loading branch information...
2 parents 368d878 + 60c6b3f commit d32f348e5b6ab8c7f1702439345ec98226349d46 @farnoy farnoy committed Apr 5, 2011
View
3 README.textile
@@ -305,14 +305,15 @@ The Formtastic input types:
* @:time@ - a time select. Default for column types: @:time@.
* @:boolean@ - a checkbox. Default for column types: @:boolean@.
* @:string@ - a text field. Default for column types: @:string@.
-* @:numeric@ - a text field (just like string). Default for column types: @:integer@, @:float@, and @:decimal@.
+* @:numeric@ - a text field (just like string). Default for column types: @:integer@, @:float@, and @:decimal@. Handles range validations.
* @:file@ - a file field. Default for file-attachment attributes matching: "paperclip":http://github.com/thoughtbot/paperclip or "attachment_fu":http://github.com/technoweenie/attachment_fu.
* @:country@ - a select menu of country names. Default for column types: :string with name @"country"@ - requires a *country_select* plugin to be installed.
* @:email@ - a text field (just like string). Default for columns with name matching @"email"@. New in HTML5. Works on some mobile browsers already.
* @:url@ - a text field (just like string). Default for columns with name matching @"url"@. New in HTML5. Works on some mobile browsers already.
* @: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. Handles range validations.
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
34 lib/formtastic/form_builder.rb
@@ -1,3 +1,37 @@
+<<<<<<< HEAD
+=======
+$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__))))
+
+require 'html_attributes'
+
+require 'helpers/inputs_helper'
+require 'helpers/buttons_helper'
+require 'helpers/label_helper'
+require 'helpers/errors_helper'
+require 'helpers/validation_helper'
+
+require 'inputs/boolean_input'
+require 'inputs/check_boxes_input'
+require 'inputs/country_input'
+require 'inputs/datetime_input'
+require 'inputs/date_input'
+require 'inputs/email_input'
+require 'inputs/file_input'
+require 'inputs/hidden_input'
+require 'inputs/numeric_input'
+require 'inputs/password_input'
+require 'inputs/phone_input'
+require 'inputs/radio_input'
+require 'inputs/range_input'
+require 'inputs/search_input'
+require 'inputs/select_input'
+require 'inputs/string_input'
+require 'inputs/text_input'
+require 'inputs/time_input'
+require 'inputs/time_zone_input'
+require 'inputs/url_input'
+
+>>>>>>> okonski/range_helper
module Formtastic
class FormBuilder < ActionView::Helpers::FormBuilder
View
39 lib/formtastic/helpers/validation_helper.rb
@@ -0,0 +1,39 @@
+module Formtastic
+ module Helpers
+ module ValidationHelper
+
+ def range_options_for(method, options = {})
+ options[:input_html] ||= {}
+ if options[:in]
+ options[:input_html][:in] = options.delete :in
+ options[:input_html][:step] = options.delete :step || 1
+ return options
+ end
+
+ reflections = @object.class.reflect_on_validations_for(method) if @object.class.respond_to? :reflect_on_validations_for
+ reflections ||= []
+ reflections.each do |reflection|
+ if reflection.macro == :validates_numericality_of
+ if reflection.options.include?(:greater_than)
+ range_start = (reflection.options[:greater_than] + 1)
+ elsif reflection.options.include?(:greater_than_or_equal_to)
+ range_start = reflection.options[:greater_than_or_equal_to]
+ end
+ if reflection.options.include?(:less_than)
+ range_end = (reflection.options[:less_than] - 1)
+ elsif reflection.options.include?(:less_than_or_equal_to)
+ range_end = reflection.options[:less_than_or_equal_to]
+ end
+
+ options[:input_html][:in] = (range_start..range_end)
+ end
+ end
+
+ options[:input_html][:step] ||= 1
+
+ return options
+ end
+
+ end
+ end
+end
View
9 lib/formtastic/inputs/numeric_input.rb
@@ -1,3 +1,5 @@
+require 'inputs/basic'
+
module Formtastic
module Inputs
@@ -39,6 +41,11 @@ def to_html
builder.number_field(method, input_html_options)
end
end
+
+ # Outputs a label and standard Rails text field inside the wrapper.
+ def numeric_input(method, options)
+ basic_input_helper(:number_field, :numeric, method, range_options_for(method, options))
+ end
end
end
-end
+end
View
12 lib/formtastic/inputs/range_input.rb
@@ -0,0 +1,12 @@
+module Formtastic
+ module Inputs
+ module RangeInput
+ include Formtastic::Inputs::Base
+
+ def range_input(method, options)
+ basic_input_helper(:range_field, :numeric, method, range_options_for(method, options))
+ end
+
+ end
+ end
+end
View
88 spec/inputs/range_input_spec.rb
@@ -0,0 +1,88 @@
+# 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; @options = {} }
+
+ describe "with validation_reflection" do
+
+ before do
+ # Insane, but we need to test with and without validation_reflection
+ ::Author.stub!(:reflect_on_validations_for).with(:age).and_return([ActiveRecord::Reflection::MacroReflection.new(:validates_numericality_of, :age, {:greater_than => 0, :less_than => 6}, ::Author)])
+ end
+
+ it "works with alternate validation options" do
+ ::Author.stub!(:reflect_on_validations_for).with(:age).and_return([ActiveRecord::Reflection::MacroReflection.new(:validates_numericality_of, :age, {:greater_than_or_equal_to => 1930, :less_than_or_equal_to => (Time.now.year - 5)}, ::Author)])
+
+ concat(semantic_form_for(@james) do |builder|
+ concat(builder.input(:age, @options.merge(:as => :range)))
+ end)
+
+ output_buffer.should have_tag("form li input[step=\"1\"][min=\"1930\"][max=\"#{Time.now.year - 5}\"]")
+ end
+
+ it "assigns range and step from model" do
+ concat(semantic_form_for(@james) do |builder|
+ concat(builder.input(:age, @options.merge(:as => :range)))
+ end)
+
+ output_buffer.should have_tag("form li input[step=\"1\"][min=\"1\"][max=\"5\"]")
+ end
+
+ it "allows for overriding range and step values" do
+ @options.merge! :in => 9..10, :step => 0.2
+
+ concat(semantic_form_for(@james) do |builder|
+ concat(builder.input(:age, @options.merge(:as => :range)))
+ end)
+
+ output_buffer.should have_tag("form li input[step=\"0.2\"][min=\"9\"][max=\"10\"]")
+ end
+
+ end
+
+ end
+
+end
+
View
3 spec/spec_helper.rb
@@ -121,6 +121,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))
@@ -130,6 +131,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([])
@@ -142,6 +144,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 d32f348

Please sign in to comment.