Skip to content
Browse files

Merge branch 'release/0.4.0'

  • Loading branch information...
2 parents 7cecba6 + 14dc1b9 commit 7892956e77eda27dcd34e4671bab11f0172468da @rick committed Jul 23, 2010
Showing with 8,191 additions and 11,614 deletions.
  1. +1 −0 VERSION
  2. +1 −1 config/boot.rb
  3. +2 −3 config/environment.rb
  4. +1 −1 spec/models/host_spec.rb
  5. +211 −0 vendor/gems/formtastic-0.9.10/.specification
  6. 0 vendor/{plugins/formtastic → gems/formtastic-0.9.10}/MIT-LICENSE
  7. +574 −0 vendor/gems/formtastic-0.9.10/README.textile
  8. +101 −0 vendor/gems/formtastic-0.9.10/Rakefile
  9. +16 −0 vendor/gems/formtastic-0.9.10/generators/form/USAGE
  10. +120 −0 vendor/gems/formtastic-0.9.10/generators/form/form_generator.rb
  11. +5 −0 vendor/gems/formtastic-0.9.10/generators/form/templates/view__form.html.erb
  12. +4 −0 vendor/gems/formtastic-0.9.10/generators/form/templates/view__form.html.haml
  13. 0 vendor/{plugins/formtastic → gems/formtastic-0.9.10}/generators/formtastic/formtastic_generator.rb
  14. +146 −0 vendor/gems/formtastic-0.9.10/generators/formtastic/templates/formtastic.css
  15. +54 −0 vendor/gems/formtastic-0.9.10/generators/formtastic/templates/formtastic.rb
  16. +14 −0 vendor/gems/formtastic-0.9.10/generators/formtastic/templates/formtastic_changes.css
  17. 0 ...c → gems/formtastic-0.9.10}/generators/formtastic_stylesheets/formtastic_stylesheets_generator.rb
  18. +1,749 −0 vendor/gems/formtastic-0.9.10/lib/formtastic.rb
  19. +36 −0 vendor/gems/formtastic-0.9.10/lib/formtastic/i18n.rb
  20. +11 −0 vendor/gems/formtastic-0.9.10/lib/formtastic/layout_helper.rb
  21. +25 −0 vendor/gems/formtastic-0.9.10/lib/formtastic/util.rb
  22. +8 −0 vendor/gems/formtastic-0.9.10/lib/locale/en.yml
  23. +5 −0 vendor/gems/formtastic-0.9.10/rails/init.rb
  24. +149 −0 vendor/gems/formtastic-0.9.10/spec/buttons_spec.rb
  25. +363 −0 vendor/gems/formtastic-0.9.10/spec/commit_button_spec.rb
  26. +62 −0 vendor/gems/formtastic-0.9.10/spec/custom_builder_spec.rb
  27. +460 −0 vendor/gems/formtastic-0.9.10/spec/custom_macros.rb
  28. +20 −0 vendor/gems/formtastic-0.9.10/spec/defaults_spec.rb
  29. +27 −0 vendor/gems/formtastic-0.9.10/spec/error_proc_spec.rb
  30. +104 −0 vendor/gems/formtastic-0.9.10/spec/errors_spec.rb
  31. +126 −0 vendor/gems/formtastic-0.9.10/spec/form_helper_spec.rb
  32. +148 −0 vendor/gems/formtastic-0.9.10/spec/i18n_spec.rb
  33. +70 −0 vendor/gems/formtastic-0.9.10/spec/include_blank_spec.rb
  34. +629 −0 vendor/gems/formtastic-0.9.10/spec/input_spec.rb
  35. +97 −0 vendor/gems/formtastic-0.9.10/spec/inputs/boolean_input_spec.rb
  36. +240 −0 vendor/gems/formtastic-0.9.10/spec/inputs/check_boxes_input_spec.rb
  37. +114 −0 vendor/gems/formtastic-0.9.10/spec/inputs/country_input_spec.rb
  38. +156 −0 vendor/gems/formtastic-0.9.10/spec/inputs/date_input_spec.rb
  39. +291 −0 vendor/gems/formtastic-0.9.10/spec/inputs/datetime_input_spec.rb
  40. +33 −0 vendor/gems/formtastic-0.9.10/spec/inputs/file_input_spec.rb
  41. +61 −0 vendor/gems/formtastic-0.9.10/spec/inputs/hidden_input_spec.rb
  42. +44 −0 vendor/gems/formtastic-0.9.10/spec/inputs/numeric_input_spec.rb
  43. +46 −0 vendor/gems/formtastic-0.9.10/spec/inputs/password_input_spec.rb
  44. +155 −0 vendor/gems/formtastic-0.9.10/spec/inputs/radio_input_spec.rb
  45. +506 −0 vendor/gems/formtastic-0.9.10/spec/inputs/select_input_spec.rb
  46. +47 −0 vendor/gems/formtastic-0.9.10/spec/inputs/string_input_spec.rb
  47. +33 −0 vendor/gems/formtastic-0.9.10/spec/inputs/text_input_spec.rb
  48. +170 −0 vendor/gems/formtastic-0.9.10/spec/inputs/time_input_spec.rb
  49. +104 −0 vendor/gems/formtastic-0.9.10/spec/inputs/time_zone_input_spec.rb
  50. +394 −0 vendor/gems/formtastic-0.9.10/spec/inputs_spec.rb
  51. +48 −0 vendor/gems/formtastic-0.9.10/spec/label_spec.rb
  52. +29 −0 vendor/gems/formtastic-0.9.10/spec/layout_helper_spec.rb
  53. +98 −0 vendor/gems/formtastic-0.9.10/spec/semantic_errors_spec.rb
  54. +44 −0 vendor/gems/formtastic-0.9.10/spec/semantic_fields_for_spec.rb
  55. +2 −0 vendor/gems/formtastic-0.9.10/spec/spec.opts
  56. +237 −0 vendor/gems/formtastic-0.9.10/spec/spec_helper.rb
  57. +0 −241 vendor/gems/haml-2.0.9/.specification
  58. +0 −138 vendor/gems/haml-2.0.9/FAQ
  59. +0 −20 vendor/gems/haml-2.0.9/MIT-LICENSE
  60. +0 −319 vendor/gems/haml-2.0.9/README.rdoc
  61. +0 −184 vendor/gems/haml-2.0.9/Rakefile
  62. +0 −1 vendor/gems/haml-2.0.9/VERSION
  63. +0 −7 vendor/gems/haml-2.0.9/bin/css2sass
  64. +0 −331 vendor/gems/haml-2.0.9/extra/haml-mode.el
  65. +0 −89 vendor/gems/haml-2.0.9/extra/sass-mode.el
  66. +0 −8 vendor/gems/haml-2.0.9/init.rb
  67. +0 −1,018 vendor/gems/haml-2.0.9/lib/haml.rb
  68. +0 −240 vendor/gems/haml-2.0.9/lib/haml/buffer.rb
  69. +0 −265 vendor/gems/haml-2.0.9/lib/haml/engine.rb
  70. +0 −22 vendor/gems/haml-2.0.9/lib/haml/error.rb
  71. +0 −374 vendor/gems/haml-2.0.9/lib/haml/exec.rb
  72. +0 −276 vendor/gems/haml-2.0.9/lib/haml/filters.rb
  73. +0 −465 vendor/gems/haml-2.0.9/lib/haml/helpers.rb
  74. +0 −45 vendor/gems/haml-2.0.9/lib/haml/helpers/action_view_extensions.rb
  75. +0 −181 vendor/gems/haml-2.0.9/lib/haml/helpers/action_view_mods.rb
  76. +0 −222 vendor/gems/haml-2.0.9/lib/haml/html.rb
  77. +0 −811 vendor/gems/haml-2.0.9/lib/haml/precompiler.rb
  78. +0 −51 vendor/gems/haml-2.0.9/lib/haml/template.rb
  79. +0 −58 vendor/gems/haml-2.0.9/lib/haml/template/patch.rb
  80. +0 −72 vendor/gems/haml-2.0.9/lib/haml/template/plugin.rb
  81. +0 −23 vendor/gems/haml-2.0.9/lib/haml/util.rb
  82. +0 −47 vendor/gems/haml-2.0.9/lib/haml/version.rb
  83. +0 −873 vendor/gems/haml-2.0.9/lib/sass.rb
  84. +0 −216 vendor/gems/haml-2.0.9/lib/sass/constant.rb
  85. +0 −101 vendor/gems/haml-2.0.9/lib/sass/constant/color.rb
  86. +0 −54 vendor/gems/haml-2.0.9/lib/sass/constant/literal.rb
  87. +0 −9 vendor/gems/haml-2.0.9/lib/sass/constant/nil.rb
  88. +0 −87 vendor/gems/haml-2.0.9/lib/sass/constant/number.rb
  89. +0 −30 vendor/gems/haml-2.0.9/lib/sass/constant/operation.rb
  90. +0 −22 vendor/gems/haml-2.0.9/lib/sass/constant/string.rb
  91. +0 −394 vendor/gems/haml-2.0.9/lib/sass/css.rb
  92. +0 −466 vendor/gems/haml-2.0.9/lib/sass/engine.rb
  93. +0 −35 vendor/gems/haml-2.0.9/lib/sass/error.rb
  94. +0 −169 vendor/gems/haml-2.0.9/lib/sass/plugin.rb
  95. +0 −56 vendor/gems/haml-2.0.9/lib/sass/plugin/merb.rb
  96. +0 −24 vendor/gems/haml-2.0.9/lib/sass/plugin/rails.rb
  97. +0 −57 vendor/gems/haml-2.0.9/lib/sass/tree/attr_node.rb
  98. +0 −20 vendor/gems/haml-2.0.9/lib/sass/tree/comment_node.rb
  99. +0 −46 vendor/gems/haml-2.0.9/lib/sass/tree/directive_node.rb
  100. +0 −46 vendor/gems/haml-2.0.9/lib/sass/tree/node.rb
  101. +0 −93 vendor/gems/haml-2.0.9/lib/sass/tree/rule_node.rb
  102. +0 −20 vendor/gems/haml-2.0.9/lib/sass/tree/value_node.rb
  103. +0 −99 vendor/gems/haml-2.0.9/test/benchmark.rb
  104. +0 −657 vendor/gems/haml-2.0.9/test/haml/engine_test.rb
  105. +0 −224 vendor/gems/haml-2.0.9/test/haml/helper_test.rb
  106. +0 −92 vendor/gems/haml-2.0.9/test/haml/html2haml_test.rb
  107. +0 −15 vendor/gems/haml-2.0.9/test/haml/results/content_for_layout.xhtml
  108. +0 −60 vendor/gems/haml-2.0.9/test/haml/results/filters.xhtml
  109. +0 −68 vendor/gems/haml-2.0.9/test/haml/results/just_stuff.xhtml
  110. +0 −12 vendor/gems/haml-2.0.9/test/haml/results/list.xhtml
  111. +0 −42 vendor/gems/haml-2.0.9/test/haml/results/standard.xhtml
  112. +0 −12 vendor/gems/haml-2.0.9/test/haml/rhtml/_av_partial_1.rhtml
  113. +0 −8 vendor/gems/haml-2.0.9/test/haml/rhtml/_av_partial_2.rhtml
  114. +0 −62 vendor/gems/haml-2.0.9/test/haml/rhtml/action_view.rhtml
  115. +0 −54 vendor/gems/haml-2.0.9/test/haml/rhtml/standard.rhtml
  116. +0 −204 vendor/gems/haml-2.0.9/test/haml/template_test.rb
  117. +0 −10 vendor/gems/haml-2.0.9/test/haml/templates/content_for_layout.haml
  118. +0 −95 vendor/gems/haml-2.0.9/test/haml/templates/helpers.haml
  119. +0 −83 vendor/gems/haml-2.0.9/test/haml/templates/just_stuff.haml
  120. +0 −12 vendor/gems/haml-2.0.9/test/haml/templates/list.haml
  121. +0 −3 vendor/gems/haml-2.0.9/test/haml/templates/partial_layout.haml
  122. +0 −42 vendor/gems/haml-2.0.9/test/haml/templates/standard.haml
  123. +0 −42 vendor/gems/haml-2.0.9/test/haml/templates/standard_ugly.haml
  124. +0 −12 vendor/gems/haml-2.0.9/test/linked_rails.rb
  125. +0 −193 vendor/gems/haml-2.0.9/test/sass/css2sass_test.rb
  126. +0 −294 vendor/gems/haml-2.0.9/test/sass/engine_test.rb
  127. +0 −161 vendor/gems/haml-2.0.9/test/sass/plugin_test.rb
  128. +0 −5 vendor/gems/haml-2.0.9/test/sass/results/compact.css
  129. +0 −87 vendor/gems/haml-2.0.9/test/sass/results/complex.css
  130. +0 −1 vendor/gems/haml-2.0.9/test/sass/results/compressed.css
  131. +0 −16 vendor/gems/haml-2.0.9/test/sass/results/constants.css
  132. +0 −19 vendor/gems/haml-2.0.9/test/sass/results/expanded.css
  133. +0 −29 vendor/gems/haml-2.0.9/test/sass/results/import.css
  134. +0 −95 vendor/gems/haml-2.0.9/test/sass/results/mixins.css
  135. +0 −22 vendor/gems/haml-2.0.9/test/sass/results/nested.css
  136. +0 −13 vendor/gems/haml-2.0.9/test/sass/results/parent_ref.css
  137. +0 −1 vendor/gems/haml-2.0.9/test/sass/results/subdir/subdir.css
  138. +0 −16 vendor/gems/haml-2.0.9/test/sass/templates/alt.sass
  139. +0 −2 vendor/gems/haml-2.0.9/test/sass/templates/bork.sass
  140. +0 −310 vendor/gems/haml-2.0.9/test/sass/templates/complex.sass
  141. +0 −100 vendor/gems/haml-2.0.9/test/sass/templates/constants.sass
  142. +0 −11 vendor/gems/haml-2.0.9/test/sass/templates/import.sass
Sorry, we could not display the entire diff because too many files (3,287) changed.
View
1 VERSION
@@ -0,0 +1 @@
+0.4.0
View
2 config/boot.rb
@@ -82,8 +82,8 @@ def gem_version
end
def load_rubygems
+ min_version = '1.3.2'
require 'rubygems'
- min_version = '1.3.1'
unless rubygems_version >= min_version
$stderr.puts %Q(Rails requires RubyGems >= #{min_version} (you have #{rubygems_version}). Please `gem update --system` and try again.)
exit 1
View
5 config/environment.rb
@@ -1,7 +1,7 @@
# Be sure to restart your server when you modify this file
# Specifies gem version of Rails to use when vendor/rails is not present
-RAILS_GEM_VERSION = '2.3.2' unless defined? RAILS_GEM_VERSION
+RAILS_GEM_VERSION = '2.3.5' unless defined? RAILS_GEM_VERSION
# Bootstrap the Rails environment, frameworks, and default configuration
require File.join(File.dirname(__FILE__), 'boot')
@@ -11,8 +11,7 @@
config.gem 'haml'
config.gem 'mocha'
config.gem 'less'
- config.gem 'mislav-will_paginate', :version => '~> 2.3.8', :lib => 'will_paginate',
- :source => 'http://gems.github.com'
+ config.gem 'formtastic'
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
View
2 spec/models/host_spec.rb
@@ -52,7 +52,7 @@
describe 'relationships' do
before :each do
- @host = Host.new
+ @host = Host.generate!
end
it 'should have many deployed services' do
View
211 vendor/gems/formtastic-0.9.10/.specification
@@ -0,0 +1,211 @@
+--- !ruby/object:Gem::Specification
+name: formtastic
+version: !ruby/object:Gem::Version
+ hash: 47
+ prerelease: false
+ segments:
+ - 0
+ - 9
+ - 10
+ version: 0.9.10
+platform: ruby
+authors:
+- Justin French
+autorequire:
+bindir: bin
+cert_chain: []
+
+date: 2010-05-25 00:00:00 -05:00
+default_executable:
+dependencies:
+- !ruby/object:Gem::Dependency
+ name: activesupport
+ prerelease: false
+ requirement: &id001 !ruby/object:Gem::Requirement
+ none: false
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ hash: 3
+ segments:
+ - 2
+ - 3
+ - 0
+ version: 2.3.0
+ type: :runtime
+ version_requirements: *id001
+- !ruby/object:Gem::Dependency
+ name: actionpack
+ prerelease: false
+ requirement: &id002 !ruby/object:Gem::Requirement
+ none: false
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ hash: 3
+ segments:
+ - 2
+ - 3
+ - 0
+ version: 2.3.0
+ type: :runtime
+ version_requirements: *id002
+- !ruby/object:Gem::Dependency
+ name: rspec-rails
+ prerelease: false
+ requirement: &id003 !ruby/object:Gem::Requirement
+ none: false
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ hash: 19
+ segments:
+ - 1
+ - 2
+ - 6
+ version: 1.2.6
+ type: :development
+ version_requirements: *id003
+- !ruby/object:Gem::Dependency
+ name: rspec_tag_matchers
+ prerelease: false
+ requirement: &id004 !ruby/object:Gem::Requirement
+ none: false
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ hash: 23
+ segments:
+ - 1
+ - 0
+ - 0
+ version: 1.0.0
+ type: :development
+ version_requirements: *id004
+description: A Rails form builder plugin/gem with semantically rich and accessible markup
+email: justin@indent.com.au
+executables: []
+
+extensions: []
+
+extra_rdoc_files:
+- README.textile
+files:
+- MIT-LICENSE
+- README.textile
+- Rakefile
+- generators/form/USAGE
+- generators/form/form_generator.rb
+- generators/form/templates/view__form.html.erb
+- generators/form/templates/view__form.html.haml
+- generators/formtastic/formtastic_generator.rb
+- generators/formtastic/templates/formtastic.css
+- generators/formtastic/templates/formtastic.rb
+- generators/formtastic/templates/formtastic_changes.css
+- generators/formtastic_stylesheets/formtastic_stylesheets_generator.rb
+- lib/formtastic.rb
+- lib/formtastic/i18n.rb
+- lib/formtastic/layout_helper.rb
+- lib/formtastic/util.rb
+- lib/locale/en.yml
+- rails/init.rb
+- spec/buttons_spec.rb
+- spec/commit_button_spec.rb
+- spec/custom_builder_spec.rb
+- spec/custom_macros.rb
+- spec/defaults_spec.rb
+- spec/error_proc_spec.rb
+- spec/errors_spec.rb
+- spec/form_helper_spec.rb
+- spec/i18n_spec.rb
+- spec/include_blank_spec.rb
+- spec/input_spec.rb
+- spec/inputs/boolean_input_spec.rb
+- spec/inputs/check_boxes_input_spec.rb
+- spec/inputs/country_input_spec.rb
+- spec/inputs/date_input_spec.rb
+- spec/inputs/datetime_input_spec.rb
+- spec/inputs/file_input_spec.rb
+- spec/inputs/hidden_input_spec.rb
+- spec/inputs/numeric_input_spec.rb
+- spec/inputs/password_input_spec.rb
+- spec/inputs/radio_input_spec.rb
+- spec/inputs/select_input_spec.rb
+- spec/inputs/string_input_spec.rb
+- spec/inputs/text_input_spec.rb
+- spec/inputs/time_input_spec.rb
+- spec/inputs/time_zone_input_spec.rb
+- spec/inputs_spec.rb
+- spec/label_spec.rb
+- spec/layout_helper_spec.rb
+- spec/semantic_errors_spec.rb
+- spec/semantic_fields_for_spec.rb
+- spec/spec.opts
+- spec/spec_helper.rb
+has_rdoc: true
+homepage: http://github.com/justinfrench/formtastic/tree/master
+licenses: []
+
+post_install_message: "\n ========================================================================\n Thanks for installing Formtastic!\n ------------------------------------------------------------------------\n You can now (optionally) run the generator to copy some stylesheets and\n a config initializer into your application:\n ./script/generate formtastic\n\n To generate some semantic form markup for your existing models, just run:\n ./script/generate form MODEL_NAME\n\n Find out more and get involved:\n http://github.com/justinfrench/formtastic\n http://groups.google.com.au/group/formtastic\n ========================================================================\n "
+rdoc_options:
+- --charset=UTF-8
+require_paths:
+- lib
+required_ruby_version: !ruby/object:Gem::Requirement
+ none: false
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ hash: 3
+ segments:
+ - 0
+ version: "0"
+required_rubygems_version: !ruby/object:Gem::Requirement
+ none: false
+ requirements:
+ - - ">="
+ - !ruby/object:Gem::Version
+ hash: 3
+ segments:
+ - 0
+ version: "0"
+requirements: []
+
+rubyforge_project:
+rubygems_version: 1.3.7
+signing_key:
+specification_version: 3
+summary: A Rails form builder plugin/gem with semantically rich and accessible markup
+test_files:
+- spec/buttons_spec.rb
+- spec/commit_button_spec.rb
+- spec/custom_builder_spec.rb
+- spec/custom_macros.rb
+- spec/defaults_spec.rb
+- spec/error_proc_spec.rb
+- spec/errors_spec.rb
+- spec/form_helper_spec.rb
+- spec/i18n_spec.rb
+- spec/include_blank_spec.rb
+- spec/input_spec.rb
+- spec/inputs/boolean_input_spec.rb
+- spec/inputs/check_boxes_input_spec.rb
+- spec/inputs/country_input_spec.rb
+- spec/inputs/date_input_spec.rb
+- spec/inputs/datetime_input_spec.rb
+- spec/inputs/file_input_spec.rb
+- spec/inputs/hidden_input_spec.rb
+- spec/inputs/numeric_input_spec.rb
+- spec/inputs/password_input_spec.rb
+- spec/inputs/radio_input_spec.rb
+- spec/inputs/select_input_spec.rb
+- spec/inputs/string_input_spec.rb
+- spec/inputs/text_input_spec.rb
+- spec/inputs/time_input_spec.rb
+- spec/inputs/time_zone_input_spec.rb
+- spec/inputs_spec.rb
+- spec/label_spec.rb
+- spec/layout_helper_spec.rb
+- spec/semantic_errors_spec.rb
+- spec/semantic_fields_for_spec.rb
+- spec/spec_helper.rb
View
0 vendor/plugins/formtastic/MIT-LICENSE → vendor/gems/formtastic-0.9.10/MIT-LICENSE
File renamed without changes.
View
574 vendor/gems/formtastic-0.9.10/README.textile
@@ -0,0 +1,574 @@
+h1. Formtastic
+
+Formtastic is a Rails FormBuilder DSL (with some other goodies) to make it far easier to create beautiful, semantically rich, syntactically awesome, readily stylable and wonderfully accessible HTML forms in your Rails applications.
+
+h2. The Story
+
+One day, I finally had enough, so I opened up my text editor, and wrote a DSL for how I'd like to author forms:
+
+<pre>
+ <% semantic_form_for @article do |form| %>
+
+ <% form.inputs :name => "Basic" do %>
+ <%= form.input :title %>
+ <%= form.input :body %>
+ <%= form.input :section %>
+ <%= form.input :publication_state, :as => :radio %>
+ <%= form.input :category %>
+ <%= form.input :allow_comments, :label => "Allow commenting on this article" %>
+ <% end %>
+
+ <% form.inputs :name => "Advanced" do %>
+ <%= form.input :keywords, :required => false, :hint => "Example: ruby, rails, forms" %>
+ <%= form.input :extract, :required => false %>
+ <%= form.input :description, :required => false %>
+ <%= form.input :url_title, :required => false %>
+ <% end %>
+
+ <% form.inputs :name => "Author", :for => :author do |author_form| %>
+ <%= author_form.input :first_name %>
+ <%= author_form.input :last_name %>
+ <% end %>
+
+ <% form.buttons do %>
+ <%= form.commit_button %>
+ <% end %>
+
+ <% end %>
+</pre>
+
+I also wrote the accompanying HTML output I expected, favoring something very similar to the fieldsets, lists and other semantic elements Aaron Gustafson presented in "Learning to Love Forms":http://www.slideshare.net/AaronGustafson/learning-to-love-forms-web-directions-south-07, hacking together enough Ruby to prove it could be done.
+
+
+h2. It's better than _SomeOtherFormBuilder_ because...
+
+* it can handle @belongs_to@ associations (like Post belongs_to :author), rendering a select or set of radio inputs with choices from the parent model.
+* it can handle @has_many@ and @has_and_belongs_to_many@ associations (like: Post has_many :tags), rendering a multi-select with choices from the child models.
+* it's Rails 2.3-ready (including nested forms).
+* it has internationalization (I18n)!
+* it's _really_ quick to get started with a basic form in place (4 lines), then go back to add in more detail if you need it.
+* there's heaps of elements, id and class attributes for you to hook in your CSS and JS.
+* it handles real world stuff like inline hints, inline error messages & help text.
+* it doesn't hijack or change any of the standard Rails form inputs, so you can still use them as expected (even mix and match).
+* it's got absolutely awesome spec coverage.
+* there's a bunch of people using and working on it (it's not just one developer building half a solution).
+
+
+h2. Why?
+
+* web apps = lots of forms.
+* forms are so friggin' boring to code.
+* semantically rich & accessible forms really are possible.
+* the "V" is way behind the "M" and "C" in Rails' MVC – it's the ugly sibling.
+* best practices and common patterns have to start somewhere.
+* i need a challenge.
+
+
+h2. Opinions
+
+* it should be easier to do things the right way than the wrong way.
+* sometimes _more mark-up_ is better.
+* elements and attribute hooks are _gold_ for stylesheet authors.
+* make the common things we do easy, yet still ensure uncommon things are still possible.
+
+
+h2. Documentation
+
+RDoc documentation _should_ be automatically generated after each commit and made available on the "rdoc.info website":http://rdoc.info/projects/justinfrench/formtastic.
+
+
+h2. Installation
+
+The gem is hosted on gemcutter, so *if you haven't already*, add it as a gem source:
+
+<pre>
+ sudo gem sources -a http://gemcutter.org/
+</pre>
+
+Then install the Formtastic gem:
+
+<pre>
+ sudo gem install formtastic
+</pre>
+
+And add it to your environment.rb configuration as a gem dependency:
+
+<pre>
+ config.gem 'formtastic'
+</pre>
+
+Optionally, run @./script/generate formtastic@ to copy the following files into your app:
+
+* @config/initializers/formtastic.rb@ - a commented out Formtastic config initializer
+* @public/stylesheets/formtastic.css@
+* @public/stylesheets/formtastic_changes.css@
+
+A proof-of-concept stylesheet is provided which you can include in your layout. Customization is best achieved by overriding these styles in an additional stylesheet so that the Formtastic styles can be updated without clobbering your changes. If you want to use these stylesheets, add both to your layout with this helper:
+
+<pre>
+ <head>
+ ...
+ <%= formtastic_stylesheet_link_tag %>
+ ...
+ </head>
+</pre>
+
+h2. Usage
+
+Forms are really boring to code... you want to get onto the good stuff as fast as possible.
+
+This renders a set of inputs (one for _most_ columns in the database table, and one for each ActiveRecord @belongs_to@-association), followed by a submit button:
+
+<pre>
+ <% semantic_form_for @user do |form| %>
+ <%= form.inputs %>
+ <%= form.buttons %>
+ <% end %>
+</pre>
+
+If you want to specify the order of the fields, skip some of the fields or even add in fields that Formtastic couldn't detect, you can pass in a list of field names to @inputs@ and list of button names to @buttons@:
+
+<pre>
+ <% semantic_form_for @user do |form| %>
+ <%= form.inputs :title, :body, :section, :categories, :created_at %>
+ <%= form.buttons :commit %>
+ <% end %>
+</pre>
+
+If you want control over the input type Formtastic uses for each field, you can expand the @inputs@ and @buttons@ blocks. This specifies the @:section@ input should be a set of radio buttons (rather than the default select box), and that the @:created_at@ field should be a string (rather than the default datetime selects):
+
+<pre>
+ <% semantic_form_for @post do |form| %>
+ <% form.inputs do %>
+ <%= form.input :title %>
+ <%= form.input :body %>
+ <%= form.input :section, :as => :radio %>
+ <%= form.input :categories %>
+ <%= form.input :created_at, :as => :string %>
+ <% end %>
+ <% form.buttons do %>
+ <%= form.commit_button %>
+ <% end %>
+ <% end %>
+</pre>
+
+If you want to customize the label text, or render some hint text below the field, specify which fields are required/optional, or break the form into two fieldsets, the DSL is pretty comprehensive:
+
+<pre>
+ <% semantic_form_for @post do |form| %>
+ <% form.inputs "Basic", :id => "basic" do %>
+ <%= form.input :title %>
+ <%= form.input :body %>
+ <% end %>
+ <% form.inputs :name => "Advanced Options", :id => "advanced" do %>
+ <%= form.input :slug, :label => "URL Title", :hint => "Created automatically if left blank", :required => false %>
+ <%= form.input :section, :as => :radio %>
+ <%= form.input :user, :label => "Author", :label_method => :full_name, %>
+ <%= form.input :categories, :required => false %>
+ <%= form.input :created_at, :as => :string, :label => "Publication Date", :required => false %>
+ <% end %>
+ <% form.buttons do %>
+ <%= form.commit_button %>
+ <% end %>
+ <% end %>
+</pre>
+
+Nested forms (Rails 2.3) are also supported (don't forget your models need to be setup correctly with accepts_nested_attributes_for – search the Rails docs). You can do it in the Rails way:
+
+<pre>
+ <% semantic_form_for @post do |form| %>
+ <%= form.inputs :title, :body, :created_at %>
+ <% form.semantic_fields_for :author do |author| %>
+ <%= author.inputs :first_name, :last_name, :name => "Author" %>
+ <% end %>
+ <%= form.buttons %>
+ <% end %>
+</pre>
+
+Or the Formtastic way with the @:for@ option:
+
+<pre>
+ <% semantic_form_for @post do |form| %>
+ <%= form.inputs :title, :body, :created_at %>
+ <%= form.inputs :first_name, :last_name, :for => :author, :name => "Author" %>
+ <%= form.buttons %>
+ <% end %>
+</pre>
+
+When working in has many association, you can even supply @"%i"@ in your fieldset name that it will be properly interpolated with the child index. For example:
+
+<pre>
+ <% semantic_form_for @post do |form| %>
+ <%= form.inputs %>
+ <%= form.inputs :name => 'Category #%i', :for => :categories %>
+ <%= form.buttons %>
+ <% end %>
+</pre>
+
+
+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:
+
+<pre>
+ <% semantic_form_for @post do |form| %>
+ <%= form.input :title, :input_html => { :size => 60 } %>
+ <%= form.input :body, :input_html => { :class => 'autogrow' } %>
+ <%= form.input :created_at, :input_html => { :disabled => true } %>
+ <%= form.buttons %>
+ <% end %>
+</pre>
+
+The same can be done for buttons with the @:button_html@ option:
+
+<pre>
+ <% semantic_form_for @post do |form| %>
+ ...
+ <% form.buttons do %>
+ <%= form.commit_button :button_html => { :class => "primary" } %>
+ <% end %>
+ <% end %>
+</pre>
+
+Customize the HTML attributes for the @<li>@ wrapper around every input with the @:wrapper_html@ option hash. There's one special key in the hash (@:class@), which will actually _append_ your string of classes to the existing classes provided by Formtastic (like @"required string error"@)
+
+<pre>
+ <% semantic_form_for @post do |form| %>
+ <%= form.input :title, :wrapper_html => { :class => "important" } %>
+ <%= form.input :body %>
+ <%= form.input :description, :wrapper_html => { :style => "display:none;" } %>
+ ...
+ <% end %>
+</pre>
+
+Many inputs provide a collection of options to choose from (like @:select@, @:radio@, @:check_boxes@, @:boolean@). In many cases, Formtastic can find choices through the model associations, but if you want to use your own set of choices, the @:collection@ option is what you want. You can pass in an Array of objects, an array of Strings, a Hash... Throw almost anything at it! Examples:
+
+<pre>
+ f.input :authors, :as => :check_boxes, :collection => User.find(:all, :order => "last_name ASC")
+ f.input :authors, :as => :check_boxes, :collection => current_user.company.users.active
+ f.input :authors, :as => :check_boxes, :collection => [@justin, @kate]
+ f.input :authors, :as => :check_boxes, :collection => ["Justin", "Kate", "Amelia", "Gus", "Meg"]
+ f.input :author, :as => :select, :collection => Author.find(:all)
+ f.input :author, :as => :select, :collection => { @justin.name => @justin.id, @kate.name => @kate.id }
+ f.input :author, :as => :select, :collection => ["Justin", "Kate", "Amelia", "Gus", "Meg"]
+ f.input :author, :as => :radio, :collection => User.find(:all)
+ f.input :author, :as => :radio, :collection => [@justin, @kate]
+ f.input :author, :as => :radio, :collection => { @justin.name => @justin.id, @kate.name => @kate.id }
+ f.input :author, :as => :radio, :collection => ["Justin", "Kate", "Amelia", "Gus", "Meg"]
+ f.input :admin, :as => :radio, :collection => ["Yes!", "No"]
+</pre>
+
+
+h2. The Available Inputs
+
+The Formtastic input types:
+
+* @:select@ - a select menu. Default for ActiveRecord associations: @belongs_to@, @has_many@, and @has_and_belongs_to_many@.
+* @:check_boxes@ - a set of check_box inputs. Alternative to @:select@ for ActiveRecord-associations: @has_many@, and @has_and_belongs_to_many@.
+* @:radio@ - a set of radio inputs. Alternative to @:select@ for ActiveRecord-associations: @belongs_to@.
+* @:time_zone@ - a select input. Default for column types: @:string@ with name matching @"time_zone"@.
+* @:password@ - a password input. Default for column types: @:string@ with name matching @"password"@.
+* @:text@ - a textarea. Default for column types: @:text@.
+* @:date@ - a date select. Default for column types: @:date@.
+* @:datetime@ - a date and time select. Default for column types: @:datetime@ and @:timestamp@.
+* @: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@.
+* @: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.
+* @:hidden@ - a hidden field. Creates a hidden field (added for compatibility).
+
+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.
+
+
+h2. Delegation for label lookups
+
+Formtastic decides which label to use in the following order:
+
+<pre>
+ 1. :label # :label => "Choose Title"
+ 2. Formtastic i18n # if either :label => true || i18n_lookups_by_default = true (see Internationalization)
+ 3. Activerecord i18n # if localization file found for the given attribute
+ 4. label_str_method # if nothing provided this defaults to :humanize but can be set to a custom method
+</pre>
+
+h2. Internationalization (I18n)
+
+h3. Basic Localization
+
+Formtastic got some neat I18n-features. ActiveRecord object names and attributes are, by default, taken from calling @@object.human_name@ and @@object.human_attribute_name(attr)@ respectively. There are a few words specific to Formtastic that can be translated. See @lib/locale/en.yml@ for more information.
+
+Basic localization (labels only, with ActiveRecord):
+
+<pre>
+ <% semantic_form_for @post do |form| %>
+ <%= form.input :title %> # => :label => I18n.t('activerecord.attributes.user.title') or 'Title'
+ <%= form.input :body %> # => :label => I18n.t('activerecord.attributes.user.body') or 'Body'
+ <%= form.input :section %> # => :label => I18n.t('activerecord.attributes.user.section') or 'Section'
+ <% end %>
+</pre>
+
+*Note:* This is perfectly fine if you just want your labels/attributes and/or models to be translated using *ActiveRecord I18n attribute translations*, and you don't use input hints and legends. But what if you do? And what if you don't want same labels in all forms?
+
+h3. Enhanced Localization (Formtastic I18n API)
+
+Formtastic supports localized *labels*, *hints*, *legends*, *actions* using the I18n API for more advanced usage. Your forms can now be DRYer and more flexible than ever, and still fully localized. This is how:
+
+*1. Enable I18n lookups by default (@config/initializers/formtastic.rb@):*
+
+<pre>
+ Formtastic::SemanticFormBuilder.i18n_lookups_by_default = true
+</pre>
+
+*2. Add some cool label-translations/variants (@config/locale/en.yml@):*
+
+<pre>
+ en:
+ formtastic:
+ titles:
+ post_details: "Post details"
+ labels:
+ post:
+ title: "Choose a title..."
+ body: "Write something..."
+ edit:
+ title: "Edit title"
+ hints:
+ post:
+ title: "Choose a good title for you post."
+ body: "Write something inspiring here."
+ actions:
+ create: "Create my {{model}}"
+ update: "Save changes"
+ dummie: "Launch!"
+</pre>
+
+*Note:* We are using English here still, but you get the point.
+
+*3. ...and now you'll get:*
+
+<pre>
+ <% semantic_form_for Post.new do |form| %>
+ <% form.inputs do %>
+ <%= form.input :title %> # => :label => "Choose a title...", :hint => "Choose a good title for you post."
+ <%= form.input :body %> # => :label => "Write something...", :hint => "Write something inspiring here."
+ <%= form.input :section %> # => :label => I18n.t('activerecord.attributes.user.section') or 'Section'
+ <% end %>
+ <% form.buttons do %>
+ <%= form.commit_button %> # => "Create my {{model}}"
+ <% end %>
+ <% end %>
+</pre>
+
+*4. Localized titles (a.k.a. legends):*
+
+_Note: Slightly different because Formtastic can't guess how you group fields in a form. Legend text can be set with first (as in the sample below) specified value, or :name/:title options - depending on what flavor is preferred._
+
+<pre>
+ <% semantic_form_for @post do |form| %>
+ <% form.inputs :post_details do %> # => :title => "Post details"
+ # ...
+ <% end %>
+ # ...
+<% end %>
+</pre>
+
+*5. Override I18n settings:*
+
+<pre>
+ <% semantic_form_for @post do |form| %>
+ <% form.inputs do %>
+ <%= form.input :title %> # => :label => "Choose a title...", :hint => "Choose a good title for you post."
+ <%= form.input :body, :hint => false %> # => :label => "Write something..."
+ <%= form.input :section, :label => 'Some section' %> # => :label => 'Some section'
+ <% end %>
+ <% form.buttons do %>
+ <%= form.commit_button :dummie %> # => "Launch!"
+ <% end %>
+ <% end %>
+</pre>
+
+If I18n-lookups is disabled, i.e.:
+
+<pre>
+ Formtastic::SemanticFormBuilder.i18n_lookups_by_default = false
+</pre>
+
+...then you can enable I18n within the forms instead:
+
+<pre>
+ <% semantic_form_for @post do |form| %>
+ <% form.inputs do %>
+ <%= form.input :title, :label => true %> # => :label => "Choose a title..."
+ <%= form.input :body, :label => true %> # => :label => "Write something..."
+ <%= form.input :section, :label => true %> # => :label => I18n.t('activerecord.attributes.user.section') or 'Section'
+ <% end %>
+ <% form.buttons do %>
+ <%= form.commit_button true %> # => "Save changes" (if we are in edit that is...)
+ <% end %>
+ <% end %>
+</pre>
+
+*6. Advanced I18n lookups*
+
+For more flexible forms; Formtastic find translations using a bottom-up approach taking the following variables in account:
+
+* @MODEL@, e.g. "post"
+* @ACTION@, e.g. "edit"
+* @KEY/ATTRIBUTE@, e.g. "title", :my_custom_key, ...
+
+...in the following order:
+
+1. @formtastic.{titles,labels,hints,actions}.MODEL.ACTION.ATTRIBUTE@ - by model and action
+2. @formtastic.{titles,labels,hints,actions}.MODEL.ATTRIBUTE@ - by model
+3. @formtastic.{titles,labels,hints,actions}.ATTRIBUTE@ - global default
+
+...which means that you can define translations like this:
+
+<pre>
+ en:
+ formtastic:
+ labels:
+ title: "Title" # Default global value
+ article:
+ body: "Article content"
+ post:
+ new:
+ title: "Choose a title..."
+ body: "Write something..."
+ edit:
+ title: "Edit title"
+ body: "Edit body"
+</pre>
+
+Values for @labels@/@hints@/@actions@ are can take values: @String@ (explicit value), @Symbol@ (i18n-lookup-key relative to the current "type", e.g. actions:), @true@ (force I18n lookup), @false@ (force no I18n lookup). Titles (legends) can only take: @String@ and @Symbol@ - true/false have no meaning.
+
+
+h2. Semantic errors
+
+You can show errors on base (by default) and any other attribute just passing it name to semantic_errors method:
+
+<pre>
+ <% semantic_form_for @post do |form| %>
+ <%= form.semantic_errors :state %>
+ <% end %>
+</pre>
+
+
+h2. ValidationReflection plugin
+
+If you have the "ValidationReflection":http://github.com/redinger/validation_reflection plugin installed, you won't have to specify the @:required@ option (it checks the validations on the model instead).
+
+
+h2. Configuration
+
+Run @./script/generate formtastic@ to copy a commented out config file into @config/initializers/formtastic.rb@. You can "view the configuration file on GitHub":http://github.com/justinfrench/formtastic/blob/master/generators/formtastic/templates/formtastic.rb
+
+
+h2. Form Generator
+
+There's a Formtastic form code generator to make your transition to Formtastic easier. All you have to do is to *specify an existing model name*, and optionally specify view template framework (ERB/HAML), and you are good to go. *Note:* This won't overwrite any of you stuff. This is how you use it:
+
+*Alt. 1: Generate in terminal:*
+
+<pre>
+$ ./script/generate form Post
+# ---------------------------------------------------------
+# GENERATED FORMTASTIC CODE
+# ---------------------------------------------------------
+
+<% f.inputs do %>
+ <%= f.input :title, :label => 'Title' %>
+ <%= f.input :body, :label => 'Body' %>
+ <%= f.input :published, :label => 'Published' %>
+<% end %>
+
+# ---------------------------------------------------------
+ Copied to clipboard - just paste it!
+</pre>
+
+*Alt. 2: Generate partial:*
+
+<pre>
+$ ./script/generate form Post --partial
+ exists app/views/posts
+ create app/views/posts/_form.html.erb
+</pre>
+
+To generate *HAML* markup, just add the @--haml@ as argument:
+
+<pre>
+$ ./script/generate form Post --haml
+ exists app/views/admin/posts
+ create app/views/admin/posts/_form.html.haml
+</pre>
+
+To specify the controller in a namespace (eg admin/posts instead of posts), use the --controller argument:
+
+<pre>
+$ ./script/generate form Post --partial --controller admin/posts
+ exists app/views/admin/posts
+ create app/views/admin/posts/_form.html.erb
+</pre>
+
+
+h2. Custom Inputs
+
+If you want to add your own input types to encapsulate your own logic or interface patterns, you can do so by subclassing SemanticFormBuilder and configuring Formtastic to use your custom builder class.
+
+@Formtastic::SemanticFormHelper.builder = MyCustomBuilder@
+
+
+
+
+h2. Status
+
+Formtastic has been in active development for about a year. We've just recently jumped to an 0.9 version number, signaling that we consider this a 1.0 release candidate, and that the API won't change significantly for the 1.x series.
+
+
+h2. Dependencies
+
+There are none, but...
+
+* if you have the "ValidationReflection":http://github.com/redinger/validation_reflection plugin is installed, you won't have to specify the @:required@ option (it checks the validations on the model instead).
+* if you want to use the @:country@ input, you'll need to install the "iso-3166-country-select plugin":http://github.com/rails/iso-3166-country-select (or any other country_select plugin with the same API).
+* "rspec":http://github.com/dchelimsky/rspec/, "rspec_hpricot_matchers":http://rubyforge.org/projects/rspec-hpricot/ and "rcov":http://github.com/relevance/rcov gems (plus any of their own dependencies) are required for the test suite.
+
+
+h2. Compatibility
+
+I'm only testing Formtastic with the latest Rails 2.4.x stable release, and it should be fine under Rails 2.3.x as well (including nested forms). Patches are welcome to allow backwards compatibility, but I don't have the energy!
+
+h2. Got TextMate?
+
+Well...there's a TextMate-bundle in town, dedicated to make usage of Formtastic in the "TextMate":http://macromates.com/ editor even more of a breeze:
+
+"Formtastic.tmbundle":http://github.com/grimen/formtastic_tmbundle
+
+
+h2. How to contribute
+
+*Before you send a pull request*, please ensure that you provide appropriate spec/test coverage and ensure the documentation is up-to-date. Bonus points if you perform your changes in a clean topic branch rather than master.
+
+Please also keep your commits *atomic* so that they are more likely to apply cleanly. That means that each commit should contain the smallest possible logical change. Don't commit two features at once, don't update the gemspec at the same time you add a feature, don't fix a whole bunch of whitespace in a file at the same time you change a few lines, etc, etc.
+
+For significant changes, you may wish to discuss your idea on the Formtastic Google group before coding to ensure that your change is likely to be accepted. Formtastic relies heavily on i18n, so if you're unsure of the impact this has on your changes, please discuss them with the group.
+
+
+h2. Maintainers & Contributors
+
+Formtastic is maintained by "Justin French":http://justinfrench.com, "José Valim":http://github.com/josevalim and "Jonas Grimfelt":http://github.com/grimen, but it wouldn't be as awesome as it is today without help from over 40 contributors.
+
+@git shortlog -n -s --no-merges@
+
+
+h2. Google Group
+
+Please join the "Formtastic Google Group":http://groups.google.com.au/group/formtastic, especially if you'd like to talk about a new feature, or report a bug.
+
+
+h2. Project Info
+
+Formtastic is hosted on Github: "http://github.com/justinfrench/formtastic":http://github.com/justinfrench/formtastic, where your contributions, forkings, comments and feedback are greatly welcomed.
+
+
+Copyright (c) 2007-2008 Justin French, released under the MIT license.
View
101 vendor/gems/formtastic-0.9.10/Rakefile
@@ -0,0 +1,101 @@
+# coding: utf-8
+require 'rake'
+require 'rake/rdoctask'
+
+begin
+ require 'spec/rake/spectask'
+rescue LoadError
+ begin
+ gem 'rspec-rails', '>= 1.0.0'
+ require 'spec/rake/spectask'
+ rescue LoadError
+ puts "[formtastic:] RSpec - or one of it's dependencies - is not available. Install it with: sudo gem install rspec-rails"
+ end
+end
+
+begin
+ GEM = "formtastic"
+ AUTHOR = "Justin French"
+ EMAIL = "justin@indent.com.au"
+ SUMMARY = "A Rails form builder plugin/gem with semantically rich and accessible markup"
+ HOMEPAGE = "http://github.com/justinfrench/formtastic/tree/master"
+ INSTALL_MESSAGE = %q{
+ ========================================================================
+ Thanks for installing Formtastic!
+ ------------------------------------------------------------------------
+ You can now (optionally) run the generator to copy some stylesheets and
+ a config initializer into your application:
+ ./script/generate formtastic
+
+ To generate some semantic form markup for your existing models, just run:
+ ./script/generate form MODEL_NAME
+
+ Find out more and get involved:
+ http://github.com/justinfrench/formtastic
+ http://groups.google.com.au/group/formtastic
+ ========================================================================
+ }
+
+ gem 'jeweler', '>= 1.0.0'
+ require 'jeweler'
+
+ Jeweler::Tasks.new do |s|
+ s.name = GEM
+ s.summary = SUMMARY
+ s.email = EMAIL
+ s.homepage = HOMEPAGE
+ s.description = SUMMARY
+ s.author = AUTHOR
+ s.post_install_message = INSTALL_MESSAGE
+
+ s.require_path = 'lib'
+ s.files = %w(MIT-LICENSE README.textile Rakefile) + Dir.glob("{rails,lib,generators,spec}/**/*")
+
+ # Runtime dependencies: When installing Formtastic these will be checked if they are installed.
+ # Will be offered to install these if they are not already installed.
+ s.add_dependency 'activesupport', '>= 2.3.0'
+ s.add_dependency 'actionpack', '>= 2.3.0'
+
+ # Development dependencies. Not installed by default.
+ # Install with: sudo gem install formtastic --development
+ s.add_development_dependency 'rspec-rails', '>= 1.2.6'
+ s.add_development_dependency 'rspec_tag_matchers', '>= 1.0.0'
+ end
+
+ Jeweler::GemcutterTasks.new
+rescue LoadError
+ puts "[formtastic:] Jeweler - or one of its dependencies - is not available. Install it with: sudo gem install jeweler -s http://gemcutter.org"
+end
+
+desc 'Default: run unit specs.'
+task :default => :spec
+
+desc 'Generate documentation for the formtastic plugin.'
+Rake::RDocTask.new(:rdoc) do |rdoc|
+ rdoc.rdoc_dir = 'rdoc'
+ rdoc.title = 'Formtastic'
+ rdoc.options << '--line-numbers' << '--inline-source'
+ rdoc.rdoc_files.include('README.textile')
+ rdoc.rdoc_files.include('lib/**/*.rb')
+end
+
+if defined?(Spec)
+ desc 'Test the formtastic plugin.'
+ Spec::Rake::SpecTask.new('spec') do |t|
+ t.spec_files = FileList['spec/**/*_spec.rb']
+ t.spec_opts = ["-c"]
+ end
+
+ desc 'Test the formtastic plugin with specdoc formatting and colors'
+ Spec::Rake::SpecTask.new('specdoc') do |t|
+ t.spec_files = FileList['spec/**/*_spec.rb']
+ t.spec_opts = ["--format specdoc", "-c"]
+ end
+
+ desc "Run all examples with RCov"
+ Spec::Rake::SpecTask.new('examples_with_rcov') do |t|
+ t.spec_files = FileList['spec/**/*_spec.rb']
+ t.rcov = true
+ t.rcov_opts = ['--exclude', 'spec,Library']
+ end
+end
View
16 vendor/gems/formtastic-0.9.10/generators/form/USAGE
@@ -0,0 +1,16 @@
+NAME
+ form - Formtastic form generator.
+
+DESCRIPTION
+ 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.
+
+ Required:
+ ExistingModelName - The name of an existing model for which the generator should generate form code.
+
+ Options:
+ --haml Generate HAML instead of ERB.
+ --partial Generate a form partial in the model views path, i.e. "_form.html.erb" or _form.html.haml".
+ --controller PATH Generate for custom controller/view path - in case model and controller namespace is different, i.e. "admin/posts".
+
+EXAMPLE
+ ./script/generate form ExistingModelName [--haml] [--partial]
View
120 vendor/gems/formtastic-0.9.10/generators/form/form_generator.rb
@@ -0,0 +1,120 @@
+# coding: utf-8
+# Get current OS - needed for clipboard functionality
+case RUBY_PLATFORM
+when /darwin/ then
+ CURRENT_OS = :osx
+when /win32/
+ CURRENT_OS = :win
+ begin
+ require 'win32/clipboard'
+ rescue LoadError
+ # Do nothing
+ end
+else
+ CURRENT_OS = :x
+end
+
+class FormGenerator < Rails::Generator::NamedBase
+
+ default_options :haml => false,
+ :partial => false
+
+ VIEWS_PATH = File.join('app', 'views').freeze
+ IGNORED_COLUMNS = [:updated_at, :created_at].freeze
+
+ attr_reader :controller_file_name,
+ :controller_class_path,
+ :controller_class_nesting,
+ :controller_class_nesting_depth,
+ :controller_class_name,
+ :template_type
+
+ def initialize(runtime_args, runtime_options = {})
+ super
+ base_name, @controller_class_path = extract_modules(@name.pluralize)
+ controller_class_name_without_nesting, @controller_file_name = inflect_names(base_name)
+ @template_type = options[:haml] ? :haml : :erb
+ end
+
+ def manifest
+ record do |m|
+ if options[:partial]
+ controller_and_view_path = options[:controller] || File.join(controller_class_path, controller_file_name)
+ # Ensure directory exists.
+ m.directory File.join(VIEWS_PATH, controller_and_view_path)
+ # Create a form partial for the model as "_form" in it's views path.
+ m.template "view__form.html.#{template_type}", File.join(VIEWS_PATH, controller_and_view_path, "_form.html.#{template_type}")
+ else
+ # Load template file, and render without saving to file
+ template = File.read(File.join(source_root, "view__form.html.#{template_type}"))
+ erb = ERB.new(template, nil, '-')
+ generated_code = erb.result(binding).strip rescue nil
+
+ # Print the result, and copy to clipboard
+ puts "# ---------------------------------------------------------"
+ puts "# GENERATED FORMTASTIC CODE"
+ puts "# ---------------------------------------------------------"
+ puts
+ puts generated_code || " Nothing could be generated - model exists?"
+ puts
+ puts "# ---------------------------------------------------------"
+ puts " Copied to clipboard - just paste it!" if save_to_clipboard(generated_code)
+ end
+ end
+ end
+
+ protected
+
+ # Save to lipboard with multiple OS support.
+ def save_to_clipboard(data)
+ return unless data
+ begin
+ case CURRENT_OS
+ when :osx
+ `echo "#{data}" | pbcopy`
+ when :win
+ ::Win32::Clipboard.data = data
+ else # :linux/:unix
+ `echo "#{data}" | xsel --clipboard` || `echo "#{data}" | xclip`
+ end
+ rescue
+ false
+ else
+ true
+ end
+ end
+
+ # Add additional model attributes if specified in args - probably not that common scenario.
+ def attributes
+ # Get columns for the requested model.
+ existing_attributes = @class_name.constantize.content_columns.reject { |column| IGNORED_COLUMNS.include?(column.name.to_sym) }
+ @args = super + existing_attributes
+ end
+
+ def add_options!(opt)
+ opt.separator ''
+ opt.separator 'Options:'
+
+ # Allow option to generate HAML views instead of ERB.
+ opt.on('--haml',
+ "Generate HAML output instead of the default ERB.") do |v|
+ options[:haml] = v
+ end
+
+ # Allow option to generate to partial in model's views path, instead of printing out in terminal.
+ opt.on('--partial',
+ "Save generated output directly to a form partial (app/views/{resource}/_form.html.*).") do |v|
+ options[:partial] = v
+ end
+
+ opt.on('--controller CONTROLLER_PATH',
+ "Specify a non-standard controller for the specified model (e.g. admin/posts).") do |v|
+ options[:controller] = v if v.present?
+ end
+ end
+
+ def banner
+ "Usage: #{$0} form ExistingModelName [--haml] [--partial]"
+ end
+
+end
View
5 vendor/gems/formtastic-0.9.10/generators/form/templates/view__form.html.erb
@@ -0,0 +1,5 @@
+<%% f.inputs do %>
+<% attributes.each do |attribute| -%>
+ <%%= f.input :<%= attribute.name %>, :label => '<%= attribute.name.humanize %>' %>
+<% end -%>
+<%% end %>
View
4 vendor/gems/formtastic-0.9.10/generators/form/templates/view__form.html.haml
@@ -0,0 +1,4 @@
+- f.inputs do
+<% attributes.each do |attribute| -%>
+ = f.input :<%= attribute.name %>, :label => '<%= attribute.name.humanize %>'
+<% end -%>
View
0 ...rators/formtastic/formtastic_generator.rb → ...rators/formtastic/formtastic_generator.rb
File renamed without changes.
View
146 vendor/gems/formtastic-0.9.10/generators/formtastic/templates/formtastic.css
@@ -0,0 +1,146 @@
+/* -------------------------------------------------------------------------------------------------
+
+It's *strongly* suggested that you don't modify this file. Instead, load a new stylesheet after
+this one in your layouts (eg formtastic_changes.css) and override the styles to suit your needs.
+This will allow you to update formtastic.css with new releases without clobbering your own changes.
+
+This stylesheet forms part of the Formtastic Rails Plugin
+(c) 2008 Justin French
+
+--------------------------------------------------------------------------------------------------*/
+
+
+/* NORMALIZE AND RESET - obviously inspired by Yahoo's reset.css, but scoped to just form.formtastic
+--------------------------------------------------------------------------------------------------*/
+form.formtastic, form.formtastic ul, form.formtastic ol, form.formtastic li, form.formtastic fieldset, form.formtastic legend, form.formtastic input, form.formtastic textarea, form.formtastic select, form.formtastic p { margin:0; padding:0; }
+form.formtastic fieldset { border:0; }
+form.formtastic em, form.formtastic strong { font-style:normal; font-weight:normal; }
+form.formtastic ol, form.formtastic ul { list-style:none; }
+form.formtastic abbr, form.formtastic acronym { border:0; font-variant:normal; }
+form.formtastic input, form.formtastic textarea, form.formtastic select { font-family:inherit; font-size:inherit; font-weight:inherit; }
+form.formtastic input, form.formtastic textarea, form.formtastic select { font-size:100%; }
+form.formtastic legend { white-space:normal; color:#000; }
+
+
+/* SEMANTIC ERRORS
+--------------------------------------------------------------------------------------------------*/
+form.formtastic ul.errors { color:#cc0000; margin:0.5em 0 1.5em 25%; list-style:square; }
+form.formtastic ul.errors li { padding:0; border:none; display:list-item; }
+
+
+/* FIELDSETS & LISTS
+--------------------------------------------------------------------------------------------------*/
+form.formtastic fieldset { }
+form.formtastic fieldset.inputs { }
+form.formtastic fieldset.buttons { padding-left:25%; }
+form.formtastic fieldset ol { }
+form.formtastic fieldset.buttons li { float:left; padding-right:0.5em; }
+
+/* clearfixing the fieldsets */
+form.formtastic fieldset { display: inline-block; }
+form.formtastic fieldset:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+html[xmlns] form.formtastic fieldset { display: block; }
+* html form.formtastic fieldset { height: 1%; }
+
+
+/* INPUT LIs
+--------------------------------------------------------------------------------------------------*/
+form.formtastic fieldset > ol > li { margin-bottom:1.5em; }
+
+/* clearfixing the li's */
+form.formtastic fieldset > ol > li { display: inline-block; }
+form.formtastic fieldset > ol > li:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; }
+html[xmlns] form.formtastic fieldset > ol > li { display: block; }
+* html form.formtastic fieldset > ol > li { height: 1%; }
+
+form.formtastic fieldset > ol > li.required { }
+form.formtastic fieldset > ol > li.optional { }
+form.formtastic fieldset > ol > li.error { }
+
+
+/* LABELS
+--------------------------------------------------------------------------------------------------*/
+form.formtastic fieldset > ol > li label { display:block; width:25%; float:left; padding-top:.2em; }
+form.formtastic fieldset > ol > li > li label { line-height:100%; padding-top:0; }
+form.formtastic fieldset > ol > li > li label input { line-height:100%; vertical-align:middle; margin-top:-0.1em;}
+
+
+/* NESTED FIELDSETS AND LEGENDS (radio, check boxes and date/time inputs use nested fieldsets)
+--------------------------------------------------------------------------------------------------*/
+form.formtastic fieldset > ol > li fieldset { position:relative; }
+form.formtastic fieldset > ol > li fieldset legend { position:absolute; width:95%; padding-top:0.1em; left: 0px; }
+form.formtastic fieldset > ol > li fieldset legend span { position:absolute; }
+form.formtastic fieldset > ol > li fieldset legend.label label { position:absolute; }
+form.formtastic fieldset > ol > li fieldset ol { float:left; width:74%; margin:0; padding:0 0 0 25%; }
+form.formtastic fieldset > ol > li fieldset ol li { padding:0; border:0; }
+
+
+/* INLINE HINTS
+--------------------------------------------------------------------------------------------------*/
+form.formtastic fieldset > ol > li p.inline-hints { color:#666; margin:0.5em 0 0 25%; }
+
+
+/* INLINE ERRORS
+--------------------------------------------------------------------------------------------------*/
+form.formtastic fieldset > ol > li p.inline-errors { color:#cc0000; margin:0.5em 0 0 25%; }
+form.formtastic fieldset > ol > li ul.errors { color:#cc0000; margin:0.5em 0 0 25%; list-style:square; }
+form.formtastic fieldset > ol > li ul.errors li { padding:0; border:none; display:list-item; }
+
+
+/* STRING & NUMERIC OVERRIDES
+--------------------------------------------------------------------------------------------------*/
+form.formtastic fieldset > ol > li.string input { max-width:74%; }
+form.formtastic fieldset > ol > li.password input { max-width: 13em; }
+form.formtastic fieldset > ol > li.numeric input { max-width:74%; }
+
+
+/* TEXTAREA OVERRIDES
+--------------------------------------------------------------------------------------------------*/
+form.formtastic fieldset > ol > li.text textarea { width:74%; }
+
+
+/* HIDDEN OVERRIDES
+The dual declarations are required because of our clearfix display hack on the LIs, which is more
+specific than the more general rule below. TODO: Revist the clearing hack and this rule.
+--------------------------------------------------------------------------------------------------*/
+form.formtastic fieldset ol li.hidden,
+html[xmlns] form.formtastic fieldset ol li.hidden { display:none; }
+
+/* BOOLEAN OVERRIDES
+--------------------------------------------------------------------------------------------------*/
+form.formtastic fieldset > ol > li.boolean label { padding-left:25%; width:auto; }
+form.formtastic fieldset > ol > li.boolean label input { margin:0 0.5em 0 0.2em; }
+
+
+/* RADIO OVERRIDES
+--------------------------------------------------------------------------------------------------*/
+form.formtastic fieldset > ol > li.radio { }
+form.formtastic fieldset > ol > li.radio fieldset ol { margin-bottom:-0.6em; }
+form.formtastic fieldset > ol > li.radio fieldset ol li { margin:0.1em 0 0.5em 0; }
+form.formtastic fieldset > ol > li.radio fieldset ol li label { float:none; width:100%; }
+form.formtastic fieldset > ol > li.radio fieldset ol li label input { margin-right:0.2em; }
+
+
+/* CHECK BOXES (COLLECTION) OVERRIDES
+--------------------------------------------------------------------------------------------------*/
+form.formtastic fieldset > ol > li.check_boxes { }
+form.formtastic fieldset > ol > li.check_boxes fieldset ol { margin-bottom:-0.6em; }
+form.formtastic fieldset > ol > li.check_boxes fieldset ol li { margin:0.1em 0 0.5em 0; }
+form.formtastic fieldset > ol > li.check_boxes fieldset ol li label { float:none; width:100%; }
+form.formtastic fieldset > ol > li.check_boxes fieldset ol li label input { margin-right:0.2em; }
+
+
+
+/* DATE & TIME OVERRIDES
+--------------------------------------------------------------------------------------------------*/
+form.formtastic fieldset > ol > li.date fieldset ol li,
+form.formtastic fieldset > ol > li.time fieldset ol li,
+form.formtastic fieldset > ol > li.datetime fieldset ol li { float:left; width:auto; margin:0 .3em 0 0; }
+
+form.formtastic fieldset > ol > li.date fieldset ol li label,
+form.formtastic fieldset > ol > li.time fieldset ol li label,
+form.formtastic fieldset > ol > li.datetime fieldset ol li label { display:none; }
+
+form.formtastic fieldset > ol > li.date fieldset ol li label input,
+form.formtastic fieldset > ol > li.time fieldset ol li label input,
+form.formtastic fieldset > ol > li.datetime fieldset ol li label input { display:inline; margin:0; padding:0; }
View
54 vendor/gems/formtastic-0.9.10/generators/formtastic/templates/formtastic.rb
@@ -0,0 +1,54 @@
+# Set the default text field size when input is a string. Default is 50.
+# Formtastic::SemanticFormBuilder.default_text_field_size = 50
+
+# Set the default text area height when input is a text. Default is 20.
+# Formtastic::SemanticFormBuilder.default_text_area_height = 5
+
+# Should all fields be considered "required" by default?
+# Defaults to true, see ValidationReflection notes below.
+# Formtastic::SemanticFormBuilder.all_fields_required_by_default = true
+
+# Should select fields have a blank option/prompt by default?
+# Defaults to true.
+# Formtastic::SemanticFormBuilder.include_blank_for_select_by_default = true
+
+# Set the string that will be appended to the labels/fieldsets which are required
+# It accepts string or procs and the default is a localized version of
+# '<abbr title="required">*</abbr>'. In other words, if you configure formtastic.required
+# in your locale, it will replace the abbr title properly. But if you don't want to use
+# abbr tag, you can simply give a string as below
+# Formtastic::SemanticFormBuilder.required_string = "(required)"
+
+# Set the string that will be appended to the labels/fieldsets which are optional
+# Defaults to an empty string ("") and also accepts procs (see required_string above)
+# Formtastic::SemanticFormBuilder.optional_string = "(optional)"
+
+# Set the way inline errors will be displayed.
+# Defaults to :sentence, valid options are :sentence, :list and :none
+# Formtastic::SemanticFormBuilder.inline_errors = :sentence
+
+# Set the method to call on label text to transform or format it for human-friendly
+# reading when formtastic is used without object. Defaults to :humanize.
+# Formtastic::SemanticFormBuilder.label_str_method = :humanize
+
+# Set the array of methods to try calling on parent objects in :select and :radio inputs
+# for the text inside each @<option>@ tag or alongside each radio @<input>@. The first method
+# that is found on the object will be used.
+# Defaults to ["to_label", "display_name", "full_name", "name", "title", "username", "login", "value", "to_s"]
+# Formtastic::SemanticFormBuilder.collection_label_methods = [
+# "to_label", "display_name", "full_name", "name", "title", "username", "login", "value", "to_s"]
+
+# Formtastic by default renders inside li tags the input, hints and then
+# errors messages. Sometimes you want the hints to be rendered first than
+# the input, in the following order: hints, input and errors. You can
+# customize it doing just as below:
+# Formtastic::SemanticFormBuilder.inline_order = [:input, :hints, :errors]
+
+# Specifies if labels/hints for input fields automatically be looked up using I18n.
+# Default value: false. Overridden for specific fields by setting value to true,
+# i.e. :label => true, or :hint => true (or opposite depending on initialized value)
+# Formtastic::SemanticFormBuilder.i18n_lookups_by_default = false
+
+# You can add custom inputs or override parts of Formtastic by subclassing SemanticFormBuilder and
+# specifying that class here. Defaults to SemanticFormBuilder.
+# Formtastic::SemanticFormHelper.builder = MyCustomBuilder
View
14 vendor/gems/formtastic-0.9.10/generators/formtastic/templates/formtastic_changes.css
@@ -0,0 +1,14 @@
+/* -------------------------------------------------------------------------------------------------
+
+Load this stylesheet after formtastic.css in your layouts to override the CSS to suit your needs.
+This will allow you to update formtastic.css with new releases without clobbering your own changes.
+
+For example, to make the inline hint paragraphs a little darker in color than the standard #666:
+
+form.formtastic fieldset > ol > li p.inline-hints { color:#333; }
+
+HINT:
+The following style may be *conditionally* included for improved support on older versions of IE(<8)
+form.formtastic fieldset ol li fieldset legend { margin-left: -6px;}
+
+--------------------------------------------------------------------------------------------------*/
View
0 ...heets/formtastic_stylesheets_generator.rb → ...heets/formtastic_stylesheets_generator.rb
File renamed without changes.
View
1,749 vendor/gems/formtastic-0.9.10/lib/formtastic.rb
@@ -0,0 +1,1749 @@
+# coding: utf-8
+require File.join(File.dirname(__FILE__), *%w[formtastic i18n])
+require File.join(File.dirname(__FILE__), *%w[formtastic util])
+
+module Formtastic #:nodoc:
+
+ class SemanticFormBuilder < ActionView::Helpers::FormBuilder
+
+ @@default_text_field_size = 50
+ @@default_text_area_height = 20
+ @@all_fields_required_by_default = true
+ @@include_blank_for_select_by_default = true
+ @@required_string = proc { ::Formtastic::Util.html_safe(%{<abbr title="#{::Formtastic::I18n.t(:required)}">*</abbr>}) }
+ @@optional_string = ''
+ @@inline_errors = :sentence
+ @@label_str_method = :humanize
+ @@collection_label_methods = %w[to_label display_name full_name name title username login value to_s]
+ @@inline_order = [ :input, :hints, :errors ]
+ @@file_methods = [ :file?, :public_filename, :filename ]
+ @@priority_countries = ["Australia", "Canada", "United Kingdom", "United States"]
+ @@i18n_lookups_by_default = false
+ @@default_commit_button_accesskey = nil
+
+ cattr_accessor :default_text_field_size, :default_text_area_height, :all_fields_required_by_default, :include_blank_for_select_by_default,
+ :required_string, :optional_string, :inline_errors, :label_str_method, :collection_label_methods,
+ :inline_order, :file_methods, :priority_countries, :i18n_lookups_by_default, :default_commit_button_accesskey
+
+ RESERVED_COLUMNS = [:created_at, :updated_at, :created_on, :updated_on, :lock_version, :version]
+
+ INLINE_ERROR_TYPES = [:sentence, :list, :first]
+
+ attr_accessor :template
+
+ # Returns a suitable form input for the given +method+, using the database column information
+ # and other factors (like the method name) to figure out what you probably want.
+ #
+ # Options:
+ #
+ # * :as - override the input type (eg force a :string to render as a :password field)
+ # * :label - use something other than the method name as the label text, when false no label is printed
+ # * :required - specify if the column is required (true) or not (false)
+ # * :hint - provide some text to hint or help the user provide the correct information for a field
+ # * :input_html - provide options that will be passed down to the generated input
+ # * :wrapper_html - provide options that will be passed down to the li wrapper
+ #
+ # Input Types:
+ #
+ # Most inputs map directly to one of ActiveRecord's column types by default (eg string_input),
+ # but there are a few special cases and some simplification (:integer, :float and :decimal
+ # columns all map to a single numeric_input, for example).
+ #
+ # * :select (a select menu for associations) - default to association names
+ # * :check_boxes (a set of check_box inputs for associations) - alternative to :select has_many and has_and_belongs_to_many associations
+ # * :radio (a set of radio inputs for associations) - alternative to :select belongs_to associations
+ # * :time_zone (a select menu with time zones)
+ # * :password (a password input) - default for :string column types with 'password' in the method name
+ # * :text (a textarea) - default for :text column types
+ # * :date (a date select) - default for :date column types
+ # * :datetime (a date and time select) - default for :datetime and :timestamp column types
+ # * :time (a time select) - default for :time column types
+ # * :boolean (a checkbox) - default for :boolean column types (you can also have booleans as :select and :radio)
+ # * :string (a text field) - default for :string column types
+ # * :numeric (a text field, like string) - default for :integer, :float and :decimal column types
+ # * :country (a select menu of country names) - requires a country_select plugin to be installed
+ # * :hidden (a hidden field) - creates a hidden field (added for compatibility)
+ #
+ # Example:
+ #
+ # <% semantic_form_for @employee do |form| %>
+ # <% form.inputs do -%>
+ # <%= form.input :secret, :value => "Hello" %>
+ # <%= form.input :name, :label => "Full Name" %>
+ # <%= form.input :manager_id, :as => :radio %>
+ # <%= form.input :hired_at, :as => :date, :label => "Date Hired" %>
+ # <%= form.input :phone, :required => false, :hint => "Eg: +1 555 1234" %>
+ # <% end %>
+ # <% end %>
+ #
+ def input(method, options = {})
+ if options.key?(:selected) || options.key?(:checked) || options.key?(:default)
+ ::ActiveSupport::Deprecation.warn(
+ "The :selected, :checked (and :default) options are deprecated in Formtastic and will be removed from 1.0. " <<
+ "Please set default values in your models (using an after_initialize callback) or in your controller set-up. " <<
+ "See http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html for more information.", caller)
+ end
+
+ options[:required] = method_required?(method) unless options.key?(:required)
+ options[:as] ||= default_input_type(method, options)
+
+ html_class = [ options[:as], (options[:required] ? :required : :optional) ]
+ html_class << 'error' if @object && @object.respond_to?(:errors) && !@object.errors[method.to_sym].blank?
+
+ wrapper_html = options.delete(:wrapper_html) || {}
+ wrapper_html[:id] ||= generate_html_id(method)
+ wrapper_html[:class] = (html_class << wrapper_html[:class]).flatten.compact.join(' ')
+
+ if options[:input_html] && options[:input_html][:id]
+ options[:label_html] ||= {}
+ options[:label_html][:for] ||= options[:input_html][:id]
+ end
+
+ input_parts = @@inline_order.dup
+ input_parts = input_parts - [:errors, :hints] if options[:as] == :hidden
+
+ list_item_content = input_parts.map do |type|
+ send(:"inline_#{type}_for", method, options)
+ end.compact.join("\n")
+
+ return template.content_tag(:li, Formtastic::Util.html_safe(list_item_content), wrapper_html)
+ end
+
+ # Creates an input fieldset and ol tag wrapping for use around a set of inputs. It can be
+ # called either with a block (in which you can do the usual Rails form stuff, HTML, ERB, etc),
+ # or with a list of fields. These two examples are functionally equivalent:
+ #
+ # # With a block:
+ # <% semantic_form_for @post do |form| %>
+ # <% form.inputs do %>
+ # <%= form.input :title %>
+ # <%= form.input :body %>
+ # <% end %>
+ # <% end %>
+ #
+ # # With a list of fields:
+ # <% semantic_form_for @post do |form| %>
+ # <%= form.inputs :title, :body %>
+ # <% end %>
+ #
+ # # Output:
+ # <form ...>
+ # <fieldset class="inputs">
+ # <ol>
+ # <li class="string">...</li>
+ # <li class="text">...</li>
+ # </ol>
+ # </fieldset>
+ # </form>
+ #
+ # === Quick Forms
+ #
+ # When called without a block or a field list, an input is rendered for each column in the
+ # model's database table, just like Rails' scaffolding. You'll obviously want more control
+ # than this in a production application, but it's a great way to get started, then come back
+ # later to customise the form with a field list or a block of inputs. Example:
+ #
+ # <% semantic_form_for @post do |form| %>
+ # <%= form.inputs %>
+ # <% end %>
+ #
+ # With a few arguments:
+ # <% semantic_form_for @post do |form| %>
+ # <%= form.inputs "Post details", :title, :body %>
+ # <% end %>
+ #
+ # === Options
+ #
+ # All options (with the exception of :name/:title) are passed down to the fieldset as HTML
+ # attributes (id, class, style, etc). If provided, the :name/:title option is passed into a
+ # legend tag inside the fieldset.
+ #
+ # # With a block:
+ # <% semantic_form_for @post do |form| %>
+ # <% form.inputs :name => "Create a new post", :style => "border:1px;" do %>
+ # ...
+ # <% end %>
+ # <% end %>
+ #
+ # # With a list (the options must come after the field list):
+ # <% semantic_form_for @post do |form| %>
+ # <%= form.inputs :title, :body, :name => "Create a new post", :style => "border:1px;" %>
+ # <% end %>
+ #
+ # # ...or the equivalent:
+ # <% semantic_form_for @post do |form| %>
+ # <%= form.inputs "Create a new post", :title, :body, :style => "border:1px;" %>
+ # <% end %>
+ #
+ # === It's basically a fieldset!
+ #
+ # Instead of hard-coding fieldsets & legends into your form to logically group related fields,
+ # use inputs:
+ #
+ # <% semantic_form_for @post do |f| %>
+ # <% f.inputs do %>
+ # <%= f.input :title %>
+ # <%= f.input :body %>
+ # <% end %>
+ # <% f.inputs :name => "Advanced", :id => "advanced" do %>
+ # <%= f.input :created_at %>
+ # <%= f.input :user_id, :label => "Author" %>
+ # <% end %>
+ # <% f.inputs "Extra" do %>
+ # <%= f.input :update_at %>
+ # <% end %>
+ # <% end %>
+ #
+ # # Output:
+ # <form ...>
+ # <fieldset class="inputs">
+ # <ol>
+ # <li class="string">...</li>
+ # <li class="text">...</li>
+ # </ol>
+ # </fieldset>
+ # <fieldset class="inputs" id="advanced">
+ # <legend><span>Advanced</span></legend>
+ # <ol>
+ # <li class="datetime">...</li>
+ # <li class="select">...</li>
+ # </ol>
+ # </fieldset>
+ # <fieldset class="inputs">
+ # <legend><span>Extra</span></legend>
+ # <ol>
+ # <li class="datetime">...</li>
+ # </ol>
+ # </fieldset>
+ # </form>
+ #
+ # === Nested attributes
+ #
+ # As in Rails, you can use semantic_fields_for to nest attributes:
+ #
+ # <% semantic_form_for @post do |form| %>
+ # <%= form.inputs :title, :body %>
+ #
+ # <% form.semantic_fields_for :author, @bob do |author_form| %>
+ # <% author_form.inputs do %>
+ # <%= author_form.input :first_name, :required => false %>
+ # <%= author_form.input :last_name %>
+ # <% end %>
+ # <% end %>
+ # <% end %>
+ #
+ # But this does not look formtastic! This is equivalent:
+ #
+ # <% semantic_form_for @post do |form| %>
+ # <%= form.inputs :title, :body %>
+ # <% form.inputs :for => [ :author, @bob ] do |author_form| %>
+ # <%= author_form.input :first_name, :required => false %>
+ # <%= author_form.input :last_name %>
+ # <% end %>
+ # <% end %>
+ #
+ # And if you don't need to give options to your input call, you could do it
+ # in just one line:
+ #
+ # <% semantic_form_for @post do |form| %>
+ # <%= form.inputs :title, :body %>
+ # <%= form.inputs :first_name, :last_name, :for => @bob %>
+ # <% end %>
+ #
+ # Just remember that calling inputs generates a new fieldset to wrap your
+ # inputs. If you have two separate models, but, semantically, on the page
+ # they are part of the same fieldset, you should use semantic_fields_for
+ # instead (just as you would do with Rails' form builder).
+ #
+ def inputs(*args, &block)
+ title = field_set_title_from_args(*args)
+ html_options = args.extract_options!
+ html_options[:class] ||= "inputs"
+ html_options[:name] = title
+
+ if html_options[:for] # Nested form
+ inputs_for_nested_attributes(*(args << html_options), &block)
+ elsif block_given?
+ field_set_and_list_wrapping(*(args << html_options), &block)
+ else
+ if @object && args.empty?
+ args = self.association_columns(:belongs_to)
+ args += self.content_columns
+ args -= RESERVED_COLUMNS
+ args.compact!
+ end
+ legend = args.shift if args.first.is_a?(::String)
+ contents = args.collect { |method| input(method.to_sym) }
+ args.unshift(legend) if legend.present?
+
+ field_set_and_list_wrapping(*((args << html_options) << contents))
+ end
+ end
+ alias :input_field_set :inputs
+
+ # Creates a fieldset and ol tag wrapping for form buttons / actions as list items.
+ # See inputs documentation for a full example. The fieldset's default class attriute
+ # is set to "buttons".
+ #
+ # See inputs for html attributes and special options.
+ def buttons(*args, &block)
+ html_options = args.extract_options!
+ html_options[:class] ||= "buttons"
+
+ if block_given?
+ field_set_and_list_wrapping(html_options, &block)
+ else
+ args = [:commit] if args.empty?
+ contents = args.map { |button_name| send(:"#{button_name}_button") }
+ field_set_and_list_wrapping(html_options, contents)
+ end
+ end
+ alias :button_field_set :buttons
+
+ # Creates a submit input tag with the value "Save [model name]" (for existing records) or
+ # "Create [model name]" (for new records) by default:
+ #
+ # <%= form.commit_button %> => <input name="commit" type="submit" value="Save Post" />
+ #
+ # The value of the button text can be overridden:
+ #
+ # <%= form.commit_button "Go" %> => <input name="commit" type="submit" value="Go" class="{create|update|submit}" />
+ # <%= form.commit_button :label => "Go" %> => <input name="commit" type="submit" value="Go" class="{create|update|submit}" />
+ #
+ # And you can pass html atributes down to the input, with or without the button text:
+ #
+ # <%= form.commit_button "Go" %> => <input name="commit" type="submit" value="Go" class="{create|update|submit}" />
+ # <%= form.commit_button :class => "pretty" %> => <input name="commit" type="submit" value="Save Post" class="pretty {create|update|submit}" />
+ #
+ def commit_button(*args)
+ options = args.extract_options!
+ text = options.delete(:label) || args.shift
+
+ if @object && @object.respond_to?(:new_record?)
+ key = @object.new_record? ? :create : :update
+
+ # Deal with some complications with ActiveRecord::Base.human_name and two name models (eg UserPost)
+ # ActiveRecord::Base.human_name falls back to ActiveRecord::Base.name.humanize ("Userpost")
+ # if there's no i18n, which is pretty crappy. In this circumstance we want to detect this
+ # fall back (human_name == name.humanize) and do our own thing name.underscore.humanize ("User Post")
+ object_human_name = @object.class.human_name # default is UserPost => "Userpost", but i18n may do better ("User post")
+ crappy_human_name = @object.class.name.humanize # UserPost => "Userpost"
+ decent_human_name = @object.class.name.underscore.humanize # UserPost => "User post"
+ object_name = (object_human_name == crappy_human_name) ? decent_human_name : object_human_name
+ else
+ key = :submit
+ object_name = @object_name.to_s.send(@@label_str_method)
+ end
+
+ text = (self.localized_string(key, text, :action, :model => object_name) ||
+ ::Formtastic::I18n.t(key, :model => object_name)) unless text.is_a?(::String)
+
+ button_html = options.delete(:button_html) || {}
+ button_html.merge!(:class => [button_html[:class], key].compact.join(' '))
+ element_class = ['commit', options.delete(:class)].compact.join(' ') # TODO: Add class reflecting on form action.
+ accesskey = (options.delete(:accesskey) || @@default_commit_button_accesskey) unless button_html.has_key?(:accesskey)
+ button_html = button_html.merge(:accesskey => accesskey) if accesskey
+ template.content_tag(:li, Formtastic::Util.html_safe(self.submit(text, button_html)), :class => element_class)
+ end
+
+ # A thin wrapper around #fields_for to set :builder => Formtastic::SemanticFormBuilder
+ # for nesting forms:
+ #
+ # # Example:
+ # <% semantic_form_for @post do |post| %>
+ # <% post.semantic_fields_for :author do |author| %>
+ # <% author.inputs :name %>
+ # <% end %>
+ # <% end %>
+ #
+ # # Output:
+ # <form ...>
+ # <fieldset class="inputs">
+ # <ol>
+ # <li class="string"><input type='text' name='post[author][name]' id='post_author_name' /></li>
+ # </ol>
+ # </fieldset>
+ # </form>
+ #
+ def semantic_fields_for(record_or_name_or_array, *args, &block)
+ opts = args.extract_options!
+ opts[:builder] ||= Formtastic::SemanticFormHelper.builder
+ args.push(opts)
+ fields_for(record_or_name_or_array, *args, &block)
+ end
+
+ # Generates the label for the input. It also accepts the same arguments as
+ # Rails label method. It has three options that are not supported by Rails
+ # label method:
+ #
+ # * :required - Appends an abbr tag if :required is true
+ # * :label - An alternative form to give the label content. Whenever label
+ # is false, a blank string is returned.
+ # * :input_name - Gives the input to match for. This is needed when you want to
+ # to call f.label :authors but it should match :author_ids.
+ #
+ # == Examples
+ #
+ # f.label :title # like in rails, except that it searches the label on I18n API too
+ #
+ # f.label :title, "Your post title"
+ # f.label :title, :label => "Your post title" # Added for formtastic API
+ #
+ # f.label :title, :required => true # Returns <label>Title<abbr title="required">*</abbr></label>
+ #
+ def label(method, options_or_text=nil, options=nil)
+ if options_or_text.is_a?(Hash)
+ return "" if options_or_text[:label] == false
+ options = options_or_text
+ text = options.delete(:label)
+ else
+ text = options_or_text
+ options ||= {}
+ end
+
+ text = localized_string(method, text, :label) || humanized_attribute_name(method)
+ text += required_or_optional_string(options.delete(:required))
+ text = Formtastic::Util.html_safe(text)
+
+ # special case for boolean (checkbox) labels, which have a nested input
+ if options.key?(:label_prefix_for_nested_input)
+ text = options.delete(:label_prefix_for_nested_input) + text
+ end
+
+ input_name = options.delete(:input_name) || method
+ super(input_name, text, options)
+ end
+
+ # Generates error messages for the given method. Errors can be shown as list,
+ # as sentence or just the first error can be displayed. If :none is set, no error is shown.
+ #
+ # This method is also aliased as errors_on, so you can call on your custom
+ # inputs as well:
+ #
+ # semantic_form_for :post do |f|
+ # f.text_field(:body)
+ # f.errors_on(:body)
+ # end
+ #
+ def inline_errors_for(method, options = nil) #:nodoc:
+ if render_inline_errors?
+ errors = [@object.errors[method.to_sym]]
+ errors << [@object.errors[association_primary_key(method)]] if association_macro_for_method(method) == :belongs_to
+ errors = errors.flatten.compact.uniq
+ send(:"error_#{@@inline_errors}", [*errors]) if errors.any?
+ else
+ nil
+ end
+ end
+ alias :errors_on :inline_errors_for
+
+ # Generates error messages for given method names and for base.
+ # You can pass a hash with html options that will be added to ul tag
+ #
+ # == Examples
+ #
+ # f.semantic_errors # This will show only errors on base
+ # f.semantic_errors :state # This will show errors on base and state
+ # f.semantic_errors :state, :class => "awesome" # errors will be rendered in ul.awesome
+ #
+ def semantic_errors(*args)
+ html_options = args.extract_options!
+ full_errors = args.inject([]) do |array, method|
+ attribute = localized_string(method, method.to_sym, :label) || humanized_attribute_name(method)
+ errors = Array(@object.errors[method.to_sym]).to_sentence
+ errors.present? ? array << [attribute, errors].join(" ") : array ||= []
+ end
+ full_errors << @object.errors.on_base
+ full_errors.flatten!
+ full_errors.compact!
+ return nil if full_errors.blank?
+ html_options[:class] ||= "errors"
+ template.content_tag(:ul, html_options) do
+ Formtastic::Util.html_safe(full_errors.map { |error| template.content_tag(:li, Formtastic::Util.html_safe(error)) }.join)
+ end
+ end
+
+ protected
+
+ def render_inline_errors?
+ @object && @object.respond_to?(:errors) && INLINE_ERROR_TYPES.include?(@@inline_errors)
+ end
+
+ # Collects content columns (non-relation columns) for the current form object class.
+ #
+ def content_columns #:nodoc:
+ self.model_name.constantize.content_columns.collect { |c| c.name.to_sym }.compact rescue []
+ end
+
+ # Collects association columns (relation columns) for the current form object class.
+ #
+ def association_columns(*by_associations) #:nodoc:
+ if @object.present?
+ @object.class.reflections.collect do |name, _|
+ if by_associations.present?
+ name if by_associations.include?(_.macro)
+ else
+ name
+ end
+ end.compact
+ else
+ []
+ end
+ end
+
+ # Returns nil, or a symbol like :belongs_to or :has_many
+ def association_macro_for_method(method) #:nodoc:
+ reflection = self.reflection_for(method)
+ reflection.macro if reflection
+ end
+
+ def association_primary_key(method)
+ reflection = self.reflection_for(method)
+ reflection.options[:foreign_key] if reflection && !reflection.options[:foreign_key].blank?
+ :"#{method}_id"
+ end
+
+ # Prepare options to be sent to label
+ #
+ def options_for_label(options) #:nodoc:
+ options.slice(:label, :required).merge!(options.fetch(:label_html, {}))
+ end
+
+ # Deals with :for option when it's supplied to inputs methods. Additional
+ # options to be passed down to :for should be supplied using :for_options
+ # key.
+ #
+ # It should raise an error if a block with arity zero is given.
+ #
+ def inputs_for_nested_attributes(*args, &block) #:nodoc:
+ options = args.extract_options!
+ args << options.merge!(:parent => { :builder => self, :for => options[:for] })
+
+ fields_for_block = if block_given?
+ raise ArgumentError, 'You gave :for option with a block to inputs method, ' <<
+ 'but the block does not accept any argument.' if block.arity <= 0
+
+ proc { |f| return f.inputs(*args){ block.call(f) } }
+ else
+ proc { |f| return f.inputs(*args) }
+ end
+
+ fields_for_args = [options.delete(:for), options.delete(:for_options) || {}].flatten
+ semantic_fields_for(*fields_for_args, &fields_for_block)
+ end
+
+ # Remove any Formtastic-specific options before passing the down options.
+ #
+ def strip_formtastic_options(options) #:nodoc:
+ options.except(:value_method, :label_method, :collection, :required, :label,
+ :as, :hint, :input_html, :label_html, :value_as_class)
+ end
+
+ # Determins if the attribute (eg :title) should be considered required or not.
+ #
+ # * if the :required option was provided in the options hash, the true/false value will be
+ # returned immediately, allowing the view to override any guesswork that follows:
+ #
+ # * if the :required option isn't provided in the options hash, and the ValidationReflection
+ # plugin is installed (http://github.com/redinger/validation_reflection), true is returned
+ # if the validates_presence_of macro has been used in the class for this attribute, or false
+ # otherwise.
+ #
+ # * if the :required option isn't provided, and the plugin isn't available, the value of the
+ # configuration option @@all_fields_required_by_default is used.
+ #
+ def method_required?(attribute) #:nodoc:
+ if @object && @object.class.respond_to?(:reflect_on_validations_for)
+ attribute_sym = attribute.to_s.sub(/_id$/, '').to_sym
+
+ @object.class.reflect_on_validations_for(attribute_sym).any? do |validation|
+ validation.macro == :validates_presence_of &&
+ validation.name == attribute_sym &&
+ (validation.options.present? ? options_require_validation?(validation.options) : true)
+ end
+ else
+ @@all_fields_required_by_default
+ end
+ end
+
+ # Determines whether the given options evaluate to true
+ def options_require_validation?(options) #nodoc
+ if_condition = !options[:if].nil?
+ condition = if_condition ? options[:if] : options[:unless]
+
+ condition = if condition.respond_to?(:call)
+ condition.call(@object)
+ elsif condition.is_a?(::Symbol) && @object.respond_to?(condition)
+ @object.send(condition)
+ else
+ condition
+ end
+
+ if_condition ? !!condition : !condition
+ end
+
+ def basic_input_helper(form_helper_method, type, method, options) #:nodoc:
+ html_options = options.delete(:input_html) || {}
+ html_options = default_string_options(method, type).merge(html_options) if [:numeric, :string, :password, :text].include?(type)
+
+ self.label(method, options_for_label(options)) <<
+ self.send(form_helper_method, method, html_options)
+ end
+
+ # Outputs a label and standard Rails text field inside the wrapper.
+ def string_input(method, options)
+ basic_input_helper(:text_field, :string, method, options)
+ end
+
+ # Outputs a label and standard Rails password field inside the wrapper.
+ def password_input(method, options)
+ basic_input_helper(:password_field, :password, method, options)
+ end
+
+ # Outputs a label and standard Rails text field inside the wrapper.
+ def numeric_input(method, options)
+ basic_input_helper(:text_field, :numeric, method, options)
+ end
+
+ # Ouputs a label and standard Rails text area inside the wrapper.
+ def text_input(method, options)
+ basic_input_helper(:text_area, :text, method, options)
+ end
+
+ # Outputs a label and a standard Rails file field inside the wrapper.
+ def file_input(method, options)
+ basic_input_helper(:file_field, :file, method, options)
+ end
+
+ # Outputs a hidden field inside the wrapper, which should be hidden with CSS.
+ # Additionals options can be given and will be sent straight to hidden input
+ # element.
+ #
+ def hidden_input(method, options)
+ options ||= {}
+ if options[:input_html].present?
+ options[:value] = options[:input_html][:value] if options[:input_html][:value].present?
+ end
+ self.hidden_field(method, strip_formtastic_options(options))
+ end
+
+ # Outputs a label and a select box containing options from the parent
+ # (belongs_to, has_many, has_and_belongs_to_many) association. If an association
+ # is has_many or has_and_belongs_to_many the select box will be set as multi-select
+ # and size = 5
+ #
+ # Example (belongs_to):
+ #
+ # f.input :author
+ #
+ # <label for="book_author_id">Author</label>
+ # <select id="book_author_id" name="book[author_id]">
+ # <option value=""></option>
+ # <option value="1">Justin French</option>
+ # <option value="2">Jane Doe</option>
+ # </select>
+ #
+ # Example (has_many):
+ #
+ # f.input :chapters
+ #
+ # <label for="book_chapter_ids">Chapters</label>
+ # <select id="book_chapter_ids" name="book[chapter_ids]">
+ # <option value=""></option>
+ # <option value="1">Chapter 1</option>
+ # <option value="2">Chapter 2</option>
+ # </select>
+ #
+ # Example (has_and_belongs_to_many):
+ #
+ # f.input :authors
+ #
+ # <label for="book_author_ids">Authors</label>
+ # <select id="book_author_ids" name="book[author_ids]">
+ # <option value=""></option>
+ # <option value="1">Justin French</option>
+ # <option value="2">Jane Doe</option>
+ # </select>
+ #
+ #
+ # You can customize the options available in the select by passing in a collection (an Array or
+ # Hash) through the :collection option. If not provided, the choices are found by inferring the
+ # parent's class name from the method name and simply calling find(:all) on it
+ # (VehicleOwner.find(:all) in the example above).
+ #
+ # Examples:
+ #
+ # f.input :author, :collection => @authors
+ # f.input :author, :collection => Author.find(:all)
+ # f.input :author, :collection => [@justin, @kate]
+ # f.input :author, :collection => {@justin.name => @justin.id, @kate.name => @kate.id}
+ # f.input :author, :collection => ["Justin", "Kate", "Amelia", "Gus", "Meg"]
+ #
+ # The :label_method option allows you to customize the text label inside each option tag two ways:
+ #
+ # * by naming the correct method to call on each object in the collection as a symbol (:name, :login, etc)
+ # * by passing a Proc that will be called on each object in the collection, allowing you to use helpers or multiple model attributes together
+ #
+ # Examples:
+ #
+ # f.input :author, :label_method => :full_name
+ # f.input :author, :label_method => :login
+ # f.input :author, :label_method => :full_name_with_post_count
+ # f.input :author, :label_method => Proc.new { |a| "#{a.name} (#{pluralize("post", a.posts.count)})" }
+ #
+ # The :value_method option provides the same customization of the value attribute of each option tag.
+ #
+ # Examples:
+ #
+ # f.input :author, :value_method => :full_name
+ # f.input :author, :value_method => :login
+ # f.input :author, :value_method => Proc.new { |a| "author_#{a.login}" }
+ #
+ # You can pre-select a specific option value by passing in the :selected option.
+ #
+ # Examples:
+ #
+ # f.input :author, :selected => current_user.id
+ # f.input :author, :value_method => :login, :selected => current_user.login
+ # f.input :authors, :value_method => :login, :selected => Author.most_popular.collect(&:id)
+ # f.input :authors, :value_method => :login, :selected => nil # override any defaults: select none
+ #
+ # You can pass html_options to the select tag using :input_html => {}
+ #
+ # Examples:
+ #
+ # f.input :authors, :input_html => {:size => 20, :multiple => true}
+ #
+ # By default, all select inputs will have a blank option at the top of the list. You can add
+ # a prompt with the :prompt option, or disable the blank option with :include_blank => false.
+ #
+ #
+ # You can group the options in optgroup elements by passing the :group_by option
+ # (Note: only tested for belongs_to relations)
+ #
+ # Examples:
+ #
+ # f.input :author, :group_by => :continent
+ #
+ # All the other options should work as expected. If you want to call a custom method on the
+ # group item. You can include the option:group_label_method
+ # Examples:
+ #
+ # f.input :author, :group_by => :continents, :group_label_method => :something_different
+ #
+ def select_input(method, options)
+ html_options = options.delete(:input_html) || {}
+ options = set_include_blank(options)
+ html_options[:multiple] = html_options[:multiple] || options.delete(:multiple)
+ html_options.delete(:multiple) if html_options[:multiple].nil?
+
+ reflection = self.reflection_for(method)
+ if reflection && [ :has_many, :has_and_belongs_to_many ].include?(reflection.macro)
+ options[:include_blank] = false
+ html_options[:multiple] = true if html_options[:multiple].nil?
+ html_options[:size] ||= 5
+ end
+ options[:selected] = options[:selected].first if options[:selected].present? && html_options[:multiple] == false
+ input_name = generate_association_input_name(method)
+
+ select_html = if options[:group_by]
+ # The grouped_options_select is a bit counter intuitive and not optimised (mostly due to ActiveRecord).
+ # The formtastic user however shouldn't notice this too much.
+ raw_collection = find_raw_collection_for_column(method, options.reverse_merge(:find_options => { :include => options[:group_by] }))
+ label, value = detect_label_and_value_method!(raw_collection)
+ group_collection = raw_collection.map { |option| option.send(options[:group_by]) }.uniq
+ group_label_method = options[:group_label_method] || detect_label_method(group_collection)
+ group_collection = group_collection.sort_by { |group_item| group_item.send(group_label_method) }
+ group_association = options[:group_association] || detect_group_association(method, options[:group_by])
+
+ # Here comes the monster with 8 arguments
+ self.grouped_collection_select(input_name, group_collection,
+ group_association, group_label_method,
+ value, label,
+ strip_formtastic_options(options), html_options)
+ else
+ collection = find_collection_for_column(method, options)
+
+ self.select(input_name, collection, strip_formtastic_options(options), html_options)
+ end
+
+ self.label(method, options_for_label(options).merge(:input_name => input_name)) << select_html
+ end
+ alias :boolean_select_input :select_input
+
+ # Outputs a timezone select input as Rails' time_zone_select helper. You
+ # can give priority zones as option.
+ #
+ # Examples:
+ #
+ # f.input :time_zone, :as => :time_zone, :priority_zones => /Australia/
+ #
+ # You can pre-select a specific option value by passing in the :selected option.
+ # Note: Right now only works if the form object attribute value is not set (nil),
+ # because of how the core helper is implemented.