├── .rspec ├── Gemfile ├── spec ├── spec.opts ├── support │ ├── output_buffer.rb │ └── test_environment.rb ├── helpers │ └── layout_helper_spec.rb ├── defaults_spec.rb ├── error_proc_spec.rb ├── inputs │ ├── url_input_spec.rb │ ├── email_input_spec.rb │ ├── phone_input_spec.rb │ ├── search_input_spec.rb │ ├── file_input_spec.rb │ ├── numeric_input_spec.rb │ ├── password_input_spec.rb │ ├── time_zone_input_spec.rb │ ├── hidden_input_spec.rb │ ├── date_input_spec.rb │ ├── country_input_spec.rb │ ├── text_input_spec.rb │ ├── boolean_input_spec.rb │ ├── time_input_spec.rb │ ├── string_input_spec.rb │ ├── radio_input_spec.rb │ ├── datetime_input_spec.rb │ └── check_boxes_input_spec.rb ├── include_blank_spec.rb ├── custom_builder_spec.rb ├── semantic_fields_for_spec.rb ├── semantic_errors_spec.rb ├── label_spec.rb ├── buttons_spec.rb ├── i18n_spec.rb ├── form_helper_spec.rb ├── errors_spec.rb ├── spec_helper.rb └── commit_button_spec.rb ├── rails └── init.rb ├── .gitignore ├── lib ├── generators │ ├── templates │ │ ├── _form.html.haml │ │ ├── _form.html.erb │ │ ├── formtastic_changes.css │ │ ├── formtastic.rb │ │ └── formtastic.css │ └── formtastic │ │ ├── install │ │ └── install_generator.rb │ │ └── form │ │ └── form_generator.rb ├── locale │ └── en.yml └── formtastic │ ├── layout_helper.rb │ ├── railtie.rb │ ├── i18n.rb │ └── util.rb ├── init.rb ├── RELEASE_PROCESS ├── generators ├── form │ ├── USAGE │ └── form_generator.rb └── formtastic │ └── formtastic_generator.rb ├── Rakefile ├── MIT-LICENSE ├── formtastic.gemspec └── CHANGELOG /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format=progress 3 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source :rubygems 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /spec/spec.opts: -------------------------------------------------------------------------------- 1 | --color 2 | --format=progress 3 | -------------------------------------------------------------------------------- /rails/init.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require File.expand_path(File.join(File.dirname(__FILE__), "..", "init")) 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .rvmrc 3 | coverage 4 | pkg 5 | *~ 6 | *watchr.rb 7 | log/* 8 | .rvmrc 9 | .bundle 10 | Gemfile.lock 11 | /.redcar -------------------------------------------------------------------------------- /lib/generators/templates/_form.html.haml: -------------------------------------------------------------------------------- 1 | - f.inputs do 2 | <% attributes.each do |attribute| -%> 3 | = f.input :<%= attribute.name %> 4 | <% end -%> 5 | -------------------------------------------------------------------------------- /spec/support/output_buffer.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | module ActionView 4 | class OutputBuffer < Formtastic::Util.rails_safe_buffer_class 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /lib/generators/templates/_form.html.erb: -------------------------------------------------------------------------------- 1 | <%% f.inputs do %> 2 | <% attributes.each do |attribute| -%> 3 | <%%= f.input :<%= attribute.name %> %> 4 | <% end -%> 5 | <%% end %> 6 | -------------------------------------------------------------------------------- /lib/locale/en.yml: -------------------------------------------------------------------------------- 1 | en: 2 | formtastic: 3 | :yes: 'Yes' 4 | :no: 'No' 5 | :create: 'Create %{model}' 6 | :update: 'Update %{model}' 7 | :required: 'required' 8 | -------------------------------------------------------------------------------- /init.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'formtastic' 3 | require 'formtastic/layout_helper' 4 | ActionView::Base.send :include, Formtastic::SemanticFormHelper 5 | ActionView::Base.send :include, Formtastic::LayoutHelper 6 | -------------------------------------------------------------------------------- /lib/formtastic/layout_helper.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | module Formtastic 4 | module LayoutHelper 5 | 6 | def formtastic_stylesheet_link_tag 7 | stylesheet_link_tag("formtastic") + 8 | stylesheet_link_tag("formtastic_changes") 9 | end 10 | 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /RELEASE_PROCESS: -------------------------------------------------------------------------------- 1 | # edit gemspec version number, date 2 | git ci formtastic.gemspec -m "new gemspec" # commit changes 3 | git tag X.X.X # tag the new version in the code base too 4 | gem build formtastic.gemspec # build the gem 5 | gem push formtastic-X.X.X.gem # publish the gem 6 | git push && git push --tags # push to remote 7 | -------------------------------------------------------------------------------- /lib/formtastic/railtie.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | require 'formtastic' 4 | require 'formtastic/layout_helper' 5 | require 'rails' 6 | 7 | module Formtastic 8 | class Railtie < Rails::Railtie 9 | initializer 'formtastic.initialize', :after => :after_initialize do 10 | ActionView::Base.send :include, Formtastic::SemanticFormHelper 11 | ActionView::Base.send(:include, Formtastic::LayoutHelper) 12 | end 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /spec/helpers/layout_helper_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | 4 | describe Formtastic::LayoutHelper do 5 | 6 | include FormtasticSpecHelper 7 | include Formtastic::LayoutHelper 8 | 9 | describe '#formtastic_stylesheet_link_tag' do 10 | 11 | it 'should render a link to formtastic.css' do 12 | formtastic_stylesheet_link_tag.should have_tag("link[@href='/stylesheets/formtastic.css']") 13 | end 14 | 15 | it 'should render a link to formtastic_changes.css' do 16 | formtastic_stylesheet_link_tag.should have_tag("link[@href='/stylesheets/formtastic_changes.css']") 17 | end 18 | 19 | end 20 | end 21 | 22 | -------------------------------------------------------------------------------- /spec/defaults_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | 4 | describe 'Formtastic::SemanticFormBuilder-defaults' do 5 | 6 | # Note: This spec might make better sense somewhere else. Just temporary. 7 | 8 | describe "required string" do 9 | 10 | it "should render proc with I18n correctly" do 11 | ::I18n.backend.store_translations :en, :formtastic => {:required => 'Haha!'} 12 | 13 | required_string = Formtastic::SemanticFormBuilder.required_string 14 | required_string = required_string.is_a?(::Proc) ? required_string.call : required_string.to_s 15 | required_string.should == %{*} 16 | end 17 | 18 | end 19 | 20 | end 21 | -------------------------------------------------------------------------------- /spec/error_proc_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | 4 | describe 'Rails field_error_proc' do 5 | 6 | include FormtasticSpecHelper 7 | 8 | before do 9 | @output_buffer = '' 10 | mock_everything 11 | end 12 | 13 | it "should not be overridden globally for all form builders" do 14 | current_field_error_proc = ::ActionView::Base.field_error_proc 15 | 16 | semantic_form_for(@new_post) do |builder| 17 | ::ActionView::Base.field_error_proc.should_not == current_field_error_proc 18 | end 19 | 20 | ::ActionView::Base.field_error_proc.should == current_field_error_proc 21 | 22 | form_for(@new_post) do |builder| 23 | ::ActionView::Base.field_error_proc.should == current_field_error_proc 24 | end 25 | end 26 | 27 | end 28 | -------------------------------------------------------------------------------- /lib/generators/formtastic/install/install_generator.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | module Formtastic 3 | class InstallGenerator < Rails::Generators::Base 4 | desc "Copies formtastic.css and formtastic_changes.css to public/stylesheets/ and a config initializer to config/initializers/formtastic_config.rb" 5 | 6 | source_root File.expand_path('../../../templates', __FILE__) 7 | 8 | def copy_files 9 | empty_directory 'config/initializers' 10 | template 'formtastic.rb', 'config/initializers/formtastic.rb' 11 | 12 | empty_directory 'public/stylesheets' 13 | template 'formtastic.css', 'public/stylesheets/formtastic.css' 14 | template 'formtastic_changes.css', 'public/stylesheets/formtastic_changes.css' 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /lib/generators/templates/formtastic_changes.css: -------------------------------------------------------------------------------- 1 | /* ------------------------------------------------------------------------------------------------- 2 | 3 | Load this stylesheet after formtastic.css in your layouts to override the CSS to suit your needs. 4 | This will allow you to update formtastic.css with new releases without clobbering your own changes. 5 | 6 | For example, to make the inline hint paragraphs a little darker in color than the standard #666: 7 | 8 | form.formtastic fieldset > ol > li p.inline-hints { color:#333; } 9 | 10 | HINT: 11 | The following style may be *conditionally* included for improved support on older versions of IE(<8) 12 | form.formtastic fieldset ol li fieldset legend { margin-left: -6px;} 13 | 14 | --------------------------------------------------------------------------------------------------*/ 15 | -------------------------------------------------------------------------------- /spec/support/test_environment.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'rspec_tag_matchers' 3 | 4 | RSpec.configure do |config| 5 | config.include RspecTagMatchers 6 | config.include CustomMacros 7 | config.mock_with :rspec 8 | end 9 | 10 | if Formtastic::Util.rails3? 11 | 12 | require "action_controller/railtie" 13 | require "active_resource/railtie" 14 | require 'active_model' 15 | 16 | # Create a simple rails application for use in testing the viewhelper 17 | module FormtasticTest 18 | class Application < Rails::Application 19 | # Configure the default encoding used in templates for Ruby 1.9. 20 | config.encoding = "utf-8" 21 | config.active_support.deprecation = :stderr 22 | end 23 | end 24 | FormtasticTest::Application.initialize! 25 | 26 | require 'rspec/rails' 27 | end 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /generators/form/USAGE: -------------------------------------------------------------------------------- 1 | NAME 2 | form - Formtastic form generator. 3 | 4 | DESCRIPTION 5 | Generates formtastic form code based on an existing model. By default the generated code will be printed out directly in the terminal, and also copied to clipboard. Can optionally be saved into partial directly. 6 | 7 | Required: 8 | ExistingModelName - The name of an existing model for which the generator should generate form code. 9 | 10 | Options: 11 | --haml Generate HAML instead of ERB. 12 | --partial Generate a form partial in the model views path, i.e. "_form.html.erb" or _form.html.haml". 13 | --controller PATH Generate for custom controller/view path - in case model and controller namespace is different, i.e. "admin/posts". 14 | 15 | EXAMPLE 16 | ./script/generate form ExistingModelName [--haml] [--partial] -------------------------------------------------------------------------------- /generators/formtastic/formtastic_generator.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | class FormtasticGenerator < Rails::Generator::Base 4 | 5 | def manifest 6 | record do |m| 7 | m.directory File.join('config', 'initializers') 8 | m.template 'formtastic.rb', File.join('config', 'initializers', 'formtastic.rb') 9 | 10 | m.directory File.join('public', 'stylesheets') 11 | m.template 'formtastic.css', File.join('public', 'stylesheets', 'formtastic.css') 12 | m.template 'formtastic_changes.css', File.join('public', 'stylesheets', 'formtastic_changes.css') 13 | end 14 | end 15 | 16 | protected 17 | 18 | def banner 19 | %{Usage: #{$0} #{spec.name}\nCopies formtastic.css and formtastic_changes.css to public/stylesheets/ and a config initializer to config/initializers/formtastic.rb} 20 | end 21 | 22 | def source_root 23 | File.expand_path('../../../lib/generators/templates', __FILE__) 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /lib/formtastic/i18n.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | module Formtastic 4 | module I18n 5 | 6 | DEFAULT_SCOPE = [:formtastic].freeze 7 | DEFAULT_VALUES = YAML.load_file(File.expand_path("../../locale/en.yml", __FILE__))["en"]["formtastic"].freeze 8 | SCOPES = [ 9 | '%{model}.%{nested_model}.%{action}.%{attribute}', 10 | '%{model}.%{action}.%{attribute}', 11 | '%{model}.%{nested_model}.%{attribute}', 12 | '%{model}.%{attribute}', 13 | '%{nested_model}.%{attribute}', 14 | '%{attribute}' 15 | ] 16 | 17 | class << self 18 | 19 | def translate(*args) 20 | key = args.shift.to_sym 21 | options = args.extract_options! 22 | options.reverse_merge!(:default => DEFAULT_VALUES[key]) 23 | options[:scope] = [DEFAULT_SCOPE, options[:scope]].flatten.compact 24 | ::I18n.translate(key, *(args << options)) 25 | end 26 | alias :t :translate 27 | 28 | end 29 | 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'rubygems' 3 | require 'rake' 4 | require 'rake/rdoctask' 5 | require 'rspec/core/rake_task' 6 | 7 | desc 'Default: run unit specs.' 8 | task :default => :spec 9 | 10 | desc 'Generate documentation for the formtastic plugin.' 11 | Rake::RDocTask.new(:rdoc) do |rdoc| 12 | rdoc.rdoc_dir = 'rdoc' 13 | rdoc.title = 'Formtastic' 14 | rdoc.options << '--line-numbers' << '--inline-source' 15 | rdoc.rdoc_files.include('README.textile') 16 | rdoc.rdoc_files.include('lib/**/*.rb') 17 | end 18 | 19 | desc 'Test the formtastic plugin.' 20 | RSpec::Core::RakeTask.new('spec') do |t| 21 | t.pattern = FileList['spec/**/*_spec.rb'] 22 | end 23 | 24 | desc 'Test the formtastic plugin with specdoc formatting and colors' 25 | RSpec::Core::RakeTask.new('specdoc') do |t| 26 | t.pattern = FileList['spec/**/*_spec.rb'] 27 | end 28 | 29 | desc 'Run all examples with RCov' 30 | RSpec::Core::RakeTask.new('examples_with_rcov') do |t| 31 | t.pattern = FileList['spec/**/*_spec.rb'] 32 | t.rcov = true 33 | t.rcov_opts = ['--exclude', 'spec,Library'] 34 | end 35 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2010 Justin French 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /spec/inputs/url_input_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | 4 | describe 'url input' do 5 | 6 | include FormtasticSpecHelper 7 | 8 | before do 9 | @output_buffer = '' 10 | mock_everything 11 | end 12 | 13 | describe "when object is provided" do 14 | before do 15 | @form = semantic_form_for(@new_post) do |builder| 16 | concat(builder.input(:url)) 17 | end 18 | end 19 | 20 | it_should_have_input_wrapper_with_class(:url) 21 | it_should_have_input_wrapper_with_id("post_url_input") 22 | it_should_have_label_with_text(/Url/) 23 | it_should_have_label_for("post_url") 24 | it_should_have_input_with_id("post_url") 25 | it_should_have_input_with_type(Formtastic::Util.rails3? ? :url : :text) 26 | it_should_have_input_with_name("post[url]") 27 | 28 | end 29 | 30 | describe "when namespace is provided" do 31 | 32 | before do 33 | @form = semantic_form_for(@new_post, :namespace => "context2") do |builder| 34 | concat(builder.input(:url)) 35 | end 36 | end 37 | 38 | it_should_have_input_wrapper_with_id("context2_post_url_input") 39 | it_should_have_label_and_input_with_id("context2_post_url") 40 | 41 | end 42 | 43 | end 44 | 45 | -------------------------------------------------------------------------------- /spec/inputs/email_input_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | 4 | describe 'email input' do 5 | 6 | include FormtasticSpecHelper 7 | 8 | before do 9 | @output_buffer = '' 10 | mock_everything 11 | end 12 | 13 | describe "when object is provided" do 14 | before do 15 | @form = semantic_form_for(@new_post) do |builder| 16 | concat(builder.input(:email)) 17 | end 18 | end 19 | 20 | it_should_have_input_wrapper_with_class(:email) 21 | it_should_have_input_wrapper_with_id("post_email_input") 22 | it_should_have_label_with_text(/Email/) 23 | it_should_have_label_for("post_email") 24 | it_should_have_input_with_id("post_email") 25 | it_should_have_input_with_type(Formtastic::Util.rails3? ? :email : :text) 26 | it_should_have_input_with_name("post[email]") 27 | 28 | end 29 | 30 | describe "when namespace is provided" do 31 | 32 | before do 33 | @form = semantic_form_for(@new_post, :namespace => 'context2') do |builder| 34 | concat(builder.input(:email)) 35 | end 36 | end 37 | 38 | it_should_have_input_wrapper_with_id("context2_post_email_input") 39 | it_should_have_label_and_input_with_id("context2_post_email") 40 | 41 | end 42 | 43 | end 44 | 45 | -------------------------------------------------------------------------------- /spec/inputs/phone_input_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | 4 | describe 'phone input' do 5 | 6 | include FormtasticSpecHelper 7 | 8 | before do 9 | @output_buffer = '' 10 | mock_everything 11 | end 12 | 13 | describe "when object is provided" do 14 | before do 15 | @form = semantic_form_for(@new_post) do |builder| 16 | concat(builder.input(:phone)) 17 | end 18 | end 19 | 20 | it_should_have_input_wrapper_with_class(:phone) 21 | it_should_have_input_wrapper_with_id("post_phone_input") 22 | it_should_have_label_with_text(/Phone/) 23 | it_should_have_label_for("post_phone") 24 | it_should_have_input_with_id("post_phone") 25 | it_should_have_input_with_type(Formtastic::Util.rails3? ? :tel : :text) 26 | it_should_have_input_with_name("post[phone]") 27 | 28 | end 29 | 30 | describe "when namespace is provided" do 31 | 32 | before do 33 | @form = semantic_form_for(@new_post, :namespace => "context2") do |builder| 34 | concat(builder.input(:phone)) 35 | end 36 | end 37 | 38 | it_should_have_input_wrapper_with_id("context2_post_phone_input") 39 | it_should_have_label_and_input_with_id("context2_post_phone") 40 | 41 | end 42 | 43 | end 44 | 45 | -------------------------------------------------------------------------------- /spec/inputs/search_input_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | 4 | describe 'search input' do 5 | 6 | include FormtasticSpecHelper 7 | 8 | before do 9 | @output_buffer = '' 10 | mock_everything 11 | end 12 | 13 | describe "when object is provided" do 14 | before do 15 | @form = semantic_form_for(@new_post) do |builder| 16 | concat(builder.input(:search)) 17 | end 18 | end 19 | 20 | it_should_have_input_wrapper_with_class(:search) 21 | it_should_have_input_wrapper_with_id("post_search_input") 22 | it_should_have_label_with_text(/Search/) 23 | it_should_have_label_for("post_search") 24 | it_should_have_input_with_id("post_search") 25 | it_should_have_input_with_type(Formtastic::Util.rails3? ? :search : :text) 26 | it_should_have_input_with_name("post[search]") 27 | 28 | end 29 | 30 | describe "when namespace is provided" do 31 | 32 | before do 33 | @form = semantic_form_for(@new_post, :namespace => "context2") do |builder| 34 | concat(builder.input(:search)) 35 | end 36 | end 37 | 38 | it_should_have_input_wrapper_with_id("context2_post_search_input") 39 | it_should_have_label_and_input_with_id("context2_post_search") 40 | 41 | end 42 | 43 | end 44 | 45 | -------------------------------------------------------------------------------- /lib/formtastic/util.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # Adapted from the rails3 compatibility shim in Haml 2.2 4 | module Formtastic 5 | module Util 6 | extend self 7 | ## Rails XSS Safety 8 | 9 | # Returns the given text, marked as being HTML-safe. 10 | # With older versions of the Rails XSS-safety mechanism, 11 | # this destructively modifies the HTML-safety of `text`. 12 | # 13 | # @param text [String] 14 | # @return [String] `text`, marked as HTML-safe 15 | def html_safe(text) 16 | return text if text.nil? 17 | return text.html_safe if defined?(ActiveSupport::SafeBuffer) 18 | return text.html_safe! if text.respond_to?(:html_safe!) 19 | text 20 | end 21 | 22 | def rails_safe_buffer_class 23 | # It's important that we check ActiveSupport first, 24 | # because in Rails 2.3.6 ActionView::SafeBuffer exists 25 | # but is a deprecated proxy object. 26 | return ActiveSupport::SafeBuffer if defined?(ActiveSupport::SafeBuffer) 27 | return ActionView::SafeBuffer 28 | end 29 | 30 | def rails3? 31 | version= 32 | if defined?(ActionPack::VERSION::MAJOR) 33 | ActionPack::VERSION::MAJOR 34 | end 35 | !version.blank? && version >= 3 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/inputs/file_input_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | 4 | describe 'file input' do 5 | 6 | include FormtasticSpecHelper 7 | 8 | before do 9 | @output_buffer = '' 10 | mock_everything 11 | 12 | @form = semantic_form_for(@new_post) do |builder| 13 | concat(builder.input(:body, :as => :file)) 14 | end 15 | end 16 | 17 | it_should_have_input_wrapper_with_class("file") 18 | it_should_have_input_wrapper_with_id("post_body_input") 19 | it_should_have_label_with_text(/Body/) 20 | it_should_have_label_for("post_body") 21 | it_should_have_input_with_id("post_body") 22 | it_should_have_input_with_name("post[body]") 23 | it_should_apply_error_logic_for_input_type(:file) 24 | 25 | it 'should use input_html to style inputs' do 26 | form = semantic_form_for(@new_post) do |builder| 27 | concat(builder.input(:title, :as => :file, :input_html => { :class => 'myclass' })) 28 | end 29 | output_buffer.concat(form) if Formtastic::Util.rails3? 30 | output_buffer.should have_tag("form li input.myclass") 31 | end 32 | 33 | describe "when namespace is provided" do 34 | 35 | before do 36 | @output_buffer = '' 37 | mock_everything 38 | 39 | @form = semantic_form_for(@new_post, :namespace => 'context2') do |builder| 40 | concat(builder.input(:body, :as => :file)) 41 | end 42 | end 43 | 44 | it_should_have_input_wrapper_with_id("context2_post_body_input") 45 | it_should_have_label_and_input_with_id("context2_post_body") 46 | 47 | end 48 | 49 | end 50 | 51 | -------------------------------------------------------------------------------- /spec/inputs/numeric_input_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | 4 | describe 'numeric input' do 5 | 6 | include FormtasticSpecHelper 7 | 8 | before do 9 | @output_buffer = '' 10 | mock_everything 11 | 12 | @form = semantic_form_for(@new_post) do |builder| 13 | concat(builder.input(:title, :as => :numeric)) 14 | end 15 | end 16 | 17 | it_should_have_input_wrapper_with_class(:numeric) 18 | it_should_have_input_wrapper_with_id("post_title_input") 19 | it_should_have_label_with_text(/Title/) 20 | it_should_have_label_for("post_title") 21 | it_should_have_input_with_id("post_title") 22 | it_should_have_input_with_type(:text) 23 | it_should_have_input_with_name("post[title]") 24 | it_should_use_default_text_field_size_when_not_nil(:string) 25 | it_should_not_use_default_text_field_size_when_nil(:string) 26 | it_should_apply_custom_input_attributes_when_input_html_provided(:string) 27 | it_should_apply_custom_for_to_label_when_input_html_id_provided(:string) 28 | it_should_apply_error_logic_for_input_type(:numeric) 29 | 30 | describe "when no object is provided" do 31 | before do 32 | @form = semantic_form_for(:project, :url => 'http://test.host/') do |builder| 33 | concat(builder.input(:title, :as => :numeric)) 34 | end 35 | end 36 | 37 | it_should_have_label_with_text(/Title/) 38 | it_should_have_label_for("project_title") 39 | it_should_have_input_with_id("project_title") 40 | it_should_have_input_with_type(:text) 41 | it_should_have_input_with_name("project[title]") 42 | end 43 | 44 | describe "when namespace provided" do 45 | before do 46 | @form = semantic_form_for(@new_post, :namespace => :context2) do |builder| 47 | concat(builder.input(:title, :as => :numeric)) 48 | end 49 | end 50 | 51 | it_should_have_input_wrapper_with_id("context2_post_title_input") 52 | it_should_have_label_and_input_with_id("context2_post_title") 53 | end 54 | 55 | end 56 | 57 | -------------------------------------------------------------------------------- /spec/inputs/password_input_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | 4 | describe 'password input' do 5 | 6 | include FormtasticSpecHelper 7 | 8 | before do 9 | @output_buffer = '' 10 | mock_everything 11 | 12 | @form = semantic_form_for(@new_post) do |builder| 13 | concat(builder.input(:title, :as => :password)) 14 | end 15 | end 16 | 17 | it_should_have_input_wrapper_with_class(:password) 18 | it_should_have_input_wrapper_with_id("post_title_input") 19 | it_should_have_label_with_text(/Title/) 20 | it_should_have_label_for("post_title") 21 | it_should_have_input_with_id("post_title") 22 | it_should_have_input_with_type(:password) 23 | it_should_have_input_with_name("post[title]") 24 | it_should_have_maxlength_matching_column_limit 25 | it_should_use_default_text_field_size_when_not_nil(:string) 26 | it_should_not_use_default_text_field_size_when_nil(:string) 27 | it_should_apply_custom_input_attributes_when_input_html_provided(:string) 28 | it_should_apply_custom_for_to_label_when_input_html_id_provided(:string) 29 | it_should_apply_error_logic_for_input_type(:password) 30 | 31 | describe "when no object is provided" do 32 | before do 33 | @form = semantic_form_for(:project, :url => 'http://test.host/') do |builder| 34 | concat(builder.input(:title, :as => :password)) 35 | end 36 | end 37 | 38 | it_should_have_label_with_text(/Title/) 39 | it_should_have_label_for("project_title") 40 | it_should_have_input_with_id("project_title") 41 | it_should_have_input_with_type(:password) 42 | it_should_have_input_with_name("project[title]") 43 | end 44 | 45 | describe "when namespace is provided" do 46 | 47 | before do 48 | @form = semantic_form_for(@new_post, :namespace => "context2") do |builder| 49 | concat(builder.input(:title, :as => :password)) 50 | end 51 | end 52 | 53 | it_should_have_input_wrapper_with_id("context2_post_title_input") 54 | it_should_have_label_and_input_with_id("context2_post_title") 55 | 56 | end 57 | 58 | end 59 | -------------------------------------------------------------------------------- /formtastic.gemspec: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | Gem::Specification.new do |s| 4 | s.name = %q{formtastic} 5 | s.version = "1.2.1" 6 | s.date = %q{2010-11-22} 7 | 8 | s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= 9 | s.authors = ["Justin French"] 10 | s.description = %q{A Rails form builder plugin/gem with semantically rich and accessible markup} 11 | s.summary = %q{A Rails form builder plugin/gem with semantically rich and accessible markup} 12 | s.email = %q{justin@indent.com.au} 13 | s.extra_rdoc_files = ["README.textile"] 14 | s.files = Dir.glob("generators/**/*") + Dir.glob("lib/**/*") + Dir.glob("rails/*") + %w(MIT-LICENSE README.textile init.rb) 15 | s.homepage = %q{http://github.com/justinfrench/formtastic/tree/master} 16 | s.post_install_message = %q{ 17 | ======================================================================== 18 | Thanks for installing Formtastic! 19 | ------------------------------------------------------------------------ 20 | You can now (optionally) run the generator to copy some stylesheets and 21 | a config initializer into your application: 22 | rails generate formtastic:install # Rails 3 23 | ./script/generate formtastic # Rails 2 24 | 25 | To generate some semantic form markup for your existing models, just run: 26 | rails generate formtastic:form MODEL_NAME # Rails 3 27 | ./script/generate form MODEL_NAME # Rails 2 28 | 29 | Find out more and get involved: 30 | http://github.com/justinfrench/formtastic 31 | http://groups.google.com.au/group/formtastic 32 | ======================================================================== 33 | } 34 | s.rdoc_options = ["--charset=UTF-8"] 35 | s.require_paths = ["lib"] 36 | s.rubygems_version = %q{1.3.6} 37 | 38 | s.add_dependency(%q, [">= 2.3.7"]) 39 | s.add_dependency(%q, [">= 2.3.7"]) 40 | s.add_dependency(%q, [">= 0.4.0"]) 41 | 42 | if ENV['RAILS_2'] 43 | s.add_development_dependency(%q, ["~> 2.3.8"]) 44 | else 45 | s.add_development_dependency(%q, [">= 3.0.0"]) 46 | end 47 | s.add_development_dependency(%q, ["~> 2.0.0"]) 48 | s.add_development_dependency(%q, [">= 1.0.0"]) 49 | s.add_development_dependency(%q, ["~> 0.8.3"]) 50 | 51 | end 52 | -------------------------------------------------------------------------------- /spec/include_blank_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | 4 | describe "*select: options[:include_blank]" do 5 | 6 | include FormtasticSpecHelper 7 | 8 | before do 9 | @output_buffer = '' 10 | mock_everything 11 | 12 | @new_post.stub!(:author_id).and_return(nil) 13 | @new_post.stub!(:publish_at).and_return(nil) 14 | 15 | @select_input_types = { 16 | :select => :author, 17 | :datetime => :publish_at, 18 | :date => :publish_at, 19 | :time => :publish_at 20 | } 21 | end 22 | 23 | describe 'when :include_blank is not set' do 24 | it 'blank value should be included if the default value specified in config is true' do 25 | ::Formtastic::SemanticFormBuilder.include_blank_for_select_by_default = true 26 | @select_input_types.each do |as, attribute| 27 | form = semantic_form_for(@new_post) do |builder| 28 | concat(builder.input(attribute, :as => as)) 29 | end 30 | output_buffer.concat(form) if Formtastic::Util.rails3? 31 | output_buffer.should have_tag("form li select option[@value='']", "") 32 | end 33 | end 34 | 35 | it 'blank value should not be included if the default value specified in config is false' do 36 | ::Formtastic::SemanticFormBuilder.include_blank_for_select_by_default = false 37 | @select_input_types.each do |as, attribute| 38 | form = semantic_form_for(@new_post) do |builder| 39 | concat(builder.input(attribute, :as => as)) 40 | end 41 | output_buffer.concat(form) if Formtastic::Util.rails3? 42 | output_buffer.should_not have_tag("form li select option[@value='']", "") 43 | end 44 | end 45 | 46 | after do 47 | ::Formtastic::SemanticFormBuilder.include_blank_for_select_by_default = true 48 | end 49 | end 50 | 51 | describe 'when :include_blank is set to false' do 52 | it 'should not have a blank option' do 53 | @select_input_types.each do |as, attribute| 54 | form = semantic_form_for(@new_post) do |builder| 55 | concat(builder.input(attribute, :as => as, :include_blank => false)) 56 | end 57 | output_buffer.concat(form) if Formtastic::Util.rails3? 58 | output_buffer.should_not have_tag("form li select option[@value='']", "") 59 | end 60 | end 61 | end 62 | 63 | describe 'when :include_blank => true is set' do 64 | it 'should have a blank select option' do 65 | @select_input_types.each do |as, attribute| 66 | form = semantic_form_for(@new_post) do |builder| 67 | concat(builder.input(attribute, :as => as, :include_blank => true)) 68 | end 69 | output_buffer.concat(form) if Formtastic::Util.rails3? 70 | output_buffer.should have_tag("form li select option[@value='']", "") 71 | end 72 | end 73 | end 74 | end 75 | -------------------------------------------------------------------------------- /spec/inputs/time_zone_input_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | 4 | describe 'time_zone input' do 5 | 6 | include FormtasticSpecHelper 7 | 8 | before do 9 | @output_buffer = '' 10 | mock_everything 11 | 12 | @form = semantic_form_for(@new_post) do |builder| 13 | concat(builder.input(:time_zone)) 14 | end 15 | end 16 | 17 | it_should_have_input_wrapper_with_class("time_zone") 18 | it_should_have_input_wrapper_with_id("post_time_zone_input") 19 | it_should_apply_error_logic_for_input_type(:time_zone) 20 | 21 | it 'should generate a label for the input' do 22 | output_buffer.concat(@form) if Formtastic::Util.rails3? 23 | output_buffer.should have_tag('form li label') 24 | output_buffer.should have_tag('form li label[@for="post_time_zone"]') 25 | output_buffer.should have_tag('form li label', /Time zone/) 26 | end 27 | 28 | it "should generate a select" do 29 | output_buffer.concat(@form) if Formtastic::Util.rails3? 30 | output_buffer.should have_tag("form li select") 31 | output_buffer.should have_tag("form li select#post_time_zone") 32 | output_buffer.should have_tag("form li select[@name=\"post[time_zone]\"]") 33 | end 34 | 35 | it 'should use input_html to style inputs' do 36 | form = semantic_form_for(@new_post) do |builder| 37 | concat(builder.input(:time_zone, :input_html => { :class => 'myclass' })) 38 | end 39 | output_buffer.concat(form) if Formtastic::Util.rails3? 40 | output_buffer.should have_tag("form li select.myclass") 41 | end 42 | 43 | describe "when namespace is provided" do 44 | 45 | before do 46 | @output_buffer = '' 47 | mock_everything 48 | 49 | @form = semantic_form_for(@new_post, :namespace => 'context2') do |builder| 50 | concat(builder.input(:time_zone)) 51 | end 52 | end 53 | 54 | it_should_have_input_wrapper_with_id("context2_post_time_zone_input") 55 | it_should_have_select_with_id("context2_post_time_zone") 56 | it_should_have_label_for("context2_post_time_zone") 57 | 58 | end 59 | 60 | describe 'when no object is given' do 61 | before(:each) do 62 | @form = semantic_form_for(:project, :url => 'http://test.host/') do |builder| 63 | concat(builder.input(:time_zone, :as => :time_zone)) 64 | end 65 | end 66 | 67 | it 'should generate labels' do 68 | output_buffer.concat(@form) if Formtastic::Util.rails3? 69 | output_buffer.should have_tag('form li label') 70 | output_buffer.should have_tag('form li label[@for="project_time_zone"]') 71 | output_buffer.should have_tag('form li label', /Time zone/) 72 | end 73 | 74 | it 'should generate select inputs' do 75 | output_buffer.concat(@form) if Formtastic::Util.rails3? 76 | output_buffer.should have_tag("form li select") 77 | output_buffer.should have_tag("form li select#project_time_zone") 78 | output_buffer.should have_tag("form li select[@name=\"project[time_zone]\"]") 79 | end 80 | end 81 | 82 | end 83 | -------------------------------------------------------------------------------- /spec/custom_builder_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | 4 | describe 'Formtastic::SemanticFormHelper.builder' do 5 | 6 | include FormtasticSpecHelper 7 | 8 | class MyCustomFormBuilder < ::Formtastic::SemanticFormBuilder 9 | def awesome_input(method, options) 10 | self.text_field(method) 11 | end 12 | end 13 | 14 | before do 15 | @output_buffer = '' 16 | mock_everything 17 | end 18 | 19 | it 'is the Formtastic::SemanticFormBuilder by default' do 20 | ::Formtastic::SemanticFormHelper.builder.should == ::Formtastic::SemanticFormBuilder 21 | end 22 | 23 | it 'can be configured to use your own custom form builder' do 24 | # Set it to a custom builder class 25 | ::Formtastic::SemanticFormHelper.builder = MyCustomFormBuilder 26 | ::Formtastic::SemanticFormHelper.builder.should == MyCustomFormBuilder 27 | 28 | # Reset it to the default 29 | ::Formtastic::SemanticFormHelper.builder = ::Formtastic::SemanticFormBuilder 30 | ::Formtastic::SemanticFormHelper.builder.should == ::Formtastic::SemanticFormBuilder 31 | end 32 | 33 | it 'should allow custom settings per form builder subclass' do 34 | with_config(:all_fields_required_by_default, true) do 35 | MyCustomFormBuilder.all_fields_required_by_default = false 36 | 37 | MyCustomFormBuilder.all_fields_required_by_default.should be_false 38 | ::Formtastic::SemanticFormBuilder.all_fields_required_by_default.should be_true 39 | end 40 | end 41 | 42 | describe "when using a custom builder" do 43 | 44 | before do 45 | @new_post.stub!(:title) 46 | ::Formtastic::SemanticFormHelper.builder = MyCustomFormBuilder 47 | end 48 | 49 | after do 50 | ::Formtastic::SemanticFormHelper.builder = ::Formtastic::SemanticFormBuilder 51 | end 52 | 53 | describe "semantic_form_for" do 54 | 55 | it "should yield an instance of the custom builder" do 56 | semantic_form_for(@new_post) do |builder| 57 | builder.class.should == MyCustomFormBuilder 58 | end 59 | end 60 | 61 | it "should allow me to call my custom input" do 62 | semantic_form_for(@new_post) do |builder| 63 | concat(builder.input(:title, :as => :awesome)) 64 | end 65 | end 66 | 67 | end 68 | 69 | describe "semantic_fields_for" do 70 | 71 | it "should yield an instance of the parent form builder" do 72 | semantic_form_for(@new_post) do |builder| 73 | builder.semantic_fields_for(:author) do |nested_builder| 74 | nested_builder.class.should == MyCustomFormBuilder 75 | end 76 | end 77 | end 78 | 79 | end 80 | 81 | end 82 | 83 | describe "when using a builder passed to form options" do 84 | 85 | describe "semantic_fields_for" do 86 | 87 | it "should yield an instance of the parent form builder" do 88 | semantic_form_for(@new_post, :builder => MyCustomFormBuilder) do |builder| 89 | builder.semantic_fields_for(:author) do |nested_builder| 90 | nested_builder.class.should == MyCustomFormBuilder 91 | end 92 | end 93 | end 94 | 95 | end 96 | 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /spec/semantic_fields_for_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | 4 | describe 'SemanticFormBuilder#semantic_fields_for' do 5 | 6 | include FormtasticSpecHelper 7 | 8 | before do 9 | @output_buffer = '' 10 | mock_everything 11 | @new_post.stub!(:author).and_return(::Author.new) 12 | end 13 | 14 | it 'yields an instance of SemanticFormHelper.builder' do 15 | semantic_form_for(@new_post) do |builder| 16 | builder.semantic_fields_for(:author) do |nested_builder| 17 | nested_builder.class.should == ::Formtastic::SemanticFormHelper.builder 18 | end 19 | end 20 | end 21 | 22 | it 'nests the object name' do 23 | semantic_form_for(@new_post) do |builder| 24 | builder.semantic_fields_for(@bob) do |nested_builder| 25 | nested_builder.object_name.should == 'post[author]' 26 | end 27 | end 28 | end 29 | 30 | it 'should sanitize html id for li tag' do 31 | @bob.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255)) 32 | form = semantic_form_for(@new_post) do |builder| 33 | concat(builder.semantic_fields_for(@bob, :index => 1) do |nested_builder| 34 | concat(nested_builder.inputs(:login)) 35 | end) 36 | end 37 | output_buffer.concat(form) if Formtastic::Util.rails3? 38 | output_buffer.should have_tag('form fieldset.inputs #post_author_1_login_input') 39 | # Not valid selector, so using good ol' regex 40 | output_buffer.should_not =~ /id="post\[author\]_1_login_input"/ 41 | # <=> output_buffer.should_not have_tag('form fieldset.inputs #post[author]_1_login_input') 42 | end 43 | 44 | it 'should use namespace provided in nested fields' do 45 | @bob.stub!(:column_for_attribute).and_return(mock('column', :type => :string, :limit => 255)) 46 | form = semantic_form_for(@new_post, :namespace => 'context2') do |builder| 47 | concat(builder.semantic_fields_for(@bob, :index => 1) do |nested_builder| 48 | concat(nested_builder.inputs(:login)) 49 | end) 50 | end 51 | output_buffer.concat(form) if Formtastic::Util.rails3? 52 | output_buffer.should have_tag('form fieldset.inputs #context2_post_author_1_login_input') 53 | end 54 | 55 | context "when I rendered my own hidden id input" do 56 | 57 | before do 58 | output_buffer.replace '' 59 | 60 | @fred.posts.size.should == 1 61 | @fred.posts.first.stub!(:persisted?).and_return(true) 62 | @fred.stub!(:posts_attributes=) 63 | 64 | form = semantic_form_for(@fred) do |builder| 65 | concat(builder.semantic_fields_for(:posts) do |nested_builder| 66 | concat(nested_builder.input(:id, :as => :hidden)) 67 | concat(nested_builder.input(:title)) 68 | end) 69 | end 70 | output_buffer.concat(form) if Formtastic::Util.rails3? 71 | end 72 | 73 | it "should only render one hidden input (my one)" do 74 | output_buffer.should have_tag 'input#author_posts_attributes_0_id', :count => 1 75 | end 76 | 77 | it "should render the hidden input inside an li.hidden" do 78 | output_buffer.should have_tag 'li.hidden input#author_posts_attributes_0_id' 79 | end 80 | end 81 | 82 | end 83 | 84 | -------------------------------------------------------------------------------- /lib/generators/formtastic/form/form_generator.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | module Formtastic 3 | class FormGenerator < Rails::Generators::NamedBase 4 | desc "Generates formtastic form code based on an existing model. By default the " << 5 | "generated code will be printed out directly in the terminal, and also copied " << 6 | "to clipboard. Can optionally be saved into partial directly." 7 | 8 | argument :name, :type => :string, :required => true, :banner => 'ExistingModelName' 9 | argument :attributes, :type => :array, :default => [], :banner => 'field:type field:type' 10 | 11 | class_option :haml, :type => :boolean, :default => false, :group => :formtastic, 12 | :desc => "Generate HAML instead of ERB" 13 | 14 | class_option :partial, :type => :boolean, :default => false, :group => :formtastic, 15 | :desc => 'Generate a form partial in the model views path, i.e. "_form.html.erb" or "_form.html.haml"' 16 | 17 | class_option :controller, :type => :string, :default => false, :group => :formtastic, 18 | :desc => 'Generate for custom controller/view path - in case model and controller namespace is different, i.e. "admin/posts"' 19 | 20 | source_root File.expand_path('../../../templates', __FILE__) 21 | 22 | def create_or_show 23 | @attributes = self.columns if @attributes.empty? 24 | 25 | if options[:partial] 26 | empty_directory "app/views/#{controller_path}" 27 | template "_form.html.#{template_type}", "app/views/#{controller_path}/_form.html.#{template_type}" 28 | else 29 | template = File.read("#{self.class.source_root}/_form.html.#{template_type}") 30 | erb = ERB.new(template, nil, '-') 31 | generated_code = erb.result(binding).strip rescue nil 32 | 33 | puts "# ---------------------------------------------------------" 34 | puts "# GENERATED FORMTASTIC CODE" 35 | puts "# ---------------------------------------------------------" 36 | puts 37 | puts generated_code || "Nothing could be generated - model exists?" 38 | puts 39 | puts "# ---------------------------------------------------------" 40 | puts "Copied to clipboard - just paste it!" if save_to_clipboard(generated_code) 41 | end 42 | end 43 | 44 | protected 45 | 46 | IGNORED_COLUMNS = [:updated_at, :created_at].freeze 47 | 48 | def template_type 49 | @template_type ||= options[:haml] ? :haml : :erb 50 | end 51 | 52 | def controller_path 53 | @controller_path ||= if options[:controller] 54 | options[:controller].underscore 55 | else 56 | name.underscore.pluralize 57 | end 58 | end 59 | 60 | def columns 61 | @columns ||= self.name.camelize.constantize.content_columns.reject { |column| IGNORED_COLUMNS.include?(column.name.to_sym) } 62 | end 63 | 64 | def save_to_clipboard(data) 65 | return unless data 66 | 67 | begin 68 | case RUBY_PLATFORM 69 | when /win32/ 70 | require 'win32/clipboard' 71 | ::Win32::Clipboard.data = data 72 | when /darwin/ # mac 73 | `echo "#{data}" | pbcopy` 74 | else # linux/unix 75 | `echo "#{data}" | xsel --clipboard` || `echo "#{data}" | xclip` 76 | end 77 | rescue LoadError 78 | false 79 | else 80 | true 81 | end 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /spec/semantic_errors_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | 4 | describe 'SemanticFormBuilder#semantic_errors' do 5 | 6 | include FormtasticSpecHelper 7 | 8 | before do 9 | @output_buffer = '' 10 | mock_everything 11 | @title_errors = ['must not be blank', 'must be awesome'] 12 | @base_errors = ['base error message', 'nasty error'] 13 | @base_error = 'one base error' 14 | @errors = mock('errors') 15 | @new_post.stub!(:errors).and_return(@errors) 16 | end 17 | 18 | describe 'when there is only one error on base' do 19 | before do 20 | @errors.stub!(:[]).with(:base).and_return(@base_error) 21 | end 22 | 23 | it 'should render an unordered list' do 24 | semantic_form_for(@new_post) do |builder| 25 | builder.semantic_errors.should have_tag('ul.errors li', @base_error) 26 | end 27 | end 28 | end 29 | 30 | describe 'when there is more than one error on base' do 31 | before do 32 | @errors.stub!(:[]).with(:base).and_return(@base_errors) 33 | end 34 | 35 | it 'should render an unordered list' do 36 | semantic_form_for(@new_post) do |builder| 37 | builder.semantic_errors.should have_tag('ul.errors') 38 | @base_errors.each do |error| 39 | builder.semantic_errors.should have_tag('ul.errors li', error) 40 | end 41 | end 42 | end 43 | end 44 | 45 | describe 'when there are errors on title' do 46 | before do 47 | @errors.stub!(:[]).with(:title).and_return(@title_errors) 48 | @errors.stub!(:[]).with(:base).and_return([]) 49 | end 50 | 51 | it 'should render an unordered list' do 52 | semantic_form_for(@new_post) do |builder| 53 | title_name = builder.send(:localized_string, :title, :title, :label) || builder.send(:humanized_attribute_name, :title) 54 | builder.semantic_errors(:title).should have_tag('ul.errors li', title_name << " " << @title_errors.to_sentence) 55 | end 56 | end 57 | end 58 | 59 | describe 'when there are errors on title and base' do 60 | before do 61 | @errors.stub!(:[]).with(:title).and_return(@title_errors) 62 | @errors.stub!(:[]).with(:base).and_return(@base_error) 63 | end 64 | 65 | it 'should render an unordered list' do 66 | semantic_form_for(@new_post) do |builder| 67 | title_name = builder.send(:localized_string, :title, :title, :label) || builder.send(:humanized_attribute_name, :title) 68 | builder.semantic_errors(:title).should have_tag('ul.errors li', title_name << " " << @title_errors.to_sentence) 69 | builder.semantic_errors(:title).should have_tag('ul.errors li', @base_error) 70 | end 71 | end 72 | end 73 | 74 | describe 'when there are no errors' do 75 | before do 76 | @errors.stub!(:[]).with(:title).and_return(nil) 77 | @errors.stub!(:[]).with(:base).and_return(nil) 78 | end 79 | 80 | it 'should return nil' do 81 | semantic_form_for(@new_post) do |builder| 82 | builder.semantic_errors(:title).should be_nil 83 | end 84 | end 85 | end 86 | 87 | describe 'when there is one error on base and options with class is passed' do 88 | before do 89 | @errors.stub!(:[]).with(:base).and_return(@base_error) 90 | end 91 | 92 | it 'should render an unordered list with given class' do 93 | semantic_form_for(@new_post) do |builder| 94 | builder.semantic_errors(:class => "awesome").should have_tag('ul.awesome li', @base_error) 95 | end 96 | end 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /spec/label_spec.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | require 'spec_helper' 3 | 4 | describe 'SemanticFormBuilder#label' do 5 | 6 | include FormtasticSpecHelper 7 | 8 | before do 9 | @output_buffer = '' 10 | mock_everything 11 | end 12 | 13 | it 'should humanize the given attribute' do 14 | semantic_form_for(@new_post) do |builder| 15 | builder.label(:login).should have_tag('label', :with => /Login/) 16 | end 17 | end 18 | 19 | describe 'when required is given' do 20 | it 'should append a required note' do 21 | semantic_form_for(@new_post) do |builder| 22 | builder.label(:login, nil, :required => true).should have_tag('label abbr') 23 | end 24 | end 25 | 26 | it 'should allow require option to be given as second argument' do 27 | semantic_form_for(@new_post) do |builder| 28 | builder.label(:login, :required => true).should have_tag('label abbr') 29 | end 30 | end 31 | end 32 | 33 | describe 'when a collection is given' do 34 | it 'should use a supplied label_method for simple collections' do 35 | form = semantic_form_for(:project, :url => 'http://test.host') do |builder| 36 | concat(builder.input(:author_id, :as => :check_boxes, :collection => [:a, :b, :c], :value_method => :to_s, :label_method => proc {|f| ('Label_%s' % [f])})) 37 | end 38 | output_buffer.concat(form) if Formtastic::Util.rails3? 39 | output_buffer.should have_tag('form li fieldset ol li label', :with => /Label_[abc]/, :count => 3) 40 | end 41 | 42 | it 'should use a supplied value_method for simple collections' do 43 | form = semantic_form_for(:project, :url => 'http://test.host') do |builder| 44 | concat(builder.input(:author_id, :as => :check_boxes, :collection => [:a, :b, :c], :value_method => proc {|f| ('Value_%s' % [f.to_s])})) 45 | end 46 | output_buffer.concat(form) if Formtastic::Util.rails3? 47 | output_buffer.should have_tag('form li fieldset ol li label input[value="Value_a"]') 48 | output_buffer.should have_tag('form li fieldset ol li label input[value="Value_b"]') 49 | output_buffer.should have_tag('form li fieldset ol li label input[value="Value_c"]') 50 | end 51 | end 52 | 53 | describe 'when label is given' do 54 | it 'should allow the text to be given as label option' do 55 | semantic_form_for(@new_post) do |builder| 56 | builder.label(:login, :required => true, :label => 'My label').should have_tag('label', :with => /My label/) 57 | end 58 | end 59 | 60 | it 'should return nil if label is false' do 61 | semantic_form_for(@new_post) do |builder| 62 | builder.label(:login, :label => false).should be_blank 63 | end 64 | end 65 | 66 | it 'should html escape the label string by default' do 67 | semantic_form_for(@new_post) do |builder| 68 | builder.label(:login, :required => false, :label => 'My label').should == "" 69 | end 70 | end 71 | 72 | it 'should not html escape the label if configured that way' do 73 | ::Formtastic::SemanticFormBuilder.escape_html_entities_in_hints_and_labels = false 74 | semantic_form_for(@new_post) do |builder| 75 | builder.label(:login, :required => false, :label => 'My label').should == "" 76 | end 77 | end 78 | 79 | it 'should not html escape the label string for html_safe strings' do 80 | ::Formtastic::SemanticFormBuilder.escape_html_entities_in_hints_and_labels = true 81 | semantic_form_for(@new_post) do |builder| 82 | builder.label(:login, :required => false, :label => 'My label'.html_safe).should == "" 83 | end 84 | end 85 | 86 | end 87 | 88 | end 89 | 90 | -------------------------------------------------------------------------------- /lib/generators/templates/formtastic.rb: -------------------------------------------------------------------------------- 1 | # encoding: utf-8 2 | 3 | # Set the default text field size when input is a string. Default is nil. 4 | # Formtastic::SemanticFormBuilder.default_text_field_size = 50 5 | 6 | # Set the default text area height when input is a text. Default is 20. 7 | # Formtastic::SemanticFormBuilder.default_text_area_height = 5 8 | 9 | # Set the default text area width when input is a text. Default is nil. 10 | # Formtastic::SemanticFormBuilder.default_text_area_width = 50 11 | 12 | # Should all fields be considered "required" by default? 13 | # Rails 2 only, ignored by Rails 3 because it will never fall back to this default. 14 | # Defaults to true. 15 | # Formtastic::SemanticFormBuilder.all_fields_required_by_default = true 16 | 17 | # Should select fields have a blank option/prompt by default? 18 | # Defaults to true. 19 | # Formtastic::SemanticFormBuilder.include_blank_for_select_by_default = true 20 | 21 | # Set the string that will be appended to the labels/fieldsets which are required 22 | # It accepts string or procs and the default is a localized version of 23 | # '*'. In other words, if you configure formtastic.required 24 | # in your locale, it will replace the abbr title properly. But if you don't want to use 25 | # abbr tag, you can simply give a string as below 26 | # Formtastic::SemanticFormBuilder.required_string = "(required)" 27 | 28 | # Set the string that will be appended to the labels/fieldsets which are optional 29 | # Defaults to an empty string ("") and also accepts procs (see required_string above) 30 | # Formtastic::SemanticFormBuilder.optional_string = "(optional)" 31 | 32 | # Set the way inline errors will be displayed. 33 | # Defaults to :sentence, valid options are :sentence, :list, :first and :none 34 | # Formtastic::SemanticFormBuilder.inline_errors = :sentence 35 | # Formtastic uses the following classes as default for hints, inline_errors and error list 36 | 37 | # If you override the class here, please ensure to override it in your formtastic_changes.css stylesheet as well 38 | # Formtastic::SemanticFormBuilder.default_hint_class = "inline-hints" 39 | # Formtastic::SemanticFormBuilder.default_inline_error_class = "inline-errors" 40 | # Formtastic::SemanticFormBuilder.default_error_list_class = "errors" 41 | 42 | # Set the method to call on label text to transform or format it for human-friendly 43 | # reading when formtastic is used without object. Defaults to :humanize. 44 | # Formtastic::SemanticFormBuilder.label_str_method = :humanize 45 | 46 | # Set the array of methods to try calling on parent objects in :select and :radio inputs 47 | # for the text inside each @