Showing preview only (471K chars total). Download the full file or copy to clipboard to get everything.
Repository: heartcombo/simple_form
Branch: main
Commit: 55bec8024c3b
Files: 122
Total size: 438.9 KB
Directory structure:
gitextract_7k1r7n03/
├── .github/
│ ├── code-scanning.yml
│ └── workflows/
│ └── test.yml
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Gemfile
├── ISSUE_TEMPLATE.md
├── MIT-LICENSE
├── README.md
├── Rakefile
├── bin/
│ └── test
├── gemfiles/
│ ├── Gemfile-rails-7-0
│ ├── Gemfile-rails-7-1
│ ├── Gemfile-rails-7-2
│ ├── Gemfile-rails-8-0
│ └── Gemfile-rails-main
├── lib/
│ ├── generators/
│ │ └── simple_form/
│ │ ├── USAGE
│ │ ├── install_generator.rb
│ │ └── templates/
│ │ ├── README
│ │ ├── _form.html.erb
│ │ ├── _form.html.haml
│ │ ├── _form.html.slim
│ │ └── config/
│ │ ├── initializers/
│ │ │ ├── simple_form.rb
│ │ │ ├── simple_form_bootstrap.rb
│ │ │ └── simple_form_foundation.rb
│ │ └── locales/
│ │ └── simple_form.en.yml
│ ├── simple_form/
│ │ ├── action_view_extensions/
│ │ │ ├── builder.rb
│ │ │ └── form_helper.rb
│ │ ├── components/
│ │ │ ├── errors.rb
│ │ │ ├── hints.rb
│ │ │ ├── html5.rb
│ │ │ ├── label_input.rb
│ │ │ ├── labels.rb
│ │ │ ├── maxlength.rb
│ │ │ ├── min_max.rb
│ │ │ ├── minlength.rb
│ │ │ ├── pattern.rb
│ │ │ ├── placeholders.rb
│ │ │ └── readonly.rb
│ │ ├── components.rb
│ │ ├── error_notification.rb
│ │ ├── form_builder.rb
│ │ ├── helpers/
│ │ │ ├── autofocus.rb
│ │ │ ├── disabled.rb
│ │ │ ├── readonly.rb
│ │ │ ├── required.rb
│ │ │ └── validators.rb
│ │ ├── helpers.rb
│ │ ├── inputs/
│ │ │ ├── base.rb
│ │ │ ├── block_input.rb
│ │ │ ├── boolean_input.rb
│ │ │ ├── collection_check_boxes_input.rb
│ │ │ ├── collection_input.rb
│ │ │ ├── collection_radio_buttons_input.rb
│ │ │ ├── collection_select_input.rb
│ │ │ ├── color_input.rb
│ │ │ ├── date_time_input.rb
│ │ │ ├── file_input.rb
│ │ │ ├── grouped_collection_select_input.rb
│ │ │ ├── hidden_input.rb
│ │ │ ├── numeric_input.rb
│ │ │ ├── password_input.rb
│ │ │ ├── priority_input.rb
│ │ │ ├── range_input.rb
│ │ │ ├── rich_text_area_input.rb
│ │ │ ├── string_input.rb
│ │ │ ├── text_input.rb
│ │ │ └── weekday_input.rb
│ │ ├── inputs.rb
│ │ ├── map_type.rb
│ │ ├── railtie.rb
│ │ ├── tags.rb
│ │ ├── version.rb
│ │ ├── wrappers/
│ │ │ ├── builder.rb
│ │ │ ├── leaf.rb
│ │ │ ├── many.rb
│ │ │ ├── root.rb
│ │ │ └── single.rb
│ │ └── wrappers.rb
│ └── simple_form.rb
├── simple_form.gemspec
└── test/
├── action_view_extensions/
│ ├── builder_test.rb
│ └── form_helper_test.rb
├── components/
│ ├── custom_components_test.rb
│ └── label_test.rb
├── form_builder/
│ ├── association_test.rb
│ ├── button_test.rb
│ ├── error_notification_test.rb
│ ├── error_test.rb
│ ├── general_test.rb
│ ├── hint_test.rb
│ ├── input_field_test.rb
│ ├── label_test.rb
│ └── wrapper_test.rb
├── generators/
│ └── simple_form_generator_test.rb
├── inputs/
│ ├── boolean_input_test.rb
│ ├── collection_check_boxes_input_test.rb
│ ├── collection_radio_buttons_input_test.rb
│ ├── collection_select_input_test.rb
│ ├── color_input_test.rb
│ ├── country_input_test.rb
│ ├── datetime_input_test.rb
│ ├── disabled_test.rb
│ ├── discovery_test.rb
│ ├── file_input_test.rb
│ ├── general_test.rb
│ ├── grouped_collection_select_input_test.rb
│ ├── hidden_input_test.rb
│ ├── numeric_input_test.rb
│ ├── readonly_test.rb
│ ├── required_test.rb
│ ├── rich_text_area_input_test.rb
│ ├── string_input_test.rb
│ ├── text_input_test.rb
│ ├── time_zone_input_test.rb
│ └── weekday_input_test.rb
├── simple_form_test.rb
├── support/
│ ├── discovery_inputs.rb
│ ├── misc_helpers.rb
│ ├── mock_controller.rb
│ └── models.rb
└── test_helper.rb
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/code-scanning.yml
================================================
paths-ignore:
- test/**
================================================
FILE: .github/workflows/test.yml
================================================
name: Test
permissions:
contents: read
on:
push:
branches:
- main
pull_request:
workflow_dispatch:
jobs:
test:
strategy:
fail-fast: false
matrix:
gemfile:
- Gemfile
- gemfiles/Gemfile-rails-main
- gemfiles/Gemfile-rails-8-0
- gemfiles/Gemfile-rails-7-2
- gemfiles/Gemfile-rails-7-1
- gemfiles/Gemfile-rails-7-0
ruby:
- '4.0'
- '3.4'
- '3.3'
- '3.2'
- '3.1'
- '3.0'
- '2.7'
exclude:
- gemfile: Gemfile
ruby: '3.1'
- gemfile: Gemfile
ruby: '3.0'
- gemfile: Gemfile
ruby: '2.7'
- gemfile: gemfiles/Gemfile-rails-main
ruby: '3.2'
- gemfile: gemfiles/Gemfile-rails-main
ruby: '3.1'
- gemfile: gemfiles/Gemfile-rails-main
ruby: '3.0'
- gemfile: gemfiles/Gemfile-rails-main
ruby: '2.7'
- gemfile: gemfiles/Gemfile-rails-8-0
ruby: '3.1'
- gemfile: gemfiles/Gemfile-rails-8-0
ruby: '3.0'
- gemfile: gemfiles/Gemfile-rails-8-0
ruby: '2.7'
- gemfile: gemfiles/Gemfile-rails-7-2
ruby: '3.0'
- gemfile: gemfiles/Gemfile-rails-7-2
ruby: '2.7'
runs-on: ubuntu-latest
env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
BUNDLE_GEMFILE: ${{ matrix.gemfile }}
steps:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: ${{ matrix.ruby }}
bundler-cache: true # runs bundle install and caches installed gems automatically
- run: bundle exec rake
================================================
FILE: .gitignore
================================================
.bundle/
pkg/
rdoc/
gemfiles/*.lock
/.idea/
================================================
FILE: CHANGELOG.md
================================================
## 5.4.1
* Ruby 4.0 support (no changes required)
* Support procs on validators for minlength/maxlength, and improve validators logic across the board to match Rails [#1859](https://github.com/heartcombo/simple_form/pull/1859)
## 5.4.0
* Add support for Ruby 3.4 and Rails 7.2/8.0/8.1. (no changes required)
* Drop support for Rails < 7 and Ruby < 2.7.
* Add `weekday` input. [#1846](https://github.com/heartcombo/simple_form/pull/1846)
* Remove redundant `aria-required` attribute for required fields. [#1823](https://github.com/heartcombo/simple_form/pull/1823)
* Integrate `:rich_text_area` with placeholders [#1842](https://github.com/heartcombo/simple_form/pull/1842)
* Fix encrypted attributes improperly casted (later fixed in Rails) [#1836](https://github.com/heartcombo/simple_form/pull/1836)
* Pass `base` object to `human_attribute_name` in labels [#1812](https://github.com/heartcombo/simple_form/pull/1812)
## 5.3.1
* Revert "Speed up input mapping lookup by avoiding rescuing exceptions" from v5.3.0, it caused a regression on dev/test environments with custom inputs.
* Try a slightly different approach to input lookups, without relying on regexp, to see if that helps with performance as originally intended.
* Add support to Ruby 3.3. (no changes required.)
## 5.3.0
* Add support for Rails 7.1. (no meaningful changes required.)
* Add `SimpleForm.deprecator` to integrate with new application deprecators in Rails 7.1.
* Remove test files from the gem package. [@orien](https://github.com/orien)
* Speed up input mapping lookup by avoiding rescuing exceptions. [@meanphil](https://github.com/meanphil) [@kriom](https://github.com/kriom) [@egeek](https://github.com/egeek)
## 5.2.0
* Add support for Rails 7.0 and Ruby 3.1/3.2 (no changes required)
* Fix escaping issue on boolean input with `include_hidden: false` and custom wrapper.
* Update Bootstrap install generator version 5. [@mhw](https://github.com/mhw)
* Accept proc as `group_method` for grouped collection select
* Honor `include_hidden` option on inline boolean inputs [@yboulkaid](https://github.com/yboulkaid)
* Fix deprecation error when using country_select input.
## 5.1.0
* Remove `I18nCache` module entirely. It was added complexity for very little gain in some translations, and caused extra trouble upgrading to Ruby 3. If you need that level of caching consider looking into I18n caching as a whole.
* Add support for Ruby 3.0, drop support for Ruby < 2.5.
* Add support for Rails 6.1, drop support for Rails < 5.2.
* Move CI to GitHub Actions.
## 5.0.3
### Bug fix
* Fix for ActiveStorage::Attached::Many. [@enriquez](https://github.com/enriquez)
## 5.0.2
### Enhancements
* Remove instruction to use form-inline class. [@goalaleo](https://github.com/goalaleo)
* Added RichTextAreaInput for ActionText. [itsterry](https://github.com/itsterry)
* Skip valid_class check if no class defined. [TALlama](https://github.com/TALlama)
### Bug fix
* Fix 'aria-required' field generated by prompt. [@CarlosAlbertoSantos](https://github.com/CarlosAlbertoSantos)
## 5.0.1
### Bug fix
* Replace `_url` with `remote_url` when trying to guess file inputs [@tegon](https://github.com/tegon). This has the side-effect of changing carrierwave's support from `0.2.1` to `0.2.2`.
## 5.0.0
### Enhancements
* Set multiple attribute for grouped selects also. [@ollym](https://github.com/ollym)
* Removes or renames label classes. [Abduvakilov](https://github.com/Abduvakilov)
* Support to label custom classes for inline collections. [@feliperenan](https://github.com/feliperenan)
* Update bootstrap generator template to match v4.3.x. [@m5o](https://github.com/m5o)
* Allow "required" attribute in generated select elements of PriorityInput. [@mcountis](https://github.com/mcountis)
### Bug fix
* Do not call `#send` in form object to check whether the attribute is a file input. [@tegon](https://github.com/tegon)
## Deprecations
* The config `SimpleForm.file_methods` is deprecated and it has no effect. Simple Form now supports automatically discover of file inputs for the following Gems: activestorage, carrierwave, paperclip, refile and shrine. If you are using a custom method that is not from one of the supported Gems, please change your forms to pass the input type explicitly:
```erb
<%= form.input :avatar, as: :file %>
```
See http://blog.plataformatec.com.br/2019/09/incorrect-access-control-in-simple-form-cve-2019-16676 for more information.
## 4.1.0
### Enhancements
* Guess input type more carefully. [@sringling](https://github.com/sringling)
* Allow custom error on forms without model. [@victorperez](https://github.com/victorperez)
* Do not support Ruby < 2.3 anymore. [@gssbzn](https://github.com/gssbzn)
* Add color input type. [@gssbzn](https://github.com/gssbzn)
### Bug fix
* Improve disabled option to input_field. [@betelgeuse](https://github.com/betelgeuse)
* Memoize `input_html_classes` in `SimpleForm::Inputs::Base`. [@RigoTheDev](https://github.com/RigoTheDev)
* Fix column type citext HTML5 input type bug. [@brucew](https://github.com/brucew)
* Use form attribute in the nested boolean hidden field when it is given. [@feliperenan](https://github.com/feliperenan)
## 4.0.1
### Bug fix
* Do not support Rails 4 anymore. [@rafaelfranca](https://github.com/rafaelfranca)
* Add missing comma. [@vill](https://github.com/vill)
## 4.0.0
### Enhancements
* Add bootstrap v4.1 generator template. [@m5o](https://github.com/m5o)
* Add Rails 5.2 support. [@gobijan](https://github.com/gobijan)
* Add API to register custom components.[@feliperenan](https://github.com/feliperenan)
* Allow custom errors classes to inputs.[@feliperenan](https://github.com/feliperenan)
* Remove support from Rails 4.0, 4.1 and 4.2. [@feliperenan](https://github.com/feliperenan)
* Add support for citext, hstore, json & jsonb column types. [@swrobel](https://github.com/swrobel)
* Add :valid_class on input wrapper when value is present and valid [@aeberlin](https://github.com/aeberlin), [@m5o](https://github.com/m5o)
* Allow :valid_class to inputs when value is present and valid. [@m5o](https://github.com/m5o)
* Allow validation classes on input_field. [@feliperenan](https://github.com/feliperenan)
* Add basic ActiveStorage support. [@murb](https://github.com/murb)
### Bug fix
* Fix horizontal form label position, from right to text-right. [@cavpollo](https://github.com/cavpollo)
* Add base error display alongside existing errors. [@bluefalcon26](https://github.com/bluefalcon26)
* Silent deprecation warning for placeholder_text. [@moofkit](https://github.com/moofkit)
* Use custom i18n scope for label required html. [@tvdeyen](https://github.com/tvdeyen)
## 3.5.1
### Enhancements
* Exclude hidden field when unchecked_value: false. [@fschwahn](https://github.com/fschwahn)
* Add frozen_string_literal magic comment to several files. [@oniofchaos](https://github.com/oniofchaos)
* Try convert @object to model in case we got decorated object [@timurvafin](https://github.com/timurvafin)
- From now, if you are using some object that inherits from `SimpleDelegator`, you must implement
`def to_model; self; end`. Otherwise, *Simple Form* will convert the decorated object to the model
since `SimpleDelegator` will delegate it to the model.
* Code cleanup [@Fornacula](https://github.com/Fornacula)
### Bug fix
* Fix error when the scope from association has parameter. [@feliperenan](https://github.com/feliperenan)
* Only call `where` on associations when they respond to it. [@anicholson](https://github.com/anicholson)
* require 'action_pack' before using it. [@etagwerker](https://github.com/etagwerker)
* Check if Rails.env is defined. [@etagwerker](https://github.com/etagwerker)
* Fix minlength. [@mameier](https://github.com/mameier)
* Make errors_on_attribute return [] when not present. [@redrick](https://github.com/redrick)
* Fix boolean inputs in nested style for label non-string. [@feliperenan](https://github.com/feliperenan)
## 3.5.0
* Updated gem dependency to support Rails 5.1.x.
## 3.4.0
* Removed Ruby 2.4.0 `Integer` unification deprecation warning.
* Removed EOL Ruby 1.9.3 from the build matrix.
* Added `minlength` component.
* `boolean_label_class` can be set on a per-input basis.
## 3.3.1
### Bug fix
* Fix support for symbols when looking up types with `ActiveModel::Type`.
## 3.3.0
### enhancements
* Add the `aria-invalid` attribute on inputs with errors.
* Added support for the new `ActiveModel::Type` API over Active Record's
column objects.
### bug fix
* Fix `merge_wrapper_options` to correctly merge options with duplicated keys. [@herminiotorres](https://github.com/herminiotorres)
Closes [#1278](https://github.com/heartcombo/simple_form/issues/1278).
## 3.2.1
### enhancements
* Updated gem dependency to support Rails 5.0.x.
## 3.2.0
### bug fix
* Improve performance of input generation by disabling support for `_html` translations. This reverts the feature introduced on the 3.1.0 branch
## 3.1.1
### enhancements
* Add the `disabled_class` to the label when the input is disabled. [@rhodrid](https://github.com/rhodrid)
### bug fix
* Make it possible to override `required` value that was previously set in the wrapper. [@nashby](https://github.com/nashby)
* `date/time/datetime` inputs now correctly generate the label `for` attribute when
HTML5 compatibility is explicitly enabled. [@ericsullivan](https://github.com/ericsullivan)
* The datetime, date, and time inputs now have a nice format by default on bootstrap.
[@ulissesalmeida](https://github.com/ulissesalmeida) [@eltonchrls](https://github.com/eltonchrls)
* Now it is possible to set custom input mappings for collections.
Example:
```ruby
# On configuration:
config.input_mappings = { /gender$/ => :check_boxes }
# On form:
f.input :gender, collection: [:male, :female]
```
[strangeworks](https://github.com/strangeworks)
## 3.1.0
### enhancements
* Update foundation generator to version 5. [@jorge-d](https://github.com/jorge-d)
* Add mapping to `uuid` columns.
* Add custom namespaces for custom inputs feature. [@vala](https://github.com/vala)
* Add `:unless_blank` option to the wrapper API. [@IanVaughan](https://github.com/IanVaughan)
* Add support to html markup in the I18n options. [@laurocaetano](https://github.com/laurocaetano)
* Add the `full_error` component. [@laurocaetano](https://github.com/laurocaetano)
* Add support to `scope` to be used on associations. [@laurocaetano](https://github.com/laurocaetano)
* Execute the association `condition` in the object context. [@laurocaetano](https://github.com/laurocaetano)
* Check if the given association responds to `order` before calling it. [@laurocaetano](https://github.com/laurocaetano)
* Add Bootstrap 3 initializer template.
* For radio or checkbox collection always use `:item_wrapper_tag` to wrap the content and add `label` when using `boolean_style` with `:nested` [@kassio](https://github.com/kassio) and [@erichkist](https://github.com/erichkist)
* `input_field` uses the same wrapper as input but only with attribute components. [@nashby](https://github.com/nashby)
* Add wrapper mapping per form basis [@rcillo](https://github.com/rcillo) and [@bernardoamc](https://github.com/bernardoamc)
* Add `for` attribute to `label` when collections are rendered as radio or checkbox [@erichkist](https://github.com/erichkist), [@ulissesalmeida](https://github.com/ulissesalmeida) and [@fabioyamate](https://github.com/fabioyamate)
* Add `include_default_input_wrapper_class` config [@luizcosta](https://github.com/luizcosta)
* Map `datetime`, `date` and `time` input types to their respective HTML5 input tags
when the `:html5` is set to `true` [@volmer](https://github.com/volmer)
* Add `boolean_label_class` config.
* Add `:html` option to include additional attributes on custom wrappers [@remofritzsche](https://github.com/remofritzsche) and [@ulissesalmeida](https://github.com/ulissesalmeida)
* Make possible to use the Wrappers API to define attributes for the components.
See https://github.com/heartcombo/simple_form/pull/997 for more information.
* Put a whitespace before the `inline_label` options of boolean input if it is present.
* Add support to configure the `label_text` proc at the wrapper level. [@NOX73](https://github.com/NOX73)
* `label_text` proc now receive three arguments (label, request, and if the label was explicit). [@timscott](https://github.com/timscott)
* Add I18n support to `:include_blank` and `:prompt` when `:translate` is used as value. [@haines](https://github.com/heartcombo/simple_form/pull/616)
* Add support to define custom error messages for the attributes.
* Add support to change the I18n scope to be used in Simple Form. [@nielsbuus](https://github.com/nielsbuus)
* The default form class can now be overridden with `html: { :class }`. [@rmm5t](https://github.com/rmm5t)
### bug fix
* Fix `full_error` when the attribute is an association. [@mvdamme](https://github.com/jorge-d)
* Fix support to `:namespace` and `:index` options for nested check boxes and radio buttons when the attribute is an association.
* Collection input that uses automatic collection translation properly sets checked values.
Closes [#971](https://github.com/heartcombo/simple_form/issues/971) [@nashby](https://github.com/nashby)
* Collection input generates `required` attribute if it has `prompt` option. [@nashby](https://github.com/nashby)
* Grouped collection uses the first non-empty object to detect label and value methods.
## deprecation
* Methods on custom inputs now accept a required argument with the wrapper options.
See https://github.com/heartcombo/simple_form/pull/997 for more information.
* SimpleForm.form_class is deprecated in favor of SimpleForm.default_form_class.
Future versions of Simple Form will not generate `simple_form` class for the form
element.
See https://github.com/heartcombo/simple_form/pull/1109 for more information.
Please check [v3.0](https://github.com/heartcombo/simple_form/blob/v3.0/CHANGELOG.md) for previous changes.
================================================
FILE: CONTRIBUTING.md
================================================
## Contributing
1. If you have any questions about Simple Form, search the
[Wiki](https://github.com/heartcombo/simple_form/wiki)
or [Stack Overflow](http://stackoverflow.com/questions/tagged/simple_form).
Do not post questions here.
2. If you find a security bug, **DO NOT** submit an issue here.
Please send an e-mail to [heartcombo@googlegroups.com](mailto:heartcombo@googlegroups.com)
instead.
3. Do a small search on the issues tracker before submitting your issue to
see if it was already reported or fixed. In case it was not, create your report
including Rails and Simple Form versions. If you are getting exceptions, please
include the full backtrace.
That's it! The more information you give, the more easy it becomes for us to
track it down and fix it. Ideal scenario would be adding the issue to Simple Form
test suite or to a sample application.
Thanks!
================================================
FILE: Gemfile
================================================
source "https://rubygems.org"
gemspec
gem "activemodel", "~> 8.1.0"
gem "actionpack", "~> 8.1.0"
gem "railties", "~> 8.1.0"
================================================
FILE: ISSUE_TEMPLATE.md
================================================
## Precheck
- Do not use the issues tracker for help or support, try Stack Overflow.
- For bugs, do a quick search and make sure the bug has not yet been reported
- If you found a security bug, do not report it through GitHub. Please send an e-mail to heartcombo@googlegroups.com instead.
- Finally, be nice and have fun!
## Environment
- Ruby **[version]**
- Rails **[version]**
- Simple Form **[version]**
## Current behavior
Include code samples, errors, steps to reproduce the error and stacktraces if appropriate.
Will be even more helpful if you provide a sample application or a test case that reproduces the error.
## Expected behavior
================================================
FILE: MIT-LICENSE
================================================
Copyright (c) 2020-CURRENT Rafael França, Carlos Antonio da Silva
Copyright (c) 2009-2019 Plataformatec
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
================================================
FILE: README.md
================================================

[](https://badge.fury.io/rb/simple_form)
Rails forms made easy.
**Simple Form** aims to be as flexible as possible while helping you with powerful components to create
your forms. The basic goal of **Simple Form** is to not touch your way of defining the layout, letting
you find the better design for your eyes. Most of the DSL was inherited from Formtastic,
which we are thankful for and should make you feel right at home.
INFO: This README refers to **Simple Form** 5.0. For older releases, check the related branch for your version.
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
## Table of Contents
- [Installation](#installation)
- [Bootstrap](#bootstrap-5)
- [Zurb Foundation 5](#zurb-foundation-5)
- [Country Select](#country-select)
- [Usage](#usage)
- [Stripping away all wrapper divs](#stripping-away-all-wrapper-divs)
- [Collections](#collections)
- [Priority](#priority)
- [Associations](#associations)
- [Buttons](#buttons)
- [Wrapping Rails Form Helpers](#wrapping-rails-form-helpers)
- [Extra helpers](#extra-helpers)
- [Simple Fields For](#simple-fields-for)
- [Collection Radio Buttons](#collection-radio-buttons)
- [Collection Check Boxes](#collection-check-boxes)
- [Available input types and defaults for each column type](#available-input-types-and-defaults-for-each-column-type)
- [Custom inputs](#custom-inputs)
- [Custom form builder](#custom-form-builder)
- [I18n](#i18n)
- [Configuration](#configuration)
- [The wrappers API](#the-wrappers-api)
- [Custom Components](#custom-components)
- [HTML 5 Notice](#html-5-notice)
- [Using non Active Record objects](#using-non-active-record-objects)
- [Information](#information)
- [RDocs](#rdocs)
- [Supported Ruby / Rails versions](#supported-ruby--rails-versions)
- [Bug reports](#bug-reports)
- [Maintainers](#maintainers)
- [License](#license)
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
## Installation
Add it to your Gemfile:
```ruby
gem 'simple_form'
```
Run the following command to install it:
```console
bundle install
```
Run the generator:
```console
rails generate simple_form:install
```
### Bootstrap 5
**Simple Form** can be easily integrated with [Bootstrap 5](https://getbootstrap.com/).
Use the `bootstrap` option in the install generator, like this:
```console
rails generate simple_form:install --bootstrap
```
This will add an initializer that configures **Simple Form** wrappers for
Bootstrap 5's [form controls](https://getbootstrap.com/docs/5.0/forms/overview/).
You have to be sure that you added a copy of the [Bootstrap](https://getbootstrap.com/)
assets on your application.
For more information see the generator output, our
[example application code](https://github.com/heartcombo/simple_form-bootstrap).
### Zurb Foundation 5
To generate wrappers that are compatible with [Zurb Foundation 5](https://get.foundation/sites/docs-v5/), pass
the `foundation` option to the generator, like this:
```console
rails generate simple_form:install --foundation
```
Please note that the Foundation wrapper does not support the `:hint` option by default. In order to
enable hints, please uncomment the appropriate line in `config/initializers/simple_form_foundation.rb`.
You will need to provide your own CSS styles for hints.
Please see the [instructions on how to install Foundation in a Rails app](https://get.foundation/sites/docs-v5/applications.html).
### Country Select
If you want to use the country select, you will need the
[country_select gem](https://rubygems.org/gems/country_select), add it to your Gemfile:
```ruby
gem 'country_select'
```
If you don't want to use the gem you can easily override this behaviour by mapping the
country inputs to something else, with a line like this in your `simple_form.rb` initializer:
```ruby
config.input_mappings = { /country/ => :string }
```
## Usage
**Simple Form** was designed to be customized as you need to. Basically it's a stack of components that
are invoked to create a complete html input for you, which by default contains label, hints, errors
and the input itself. It does not aim to create a lot of different logic from the default Rails
form helpers, as they do a great job by themselves. Instead, **Simple Form** acts as a DSL and just
maps your input type (retrieved from the column definition in the database) to a specific helper method.
To start using **Simple Form** you just have to use the helper it provides:
```erb
<%= simple_form_for @user do |f| %>
<%= f.input :username %>
<%= f.input :password %>
<%= f.button :submit %>
<% end %>
```
This will generate an entire form with labels for user name and password as well, and render errors
by default when you render the form with invalid data (after submitting for example).
You can overwrite the default label by passing it to the input method. You can also add a hint,
an error, or even a placeholder. For boolean inputs, you can add an inline label as well:
```erb
<%= simple_form_for @user do |f| %>
<%= f.input :username, label: 'Your username please', error: 'Username is mandatory, please specify one' %>
<%= f.input :password, hint: 'No special characters.' %>
<%= f.input :email, placeholder: 'user@domain.com' %>
<%= f.input :remember_me, inline_label: 'Yes, remember me' %>
<%= f.button :submit %>
<% end %>
```
In some cases you may want to disable labels, hints or errors. Or you may want to configure the html
of any of them:
```erb
<%= simple_form_for @user do |f| %>
<%= f.input :username, label_html: { class: 'my_class' }, hint_html: { class: 'hint_class' } %>
<%= f.input :password, hint: false, error_html: { id: 'password_error' } %>
<%= f.input :password_confirmation, label: false %>
<%= f.button :submit %>
<% end %>
```
It is also possible to pass any html attribute straight to the input, by using the `:input_html`
option, for instance:
```erb
<%= simple_form_for @user do |f| %>
<%= f.input :username, input_html: { class: 'special' } %>
<%= f.input :password, input_html: { maxlength: 20 } %>
<%= f.input :remember_me, input_html: { value: '1' } %>
<%= f.button :submit %>
<% end %>
```
If you want to pass the same options to all inputs in the form (for example, a default class),
you can use the `:defaults` option in `simple_form_for`. Specific options in `input` call will
overwrite the defaults:
```erb
<%= simple_form_for @user, defaults: { input_html: { class: 'default_class' } } do |f| %>
<%= f.input :username, input_html: { class: 'special' } %>
<%= f.input :password, input_html: { maxlength: 20 } %>
<%= f.input :remember_me, input_html: { value: '1' } %>
<%= f.button :submit %>
<% end %>
```
Since **Simple Form** generates a wrapper div around your label and input by default, you can pass
any html attribute to that wrapper as well using the `:wrapper_html` option, like so:
```erb
<%= simple_form_for @user do |f| %>
<%= f.input :username, wrapper_html: { class: 'username' } %>
<%= f.input :password, wrapper_html: { id: 'password' } %>
<%= f.input :remember_me, wrapper_html: { class: 'options' } %>
<%= f.button :submit %>
<% end %>
```
Required fields are marked with an * prepended to their labels.
By default all inputs are required. When the form object includes `ActiveModel::Validations`
(which, for example, happens with Active Record models), fields are required only when there is `presence` validation.
Otherwise, **Simple Form** will mark fields as optional. For performance reasons, this
detection is skipped on validations that make use of conditional options, such as `:if` and `:unless`.
And of course, the `required` property of any input can be overwritten as needed:
```erb
<%= simple_form_for @user do |f| %>
<%= f.input :name, required: false %>
<%= f.input :username %>
<%= f.input :password %>
<%= f.button :submit %>
<% end %>
```
By default, **Simple Form** will look at the column type in the database and use an
appropriate input for the column. For example, a column created with type
`:text` in the database will use a `textarea` input by default. See the section
[Available input types and defaults for each column
type](https://github.com/heartcombo/simple_form#available-input-types-and-defaults-for-each-column-type)
for a complete list of defaults.
**Simple Form** also lets you overwrite the default input type it creates:
```erb
<%= simple_form_for @user do |f| %>
<%= f.input :username %>
<%= f.input :password %>
<%= f.input :description, as: :text %>
<%= f.input :accepts, as: :radio_buttons %>
<%= f.button :submit %>
<% end %>
```
So instead of a checkbox for the *accepts* attribute, you'll have a pair of radio buttons with yes/no
labels and a textarea instead of a text field for the description. You can also render boolean
attributes using `as: :select` to show a dropdown.
It is also possible to give the `:disabled` option to **Simple Form**, and it'll automatically mark
the wrapper as disabled with a CSS class, so you can style labels, hints and other components inside
the wrapper as well:
```erb
<%= simple_form_for @user do |f| %>
<%= f.input :username, disabled: true, hint: 'You cannot change your username.' %>
<%= f.button :submit %>
<% end %>
```
**Simple Form** inputs accept the same options as their corresponding input type helper in Rails:
```erb
<%= simple_form_for @user do |f| %>
<%= f.input :date_of_birth, as: :date, start_year: Date.today.year - 90,
end_year: Date.today.year - 12, discard_day: true,
order: [:month, :year] %>
<%= f.input :accepts, as: :boolean, checked_value: 'positive', unchecked_value: 'negative' %>
<%= f.button :submit %>
<% end %>
```
By default, **Simple Form** generates a hidden field to handle the un-checked case for boolean fields.
Passing `unchecked_value: false` in the options for boolean fields will cause this hidden field to be omitted,
following the convention in Rails. You can also specify `include_hidden: false` to skip the hidden field:
```erb
<%= simple_form_for @user do |f| %>
<%= f.input :just_the_checked_case, as: :boolean, include_hidden: false %>
<%= f.button :submit %>
<% end %>
```
**Simple Form** also allows you to use label, hint, input_field, error and full_error helpers
(please take a look at the rdocs for each method for more info):
```erb
<%= simple_form_for @user do |f| %>
<%= f.label :username %>
<%= f.input_field :username %>
<%= f.hint 'No special characters, please!' %>
<%= f.error :username, id: 'user_name_error' %>
<%= f.full_error :token %>
<%= f.submit 'Save' %>
<% end %>
```
Any extra option passed to these methods will be rendered as html option.
### Stripping away all wrapper divs
**Simple Form** also allows you to strip away all the div wrappers around the `<input>` field that is
generated with the usual `f.input`.
The easiest way to achieve this is to use `f.input_field`.
Example:
```ruby
simple_form_for @user do |f|
f.input_field :name
f.input_field :remember_me, as: :boolean
end
```
```html
<form>
...
<input class="string required" id="user_name" maxlength="255" name="user[name]" size="255" type="text">
<input name="user[remember_me]" type="hidden" value="0">
<label class="checkbox">
<input class="boolean optional" id="user_published" name="user[remember_me]" type="checkbox" value="1">
</label>
</form>
```
For check boxes and radio buttons you can remove the label changing `boolean_style` from default value `:nested` to `:inline`.
Example:
```ruby
simple_form_for @user do |f|
f.input_field :name
f.input_field :remember_me, as: :boolean, boolean_style: :inline
end
```
```html
<form>
...
<input class="string required" id="user_name" maxlength="255" name="user[name]" size="255" type="text">
<input name="user[remember_me]" type="hidden" value="0">
<input class="boolean optional" id="user_remember_me" name="user[remember_me]" type="checkbox" value="1">
</form>
```
To view the actual RDocs for this, check them out here - https://rubydoc.info/github/heartcombo/simple_form/main/SimpleForm/FormBuilder:input_field
### Collections
And what if you want to create a select containing the age from 18 to 60 in your form? You can do it
overriding the `:collection` option:
```erb
<%= simple_form_for @user do |f| %>
<%= f.input :user %>
<%= f.input :age, collection: 18..60 %>
<%= f.button :submit %>
<% end %>
```
Collections can be arrays or ranges, and when a `:collection` is given the `:select` input will be
rendered by default, so we don't need to pass the `as: :select` option. Other types of collection
are `:radio_buttons` and `:check_boxes`. Those are added by **Simple Form** to Rails set of form
helpers (read Extra Helpers section below for more information).
Collection inputs accept two other options beside collections:
* *label_method* => the label method to be applied to the collection to retrieve the label (use this
instead of the `text_method` option in `collection_select`)
* *value_method* => the value method to be applied to the collection to retrieve the value
Those methods are useful to manipulate the given collection. Both of these options also accept
lambda/procs in case you want to calculate the value or label in a special way eg. custom
translation. You can also define a `to_label` method on your model as **Simple Form** will search for
and use `:to_label` as a `:label_method` first if it is found.
By default, **Simple Form** will use the first item from an array as the label and the second one as the value.
If you want to change this behavior you must make it explicit, like this:
```erb
<%= simple_form_for @user do |f| %>
<%= f.input :gender, as: :radio_buttons, collection: [['0', 'female'], ['1', 'male']], label_method: :second, value_method: :first %>
<% end %>
```
All other options given are sent straight to the underlying Rails helper(s): [`collection_select`](https://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-collection_select), [`collection_check_boxes`](https://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-collection_check_boxes), [`collection_radio_buttons`](https://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html#method-i-collection_radio_buttons). For example, you can pass `prompt` and `selected` as:
```ruby
f.input :age, collection: 18..60, prompt: "Select your age", selected: 21
```
It may also be useful to explicitly pass a value to the optional `:selected` like above, especially if passing a collection of nested objects.
It is also possible to create grouped collection selects, that will use the html *optgroup* tags, like this:
```ruby
f.input :country_id, collection: @continents, as: :grouped_select, group_method: :countries
```
Grouped collection inputs accept the same `:label_method` and `:value_method` options, which will be
used to retrieve label/value attributes for the `option` tags. Besides that, you can give:
* *group_method* => the method to be called on the given collection to generate the options for
each group (required)
* *group_label_method* => the label method to be applied on the given collection to retrieve the label
for the _optgroup_ (**Simple Form** will attempt to guess the best one the same way it does with
`:label_method`)
### Priority
**Simple Form** also supports `:time_zone` and `:country`. When using such helpers, you can give
`:priority` as an option to select which time zones and/or countries should be given higher priority:
```ruby
f.input :residence_country, priority: [ "Brazil" ]
f.input :time_zone, priority: /US/
```
Those values can also be configured with a default value to be used on the site through the
`SimpleForm.country_priority` and `SimpleForm.time_zone_priority` helpers.
Note: While using `country_select` if you want to restrict to only a subset of countries for a specific
drop down then you may use the `:collection` option:
```ruby
f.input :shipping_country, priority: [ "Brazil" ], collection: [ "Australia", "Brazil", "New Zealand"]
```
### Associations
To deal with associations, **Simple Form** can generate select inputs, a series of radios buttons or checkboxes.
Lets see how it works: imagine you have a user model that belongs to a company and `has_and_belongs_to_many`
roles. The structure would be something like:
```ruby
class User < ActiveRecord::Base
belongs_to :company
has_and_belongs_to_many :roles
end
class Company < ActiveRecord::Base
has_many :users
end
class Role < ActiveRecord::Base
has_and_belongs_to_many :users
end
```
Now we have the user form:
```erb
<%= simple_form_for @user do |f| %>
<%= f.input :name %>
<%= f.association :company %>
<%= f.association :roles %>
<%= f.button :submit %>
<% end %>
```
Simple enough, right? This is going to render a `:select` input for choosing the `:company`, and another
`:select` input with `:multiple` option for the `:roles`. You can, of course, change it to use radio
buttons and checkboxes as well:
```ruby
f.association :company, as: :radio_buttons
f.association :roles, as: :check_boxes
```
The association helper just invokes `input` under the hood, so all options available to `:select`,
`:radio_buttons` and `:check_boxes` are also available to association. Additionally, you can specify
the collection by hand, all together with the prompt:
```ruby
f.association :company, collection: Company.active.order(:name), prompt: "Choose a Company"
```
In case you want to declare different labels and values:
```ruby
f.association :company, label_method: :company_name, value_method: :id, include_blank: false
```
Please note that the association helper is currently only tested with Active Record. It currently
does not work well with Mongoid and depending on the ORM you're using your mileage may vary.
### Buttons
All web forms need buttons, right? **Simple Form** wraps them in the DSL, acting like a proxy:
```erb
<%= simple_form_for @user do |f| %>
<%= f.input :name %>
<%= f.button :submit %>
<% end %>
```
The above will simply call submit. You choose to use it or not, it's just a question of taste.
The button method also accepts optional parameters, that are delegated to the underlying submit call:
```erb
<%= f.button :submit, "Custom Button Text", class: "my-button" %>
```
To create a `<button>` element, use the following syntax:
```erb
<%= f.button :button, "Custom Button Text" %>
<%= f.button :button do %>
Custom Button Text
<% end %>
```
### Wrapping Rails Form Helpers
Say you wanted to use a rails form helper but still wrap it in **Simple Form** goodness? You can, by
calling input with a block like so:
```erb
<%= f.input :role do %>
<%= f.select :role, Role.all.map { |r| [r.name, r.id, { class: r.company.id }] }, include_blank: true %>
<% end %>
```
In the above example, we're taking advantage of Rails 3's select method that allows us to pass in a
hash of additional attributes for each option.
### Extra helpers
**Simple Form** also comes with some extra helpers you can use inside rails default forms without relying
on `simple_form_for` helper. They are listed below.
#### Simple Fields For
Wrapper to use **Simple Form** inside a default rails form. It works in the same way that the `fields_for`
Rails helper, but change the builder to use the `SimpleForm::FormBuilder`.
```ruby
form_for @user do |f|
f.simple_fields_for :posts do |posts_form|
# Here you have all simple_form methods available
posts_form.input :title
end
end
```
#### Collection Radio Buttons
Creates a collection of radio inputs with labels associated (same API as `collection_select`):
```ruby
form_for @user do |f|
f.collection_radio_buttons :options, [[true, 'Yes'], [false, 'No']], :first, :last
end
```
```html
<input id="user_options_true" name="user[options]" type="radio" value="true" />
<label class="collection_radio_buttons" for="user_options_true">Yes</label>
<input id="user_options_false" name="user[options]" type="radio" value="false" />
<label class="collection_radio_buttons" for="user_options_false">No</label>
```
#### Collection Check Boxes
Creates a collection of checkboxes with labels associated (same API as `collection_select`):
```ruby
form_for @user do |f|
f.collection_check_boxes :options, [[true, 'Yes'], [false, 'No']], :first, :last
end
```
```html
<input name="user[options][]" type="hidden" value="" />
<input id="user_options_true" name="user[options][]" type="checkbox" value="true" />
<label class="collection_check_box" for="user_options_true">Yes</label>
<input name="user[options][]" type="hidden" value="" />
<input id="user_options_false" name="user[options][]" type="checkbox" value="false" />
<label class="collection_check_box" for="user_options_false">No</label>
```
To use this with associations in your model, you can do the following:
```ruby
form_for @user do |f|
f.collection_check_boxes :role_ids, Role.all, :id, :name # using :roles here is not going to work.
end
```
To add a CSS class to the label item, you can use the `item_label_class` option:
```ruby
f.collection_check_boxes :role_ids, Role.all, :id, :name, item_label_class: 'my-custom-class'
```
## Available input types and defaults for each column type
The following table shows the html element you will get for each attribute
according to its database definition. These defaults can be changed by
specifying the helper method in the column `Mapping` as the `as:` option.
Mapping | Generated HTML Element | Database Column Type
--------------- |--------------------------------------|---------------------
`boolean` | `input[type=checkbox]` | `boolean`
`string` | `input[type=text]` | `string`
`citext` | `input[type=text]` | `citext`
`email` | `input[type=email]` | `string` with `name =~ /email/`
`url` | `input[type=url]` | `string` with `name =~ /url/`
`tel` | `input[type=tel]` | `string` with `name =~ /phone/`
`password` | `input[type=password]` | `string` with `name =~ /password/`
`search` | `input[type=search]` | -
`uuid` | `input[type=text]` | `uuid`
`color` | `input[type=color]` | `string`
`text` | `textarea` | `text`
`hstore` | `textarea` | `hstore`
`json` | `textarea` | `json`
`jsonb` | `textarea` | `jsonb`
`file` | `input[type=file]` | `string` responding to file methods
`hidden` | `input[type=hidden]` | -
`integer` | `input[type=number]` | `integer`
`float` | `input[type=number]` | `float`
`decimal` | `input[type=number]` | `decimal`
`range` | `input[type=range]` | -
`datetime` | `datetime select` | `datetime/timestamp`
`date` | `date select` | `date`
`time` | `time select` | `time`
`weekday` | `select` (weekdays as options) | -
`select` | `select` | `belongs_to`/`has_many`/`has_and_belongs_to_many` associations
`radio_buttons` | collection of `input[type=radio]` | `belongs_to` associations
`check_boxes` | collection of `input[type=checkbox]` | `has_many`/`has_and_belongs_to_many` associations
`country` | `select` (countries as options) | `string` with `name =~ /country/`
`time_zone` | `select` (timezones as options) | `string` with `name =~ /time_zone/`
`rich_text_area`| `trix-editor` | -
## Custom inputs
It is very easy to add custom inputs to **Simple Form**. For instance, if you want to add a custom input
that extends the string one, you just need to add this file:
```ruby
# app/inputs/currency_input.rb
class CurrencyInput < SimpleForm::Inputs::Base
def input(wrapper_options)
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
"$ #{@builder.text_field(attribute_name, merged_input_options)}".html_safe
end
end
```
And use it in your views:
```ruby
f.input :money, as: :currency
```
Note, you may have to create the `app/inputs/` directory and restart your webserver.
You can also redefine existing **Simple Form** inputs by creating a new class with the same name. For
instance, if you want to wrap date/time/datetime in a div, you can do:
```ruby
# app/inputs/date_time_input.rb
class DateTimeInput < SimpleForm::Inputs::DateTimeInput
def input(wrapper_options)
template.content_tag(:div, super)
end
end
```
Or if you want to add a class to all the select fields you can do:
```ruby
# app/inputs/collection_select_input.rb
class CollectionSelectInput < SimpleForm::Inputs::CollectionSelectInput
def input_html_classes
super.push('chosen')
end
end
```
If needed, you can namespace your custom inputs in a module and tell **Simple Form** to look for
their definitions in this module. This can avoid conflicts with other form libraries (like Formtastic) that look up
the global context to find inputs definition too.
```ruby
# app/inputs/custom_inputs/numeric_input
module CustomInputs
class NumericInput < SimpleForm::Inputs::NumericInput
def input_html_classes
super.push('no-spinner')
end
end
end
```
And in the **SimpleForm** initializer :
```ruby
# config/simple_form.rb
config.custom_inputs_namespaces << "CustomInputs"
```
## Custom form builder
You can create a custom form builder that uses **Simple Form**.
Create a helper method that calls `simple_form_for` with a custom builder:
```ruby
def custom_form_for(object, *args, &block)
options = args.extract_options!
simple_form_for(object, *(args << options.merge(builder: CustomFormBuilder)), &block)
end
```
Create a form builder class that inherits from `SimpleForm::FormBuilder`.
```ruby
class CustomFormBuilder < SimpleForm::FormBuilder
def input(attribute_name, options = {}, &block)
super(attribute_name, options.merge(label: false), &block)
end
end
```
## I18n
**Simple Form** uses all power of I18n API to lookup labels, hints, prompts and placeholders. To customize your
forms you can create a locale file like this:
```yaml
en:
simple_form:
labels:
user:
username: 'User name'
password: 'Password'
hints:
user:
username: 'User name to sign in.'
password: 'No special characters, please.'
placeholders:
user:
username: 'Your username'
password: '****'
include_blanks:
user:
age: 'Rather not say'
prompts:
user:
role: 'Select your role'
```
And your forms will use this information to render the components for you.
**Simple Form** also lets you be more specific, separating lookups through actions.
Let's say you want a different label for new and edit actions, the locale file would
be something like:
```yaml
en:
simple_form:
labels:
user:
username: 'User name'
password: 'Password'
edit:
username: 'Change user name'
password: 'Change password'
```
This way **Simple Form** will figure out the right translation for you, based on the action being
rendered. And to be a little bit DRYer with your locale file, you can specify defaults for all
models under the 'defaults' key:
```yaml
en:
simple_form:
labels:
defaults:
username: 'User name'
password: 'Password'
new:
username: 'Choose a user name'
hints:
defaults:
username: 'User name to sign in.'
password: 'No special characters, please.'
placeholders:
defaults:
username: 'Your username'
password: '****'
```
**Simple Form** will always look for a default attribute translation under the "defaults" key if no
specific is found inside the model key.
In addition, **Simple Form** will fallback to default `human_attribute_name` from Rails when no other
translation is found for labels. Finally, you can also overwrite any label, hint or placeholder
inside your view, just by passing the option manually. This way the I18n lookup will be skipped.
For `:prompt` and `:include_blank` the I18n lookup is optional and to enable it is necessary to pass
`:translate` as value.
```ruby
f.input :role, prompt: :translate
```
**Simple Form** also has support for translating options in collection helpers. For instance, given a
User with a `:role` attribute, you might want to create a select box showing translated labels
that would post either `:admin` or `:editor` as value. With **Simple Form** you could create an input
like this:
```ruby
f.input :role, collection: [:admin, :editor]
```
And **Simple Form** will try a lookup like this in your locale file, to find the right labels to show:
```yaml
en:
simple_form:
options:
user:
role:
admin: 'Administrator'
editor: 'Editor'
```
You can also use the `defaults` key as you would do with labels, hints and placeholders. It is
important to notice that **Simple Form** will only do the lookup for options if you give a collection
composed of symbols only. This is to avoid constant lookups to I18n.
It's also possible to translate buttons, using Rails' built-in I18n support:
```yaml
en:
helpers:
submit:
user:
create: "Add %{model}"
update: "Save Changes"
```
There are other options that can be configured through I18n API, such as required text and boolean.
Be sure to check our locale file or the one copied to your application after you run
`rails generate simple_form:install`.
It should be noted that translations for labels, hints and placeholders for a namespaced model, e.g.
`Admin::User`, should be placed under `admin_user`, not under `admin/user`. This is different from
how translations for namespaced model and attribute names are defined:
```yaml
en:
activerecord:
models:
admin/user: User
attributes:
admin/user:
name: Name
```
They should be placed under `admin/user`. Form labels, hints and placeholders for those attributes,
though, should be placed under `admin_user`:
```yaml
en:
simple_form:
labels:
admin_user:
name: Name
```
This difference exists because **Simple Form** relies on `object_name` provided by Rails'
FormBuilder to determine the translation path for a given object instead of `i18n_key` from the
object itself. Thus, similarly, if a form for an `Admin::User` object is defined by calling
`simple_form_for @admin_user, as: :some_user`, **Simple Form** will look for translations
under `some_user` instead of `admin_user`.
When translating `simple_fields_for` attributes be sure to use the same name you pass to it, e.g. `simple_fields_for :posts` should be placed under `posts` not `post`:
```yaml
en:
simple_form:
labels:
posts:
title: 'Post title'
hints:
posts:
title: 'A good title'
placeholders:
posts:
title: 'Once upon a time...'
```
## Configuration
**Simple Form** has several configuration options. You can read and change them in the initializer
created by **Simple Form**, so if you haven't executed the command below yet, please do:
`rails generate simple_form:install`
### The wrappers API
With **Simple Form** you can configure how your components will be rendered using the wrappers API.
The syntax looks like this:
```ruby
config.wrappers tag: :div, class: :input,
error_class: :field_with_errors,
valid_class: :field_without_errors do |b|
# Form extensions
b.use :html5
b.optional :pattern
b.use :maxlength
b.use :placeholder
b.use :readonly
# Form components
b.use :label_input
b.use :hint, wrap_with: { tag: :span, class: :hint }
b.use :error, wrap_with: { tag: :span, class: :error }
end
```
The _Form components_ will generate the form tags like labels, inputs, hints or errors contents.
The available components are:
```ruby
:label # The <label> tag alone
:input # The <input> tag alone
:label_input # The <label> and the <input> tags
:hint # The hint for the input
:error # The error for the input
```
The _Form extensions_ are used to generate some attributes or perform some lookups on the model to
add extra information to your components.
You can create new _Form components_ using the wrappers API as in the following example:
```ruby
config.wrappers do |b|
b.use :placeholder
b.use :label_input
b.wrapper tag: :div, class: 'separator' do |component|
component.use :hint, wrap_with: { tag: :span, class: :hint }
component.use :error, wrap_with: { tag: :span, class: :error }
end
end
```
this will wrap the hint and error components within a `div` tag using the class `'separator'`.
You can customize _Form components_ passing options to them:
```ruby
config.wrappers do |b|
b.use :label_input, class: 'label-input-class', error_class: 'is-invalid', valid_class: 'is-valid'
end
```
This sets the input and label classes to `'label-input-class'` and will set the class `'is-invalid'`
if the input has errors and `'is-valid'` if the input is valid.
If you want to customize the custom _Form components_ on demand you can give it a name like this:
```ruby
config.wrappers do |b|
b.use :placeholder
b.use :label_input
b.wrapper :my_wrapper, tag: :div, class: 'separator', html: { id: 'my_wrapper_id' } do |component|
component.use :hint, wrap_with: { tag: :span, class: :hint }
component.use :error, wrap_with: { tag: :span, class: :error }
end
end
```
and now you can pass options to your `input` calls to customize the `:my_wrapper` _Form component_.
```ruby
# Completely turns off the custom wrapper
f.input :name, my_wrapper: false
# Configure the html
f.input :name, my_wrapper_html: { id: 'special_id' }
# Configure the tag
f.input :name, my_wrapper_tag: :p
```
You can also define more than one wrapper and pick one to render in a specific form or input.
To define another wrapper you have to give it a name, as the follow:
```ruby
config.wrappers :small do |b|
b.use :placeholder
b.use :label_input
end
```
and use it in this way:
```ruby
# Specifying to whole form
simple_form_for @user, wrapper: :small do |f|
f.input :name
end
# Specifying to one input
simple_form_for @user do |f|
f.input :name, wrapper: :small
end
```
**Simple Form** also allows you to use optional elements. For instance, let's suppose you want to use
hints or placeholders, but you don't want them to be generated automatically. You can set their
default values to `false` or use the `optional` method. Is preferable to use the `optional` syntax:
```ruby
config.wrappers placeholder: false do |b|
b.use :placeholder
b.use :label_input
b.wrapper tag: :div, class: 'separator' do |component|
component.optional :hint, wrap_with: { tag: :span, class: :hint }
component.use :error, wrap_with: { tag: :span, class: :error }
end
end
```
By setting it as `optional`, a hint will only be generated when `hint: true` is explicitly used.
The same for placeholder.
It is also possible to give the option `:unless_blank` to the wrapper if you want to render it only
when the content is present.
```ruby
b.wrapper tag: :span, class: 'hint', unless_blank: true do |component|
component.optional :hint
end
```
## Custom Components
When you use custom wrappers, you might also be looking for a way to add custom components to your
wrapper. The default components are:
```ruby
:label # The <label> tag alone
:input # The <input> tag alone
:label_input # The <label> and the <input> tags
:hint # The hint for the input
:error # The error for the input
```
A custom component might be interesting for you if your views look something like this:
```erb
<%= simple_form_for @blog do |f| %>
<div class="row">
<div class="span1 number">
1
</div>
<div class="span8">
<%= f.input :title %>
</div>
</div>
<div class="row">
<div class="span1 number">
2
</div>
<div class="span8">
<%= f.input :body, as: :text %>
</div>
</div>
<% end %>
```
A cleaner method to create your views would be:
```erb
<%= simple_form_for @blog, wrapper: :with_numbers do |f| %>
<%= f.input :title, number: 1 %>
<%= f.input :body, as: :text, number: 2 %>
<% end %>
```
To use the number option on the input, first, tells to Simple Form the place where the components
will be:
``` ruby
# config/initializers/simple_form.rb
Dir[Rails.root.join('lib/components/**/*.rb')].each { |f| require f }
```
Create a new component within the path specified above:
```ruby
# lib/components/numbers_component.rb
module NumbersComponent
# To avoid deprecation warning, you need to make the wrapper_options explicit
# even when they won't be used.
def number(wrapper_options = nil)
@number ||= begin
options[:number].to_s.html_safe if options[:number].present?
end
end
end
SimpleForm.include_component(NumbersComponent)
```
Finally, add a new wrapper to the config/initializers/simple_form.rb file:
```ruby
config.wrappers :with_numbers, tag: 'div', class: 'row', error_class: 'error' do |b|
b.use :html5
b.use :number, wrap_with: { tag: 'div', class: 'span1 number' }
b.wrapper tag: 'div', class: 'span8' do |ba|
ba.use :placeholder
ba.use :label
ba.use :input
ba.use :error, wrap_with: { tag: 'span', class: 'help-inline' }
ba.use :hint, wrap_with: { tag: 'p', class: 'help-block' }
end
end
```
## HTML 5 Notice
By default, **Simple Form** will generate input field types and attributes that are supported in HTML5,
but are considered invalid HTML for older document types such as HTML4 or XHTML1.0. The HTML5
extensions include the new field types such as email, number, search, url, tel, and the new
attributes such as required, autofocus, maxlength, min, max, step.
Most browsers will not care, but some of the newer ones - in particular Chrome 10+ - use the
required attribute to force a value into an input and will prevent form submission without it.
Depending on the design of the application this may or may not be desired. In many cases it can
break existing UI's.
It is possible to disable all HTML 5 extensions in **Simple Form** by removing the `html5`
component from the wrapper used to render the inputs.
For example, change:
```ruby
config.wrappers tag: :div do |b|
b.use :html5
b.use :label_input
end
```
To:
```ruby
config.wrappers tag: :div do |b|
b.use :label_input
end
```
If you want to have all other HTML 5 features, such as the new field types, you can disable only
the browser validation:
```ruby
SimpleForm.browser_validations = false # default is true
```
This option adds a new `novalidate` property to the form, instructing it to skip all HTML 5
validation. The inputs will still be generated with the required and other attributes, that might
help you to use some generic javascript validation.
You can also add `novalidate` to a specific form by setting the option on the form itself:
```erb
<%= simple_form_for(resource, html: { novalidate: true }) do |form| %>
```
Please notice that none of the configurations above will disable the `placeholder` component,
which is an HTML 5 feature. We believe most of the newest browsers are handling this attribute
just fine, and if they aren't, any plugin you use would take care of applying the placeholder.
In any case, you can disable it if you really want to, by removing the placeholder component
from the components list in the **Simple Form** configuration file.
HTML 5 date / time inputs are not generated by **Simple Form** by default, so using `date`,
`time` or `datetime` will all generate select boxes using normal Rails helpers. We believe
browsers are not totally ready for these yet, but you can easily opt-in on a per-input basis
by passing the html5 option:
```erb
<%= f.input :expires_at, as: :date, html5: true %>
```
## Using non Active Record objects
There are few ways to build forms with objects that don't inherit from Active Record, as
follows:
You can include the module `ActiveModel::Model`.
```ruby
class User
include ActiveModel::Model
attr_accessor :id, :name
end
```
If you are using Presenters or Decorators that inherit from `SimpleDelegator` you can delegate
it to the model.
```ruby
class UserPresenter < SimpleDelegator
# Without that, Simple Form will consider the user model as the object.
def to_model
self
end
end
```
You can define all methods required by the helpers.
```ruby
class User
extend ActiveModel::Naming
attr_accessor :id, :name
def to_model
self
end
def to_key
id
end
def persisted?
false
end
end
```
To have SimpleForm infer the attributes' types, you can provide
`#has_attribute?` and `#type_for_attribute` methods.
The later should return an object that responds to `#type`
with the attribute type. This is useful for generating
the correct input types (eg: checkboxes for booleans).
```ruby
class User < Struct.new(:id, :name, :age, :registered)
def to_model
self
end
def model_name
OpenStruct.new(param_key: "user")
end
def to_key
id
end
def persisted?
id.present?
end
def has_attribute?(attr_name)
%w(id name age registered).include?(attr_name.to_s)
end
def type_for_attribute(attr_name)
case attr_name.to_s
when "id" then OpenStruct.new(type: :integer)
when "name" then OpenStruct.new(type: :string)
when "age" then OpenStruct.new(type: :integer)
when "registered" then OpenStruct.new(type: :boolean)
end
end
end
```
If your object doesn't implement those methods, you must make explicit it when you are
building the form
```ruby
class User
attr_accessor :id, :name
# The only method required to use the f.submit helper.
def persisted?
false
end
end
```
```erb
<%= simple_form_for(@user, as: :user, method: :post, url: users_path) do |f| %>
<%= f.input :name %>
<%= f.submit 'New user' %>
<% end %>
```
## Information
### RDocs
You can view the **Simple Form** documentation in RDoc format here:
https://rubydoc.info/github/heartcombo/simple_form/main/frames
### Supported Ruby / Rails versions
We intend to maintain support for all Ruby / Rails versions that haven't reached end-of-life.
For more information about specific versions please check [Ruby](https://www.ruby-lang.org/en/downloads/branches/)
and [Rails](https://guides.rubyonrails.org/maintenance_policy.html) maintenance policies, and our test matrix.
### Bug reports
If you discover any bugs, feel free to create an issue on GitHub. Please add as much information as
possible to help us in fixing the potential bug. We also encourage you to help even more by forking and
sending us a pull request.
https://github.com/heartcombo/simple_form/issues
If you have discovered a security related bug, please do NOT use the GitHub issue tracker. Send an e-mail to heartcombo.oss@gmail.com.
## License
MIT License.
Copyright 2020-CURRENT Rafael França, Carlos Antonio da Silva.
Copyright 2009-2019 Plataformatec.
The Simple Form logo is licensed under [Creative Commons Attribution-NonCommercial-NoDerivatives 4.0 International License](https://creativecommons.org/licenses/by-nc-nd/4.0/).
================================================
FILE: Rakefile
================================================
# encoding: UTF-8
require 'bundler/gem_tasks'
require 'rake/testtask'
desc 'Default: run unit tests.'
task default: :test
desc 'Test the simple_form plugin.'
Rake::TestTask.new(:test) do |t|
t.libs << 'lib'
t.libs << 'test'
t.pattern = 'test/**/*_test.rb'
t.verbose = true
end
begin
require 'rdoc/task'
desc 'Generate documentation for the simple_form plugin.'
RDoc::Task.new(:rdoc) do |rdoc|
rdoc.rdoc_dir = 'rdoc'
rdoc.title = 'SimpleForm'
rdoc.options << '--line-numbers'
rdoc.rdoc_files.include('README.md')
rdoc.rdoc_files.include('lib/**/*.rb')
end
rescue LoadError
puts 'RDoc::Task is not supported on this platform'
end
================================================
FILE: bin/test
================================================
#!/usr/bin/env ruby
$: << File.expand_path(File.expand_path('../../test', __FILE__))
require 'bundler/setup'
require 'rails/test_unit/runner'
require 'rails/test_unit/reporter'
Rails::TestUnitReporter.executable = 'bin/test'
Rails::TestUnit::Runner.parse_options(ARGV)
Rails::TestUnit::Runner.run(ARGV)
================================================
FILE: gemfiles/Gemfile-rails-7-0
================================================
source "https://rubygems.org"
gemspec path: ".."
gem "activemodel", "~> 7.0.0"
gem "actionpack", "~> 7.0.0"
gem "railties", "~> 7.0.0"
================================================
FILE: gemfiles/Gemfile-rails-7-1
================================================
source "https://rubygems.org"
gemspec path: ".."
gem "activemodel", "~> 7.1.0"
gem "actionpack", "~> 7.1.0"
gem "railties", "~> 7.1.0"
================================================
FILE: gemfiles/Gemfile-rails-7-2
================================================
source "https://rubygems.org"
gemspec path: ".."
gem "activemodel", "~> 7.2.0"
gem "actionpack", "~> 7.2.0"
gem "railties", "~> 7.2.0"
================================================
FILE: gemfiles/Gemfile-rails-8-0
================================================
source "https://rubygems.org"
gemspec path: ".."
gem "activemodel", "~> 8.0.0"
gem "actionpack", "~> 8.0.0"
gem "railties", "~> 8.0.0"
================================================
FILE: gemfiles/Gemfile-rails-main
================================================
source "https://rubygems.org"
gemspec path: ".."
gem "railties", github: "rails/rails", branch: "main"
gem "activemodel", github: "rails/rails", branch: "main"
gem "actionpack", github: "rails/rails", branch: "main"
gem "tzinfo"
================================================
FILE: lib/generators/simple_form/USAGE
================================================
To copy a SimpleForm initializer to your Rails App, with some configuration values, just do:
rails generate simple_form:install
================================================
FILE: lib/generators/simple_form/install_generator.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Generators
class InstallGenerator < Rails::Generators::Base
desc "Copy SimpleForm default files"
source_root File.expand_path('../templates', __FILE__)
class_option :template_engine, desc: 'Template engine to be invoked (erb, haml or slim).'
class_option :bootstrap, type: :boolean, desc: 'Add the Bootstrap 5 wrappers to the SimpleForm initializer.'
class_option :foundation, type: :boolean, desc: 'Add the Zurb Foundation 5 wrappers to the SimpleForm initializer.'
def info_bootstrap
return if options.bootstrap? || options.foundation?
puts "SimpleForm supports Bootstrap 5 and Zurb Foundation 5. If you want "\
"a configuration that is compatible with one of these frameworks, then please " \
"re-run this generator with --bootstrap or --foundation as an option."
end
def copy_config
template "config/initializers/simple_form.rb"
if options[:bootstrap]
template "config/initializers/simple_form_bootstrap.rb"
elsif options[:foundation]
template "config/initializers/simple_form_foundation.rb"
end
directory 'config/locales'
end
def copy_scaffold_template
engine = options[:template_engine]
copy_file "_form.html.#{engine}", "lib/templates/#{engine}/scaffold/_form.html.#{engine}"
end
def show_readme
if behavior == :invoke && options.bootstrap?
readme "README"
end
end
end
end
end
================================================
FILE: lib/generators/simple_form/templates/README
================================================
===============================================================================
Be sure to have a copy of the Bootstrap stylesheet available on your
application, you can get it on http://getbootstrap.com/.
For usage examples and documentation, see the example app:
https://github.com/heartcombo/simple_form-bootstrap
===============================================================================
================================================
FILE: lib/generators/simple_form/templates/_form.html.erb
================================================
<%# frozen_string_literal: true %>
<%%= simple_form_for(@<%= singular_table_name %>) do |f| %>
<%%= f.error_notification %>
<%%= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present? %>
<div class="form-inputs">
<%- attributes.each do |attribute| -%>
<%%= f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %> %>
<%- end -%>
</div>
<div class="form-actions">
<%%= f.button :submit %>
</div>
<%% end %>
================================================
FILE: lib/generators/simple_form/templates/_form.html.haml
================================================
-# frozen_string_literal: true
= simple_form_for(@<%= singular_table_name %>) do |f|
= f.error_notification
= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present?
.form-inputs
<%- attributes.each do |attribute| -%>
= f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %>
<%- end -%>
.form-actions
= f.button :submit
================================================
FILE: lib/generators/simple_form/templates/_form.html.slim
================================================
= simple_form_for(@<%= singular_table_name %>) do |f|
= f.error_notification
= f.error_notification message: f.object.errors[:base].to_sentence if f.object.errors[:base].present?
.form-inputs
<%- attributes.each do |attribute| -%>
= f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %>
<%- end -%>
.form-actions
= f.button :submit
================================================
FILE: lib/generators/simple_form/templates/config/initializers/simple_form.rb
================================================
# frozen_string_literal: true
#
# Uncomment this and change the path if necessary to include your own
# components.
# See https://github.com/heartcombo/simple_form#custom-components to know
# more about custom components.
# Dir[Rails.root.join('lib/components/**/*.rb')].each { |f| require f }
#
# Use this setup block to configure all options available in SimpleForm.
SimpleForm.setup do |config|
# Wrappers are used by the form builder to generate a
# complete input. You can remove any component from the
# wrapper, change the order or even add your own to the
# stack. The options given below are used to wrap the
# whole input.
config.wrappers :default, class: :input,
hint_class: :field_with_hint, error_class: :field_with_errors, valid_class: :field_without_errors do |b|
## Extensions enabled by default
# Any of these extensions can be disabled for a
# given input by passing: `f.input EXTENSION_NAME => false`.
# You can make any of these extensions optional by
# renaming `b.use` to `b.optional`.
# Determines whether to use HTML5 (:email, :url, ...)
# and required attributes
b.use :html5
# Calculates placeholders automatically from I18n
# You can also pass a string as f.input placeholder: "Placeholder"
b.use :placeholder
## Optional extensions
# They are disabled unless you pass `f.input EXTENSION_NAME => true`
# to the input. If so, they will retrieve the values from the model
# if any exists. If you want to enable any of those
# extensions by default, you can change `b.optional` to `b.use`.
# Calculates maxlength from length validations for string inputs
# and/or database column lengths
b.optional :maxlength
# Calculate minlength from length validations for string inputs
b.optional :minlength
# Calculates pattern from format validations for string inputs
b.optional :pattern
# Calculates min and max from length validations for numeric inputs
b.optional :min_max
# Calculates readonly automatically from readonly attributes
b.optional :readonly
## Inputs
# b.use :input, class: 'input', error_class: 'is-invalid', valid_class: 'is-valid'
b.use :label_input
b.use :hint, wrap_with: { tag: :span, class: :hint }
b.use :error, wrap_with: { tag: :span, class: :error }
## full_messages_for
# If you want to display the full error message for the attribute, you can
# use the component :full_error, like:
#
# b.use :full_error, wrap_with: { tag: :span, class: :error }
end
# The default wrapper to be used by the FormBuilder.
config.default_wrapper = :default
# Define the way to render check boxes / radio buttons with labels.
# Defaults to :nested for bootstrap config.
# inline: input + label
# nested: label > input
config.boolean_style = :nested
# Default class for buttons
config.button_class = 'btn'
# Method used to tidy up errors. Specify any Rails Array method.
# :first lists the first message for each field.
# Use :to_sentence to list all errors for each field.
# config.error_method = :first
# Default tag used for error notification helper.
config.error_notification_tag = :div
# CSS class to add for error notification helper.
config.error_notification_class = 'error_notification'
# Series of attempts to detect a default label method for collection.
# config.collection_label_methods = [ :to_label, :name, :title, :to_s ]
# Series of attempts to detect a default value method for collection.
# config.collection_value_methods = [ :id, :to_s ]
# You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
# config.collection_wrapper_tag = nil
# You can define the class to use on all collection wrappers. Defaulting to none.
# config.collection_wrapper_class = nil
# You can wrap each item in a collection of radio/check boxes with a tag,
# defaulting to :span.
# config.item_wrapper_tag = :span
# You can define a class to use in all item wrappers. Defaulting to none.
# config.item_wrapper_class = nil
# How the label text should be generated altogether with the required text.
# config.label_text = lambda { |label, required, explicit_label| "#{required} #{label}" }
# You can define the class to use on all labels. Default is nil.
# config.label_class = nil
# You can define the default class to be used on forms. Can be overridden
# with `html: { :class }`. Defaulting to none.
# config.default_form_class = nil
# You can define which elements should obtain additional classes
# config.generate_additional_classes_for = [:wrapper, :label, :input]
# Whether attributes are required by default (or not). Default is true.
# config.required_by_default = true
# Tell browsers whether to use the native HTML5 validations (novalidate form option).
# These validations are enabled in SimpleForm's internal config but disabled by default
# in this configuration, which is recommended due to some quirks from different browsers.
# To stop SimpleForm from generating the novalidate option, enabling the HTML5 validations,
# change this configuration to true.
config.browser_validations = false
# Custom mappings for input types. This should be a hash containing a regexp
# to match as key, and the input type that will be used when the field name
# matches the regexp as value.
# config.input_mappings = { /count/ => :integer }
# Custom wrappers for input types. This should be a hash containing an input
# type as key and the wrapper that will be used for all inputs with specified type.
# config.wrapper_mappings = { string: :prepend }
# Namespaces where SimpleForm should look for custom input classes that
# override default inputs.
# config.custom_inputs_namespaces << "CustomInputs"
# Default priority for time_zone inputs.
# config.time_zone_priority = nil
# Default priority for country inputs.
# config.country_priority = nil
# When false, do not use translations for labels.
# config.translate_labels = true
# Automatically discover new inputs in Rails' autoload path.
# config.inputs_discovery = true
# Cache SimpleForm inputs discovery
# config.cache_discovery = !Rails.env.development?
# Default class for inputs
# config.input_class = nil
# Define the default class of the input wrapper of the boolean input.
config.boolean_label_class = 'checkbox'
# Defines if the default input wrapper class should be included in radio
# collection wrappers.
# config.include_default_input_wrapper_class = true
# Defines which i18n scope will be used in Simple Form.
# config.i18n_scope = 'simple_form'
# Defines validation classes to the input_field. By default it's nil.
# config.input_field_valid_class = 'is-valid'
# config.input_field_error_class = 'is-invalid'
end
================================================
FILE: lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb
================================================
# frozen_string_literal: true
# These defaults are defined and maintained by the community at
# https://github.com/heartcombo/simple_form-bootstrap
# Please submit feedback, changes and tests only there.
# Uncomment this and change the path if necessary to include your own
# components.
# See https://github.com/heartcombo/simple_form#custom-components
# to know more about custom components.
# Dir[Rails.root.join('lib/components/**/*.rb')].each { |f| require f }
# Use this setup block to configure all options available in SimpleForm.
SimpleForm.setup do |config|
# Default class for buttons
config.button_class = 'btn'
# Define the default class of the input wrapper of the boolean input.
config.boolean_label_class = 'form-check-label'
# How the label text should be generated altogether with the required text.
config.label_text = lambda { |label, required, explicit_label| "#{label} #{required}" }
# Define the way to render check boxes / radio buttons with labels.
config.boolean_style = :inline
# You can wrap each item in a collection of radio/check boxes with a tag
config.item_wrapper_tag = :div
# Defines if the default input wrapper class should be included in radio
# collection wrappers.
config.include_default_input_wrapper_class = false
# CSS class to add for error notification helper.
config.error_notification_class = 'alert alert-danger'
# Method used to tidy up errors. Specify any Rails Array method.
# :first lists the first message for each field.
# :to_sentence to list all errors for each field.
config.error_method = :to_sentence
# add validation classes to `input_field`
config.input_field_error_class = 'is-invalid'
config.input_field_valid_class = 'is-valid'
# vertical forms
#
# vertical default_wrapper
config.wrappers :vertical_form, class: 'mb-3' do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :minlength
b.optional :pattern
b.optional :min_max
b.optional :readonly
b.use :label, class: 'form-label'
b.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
b.use :full_error, wrap_with: { class: 'invalid-feedback' }
b.use :hint, wrap_with: { class: 'form-text' }
end
# vertical input for boolean
config.wrappers :vertical_boolean, tag: 'fieldset', class: 'mb-3' do |b|
b.use :html5
b.optional :readonly
b.wrapper :form_check_wrapper, class: 'form-check' do |bb|
bb.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
bb.use :label, class: 'form-check-label'
bb.use :full_error, wrap_with: { class: 'invalid-feedback' }
bb.use :hint, wrap_with: { class: 'form-text' }
end
end
# vertical input for radio buttons and check boxes
config.wrappers :vertical_collection, item_wrapper_class: 'form-check', item_label_class: 'form-check-label', tag: 'fieldset', class: 'mb-3' do |b|
b.use :html5
b.optional :readonly
b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
ba.use :label_text
end
b.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
b.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
b.use :hint, wrap_with: { class: 'form-text' }
end
# vertical input for inline radio buttons and check boxes
config.wrappers :vertical_collection_inline, item_wrapper_class: 'form-check form-check-inline', item_label_class: 'form-check-label', tag: 'fieldset', class: 'mb-3' do |b|
b.use :html5
b.optional :readonly
b.wrapper :legend_tag, tag: 'legend', class: 'col-form-label pt-0' do |ba|
ba.use :label_text
end
b.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
b.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
b.use :hint, wrap_with: { class: 'form-text' }
end
# vertical file input
config.wrappers :vertical_file, class: 'mb-3' do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :minlength
b.optional :readonly
b.use :label, class: 'form-label'
b.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
b.use :full_error, wrap_with: { class: 'invalid-feedback' }
b.use :hint, wrap_with: { class: 'form-text' }
end
# vertical select input
config.wrappers :vertical_select, class: 'mb-3' do |b|
b.use :html5
b.optional :readonly
b.use :label, class: 'form-label'
b.use :input, class: 'form-select', error_class: 'is-invalid', valid_class: 'is-valid'
b.use :full_error, wrap_with: { class: 'invalid-feedback' }
b.use :hint, wrap_with: { class: 'form-text' }
end
# vertical multi select
config.wrappers :vertical_multi_select, class: 'mb-3' do |b|
b.use :html5
b.optional :readonly
b.use :label, class: 'form-label'
b.wrapper class: 'd-flex flex-row justify-content-between align-items-center' do |ba|
ba.use :input, class: 'form-select mx-1', error_class: 'is-invalid', valid_class: 'is-valid'
end
b.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
b.use :hint, wrap_with: { class: 'form-text' }
end
# vertical range input
config.wrappers :vertical_range, class: 'mb-3' do |b|
b.use :html5
b.use :placeholder
b.optional :readonly
b.optional :step
b.use :label, class: 'form-label'
b.use :input, class: 'form-range', error_class: 'is-invalid', valid_class: 'is-valid'
b.use :full_error, wrap_with: { class: 'invalid-feedback' }
b.use :hint, wrap_with: { class: 'form-text' }
end
# horizontal forms
#
# horizontal default_wrapper
config.wrappers :horizontal_form, class: 'row mb-3' do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :minlength
b.optional :pattern
b.optional :min_max
b.optional :readonly
b.use :label, class: 'col-sm-3 col-form-label'
b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
ba.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
ba.use :full_error, wrap_with: { class: 'invalid-feedback' }
ba.use :hint, wrap_with: { class: 'form-text' }
end
end
# horizontal input for boolean
config.wrappers :horizontal_boolean, class: 'row mb-3' do |b|
b.use :html5
b.optional :readonly
b.wrapper :grid_wrapper, class: 'col-sm-9 offset-sm-3' do |wr|
wr.wrapper :form_check_wrapper, class: 'form-check' do |bb|
bb.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
bb.use :label, class: 'form-check-label'
bb.use :full_error, wrap_with: { class: 'invalid-feedback' }
bb.use :hint, wrap_with: { class: 'form-text' }
end
end
end
# horizontal input for radio buttons and check boxes
config.wrappers :horizontal_collection, item_wrapper_class: 'form-check', item_label_class: 'form-check-label', class: 'row mb-3' do |b|
b.use :html5
b.optional :readonly
b.use :label, class: 'col-sm-3 col-form-label pt-0'
b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
ba.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
ba.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
ba.use :hint, wrap_with: { class: 'form-text' }
end
end
# horizontal input for inline radio buttons and check boxes
config.wrappers :horizontal_collection_inline, item_wrapper_class: 'form-check form-check-inline', item_label_class: 'form-check-label', class: 'row mb-3' do |b|
b.use :html5
b.optional :readonly
b.use :label, class: 'col-sm-3 col-form-label pt-0'
b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
ba.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
ba.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
ba.use :hint, wrap_with: { class: 'form-text' }
end
end
# horizontal file input
config.wrappers :horizontal_file, class: 'row mb-3' do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :minlength
b.optional :readonly
b.use :label, class: 'col-sm-3 col-form-label'
b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
ba.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
ba.use :full_error, wrap_with: { class: 'invalid-feedback' }
ba.use :hint, wrap_with: { class: 'form-text' }
end
end
# horizontal select input
config.wrappers :horizontal_select, class: 'row mb-3' do |b|
b.use :html5
b.optional :readonly
b.use :label, class: 'col-sm-3 col-form-label'
b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
ba.use :input, class: 'form-select', error_class: 'is-invalid', valid_class: 'is-valid'
ba.use :full_error, wrap_with: { class: 'invalid-feedback' }
ba.use :hint, wrap_with: { class: 'form-text' }
end
end
# horizontal multi select
config.wrappers :horizontal_multi_select, class: 'row mb-3' do |b|
b.use :html5
b.optional :readonly
b.use :label, class: 'col-sm-3 col-form-label'
b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
ba.wrapper class: 'd-flex flex-row justify-content-between align-items-center' do |bb|
bb.use :input, class: 'form-select mx-1', error_class: 'is-invalid', valid_class: 'is-valid'
end
ba.use :full_error, wrap_with: { class: 'invalid-feedback d-block' }
ba.use :hint, wrap_with: { class: 'form-text' }
end
end
# horizontal range input
config.wrappers :horizontal_range, class: 'row mb-3' do |b|
b.use :html5
b.use :placeholder
b.optional :readonly
b.optional :step
b.use :label, class: 'col-sm-3 col-form-label pt-0'
b.wrapper :grid_wrapper, class: 'col-sm-9' do |ba|
ba.use :input, class: 'form-range', error_class: 'is-invalid', valid_class: 'is-valid'
ba.use :full_error, wrap_with: { class: 'invalid-feedback' }
ba.use :hint, wrap_with: { class: 'form-text' }
end
end
# inline forms
#
# inline default_wrapper
config.wrappers :inline_form, class: 'col-12' do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :minlength
b.optional :pattern
b.optional :min_max
b.optional :readonly
b.use :label, class: 'visually-hidden'
b.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
b.use :error, wrap_with: { class: 'invalid-feedback' }
b.optional :hint, wrap_with: { class: 'form-text' }
end
# inline input for boolean
config.wrappers :inline_boolean, class: 'col-12' do |b|
b.use :html5
b.optional :readonly
b.wrapper :form_check_wrapper, class: 'form-check' do |bb|
bb.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
bb.use :label, class: 'form-check-label'
bb.use :error, wrap_with: { class: 'invalid-feedback' }
bb.optional :hint, wrap_with: { class: 'form-text' }
end
end
# bootstrap custom forms
#
# custom input switch for boolean
config.wrappers :custom_boolean_switch, class: 'mb-3' do |b|
b.use :html5
b.optional :readonly
b.wrapper :form_check_wrapper, tag: 'div', class: 'form-check form-switch' do |bb|
bb.use :input, class: 'form-check-input', error_class: 'is-invalid', valid_class: 'is-valid'
bb.use :label, class: 'form-check-label'
bb.use :full_error, wrap_with: { tag: 'div', class: 'invalid-feedback' }
bb.use :hint, wrap_with: { class: 'form-text' }
end
end
# Input Group - custom component
# see example app and config at https://github.com/heartcombo/simple_form-bootstrap
config.wrappers :input_group, class: 'mb-3' do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :minlength
b.optional :pattern
b.optional :min_max
b.optional :readonly
b.use :label, class: 'form-label'
b.wrapper :input_group_tag, class: 'input-group' do |ba|
ba.optional :prepend
ba.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
ba.optional :append
ba.use :full_error, wrap_with: { class: 'invalid-feedback' }
end
b.use :hint, wrap_with: { class: 'form-text' }
end
# Floating Labels form
#
# floating labels default_wrapper
config.wrappers :floating_labels_form, class: 'form-floating mb-3' do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :minlength
b.optional :pattern
b.optional :min_max
b.optional :readonly
b.use :input, class: 'form-control', error_class: 'is-invalid', valid_class: 'is-valid'
b.use :label
b.use :full_error, wrap_with: { class: 'invalid-feedback' }
b.use :hint, wrap_with: { class: 'form-text' }
end
# custom multi select
config.wrappers :floating_labels_select, class: 'form-floating mb-3' do |b|
b.use :html5
b.optional :readonly
b.use :input, class: 'form-select', error_class: 'is-invalid', valid_class: 'is-valid'
b.use :label
b.use :full_error, wrap_with: { class: 'invalid-feedback' }
b.use :hint, wrap_with: { class: 'form-text' }
end
# The default wrapper to be used by the FormBuilder.
config.default_wrapper = :vertical_form
# Custom wrappers for input types. This should be a hash containing an input
# type as key and the wrapper that will be used for all inputs with specified type.
config.wrapper_mappings = {
boolean: :vertical_boolean,
check_boxes: :vertical_collection,
date: :vertical_multi_select,
datetime: :vertical_multi_select,
file: :vertical_file,
radio_buttons: :vertical_collection,
range: :vertical_range,
time: :vertical_multi_select,
select: :vertical_select
}
end
================================================
FILE: lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb
================================================
# frozen_string_literal: true
#
# Uncomment this and change the path if necessary to include your own
# components.
# See https://github.com/heartcombo/simple_form#custom-components to know
# more about custom components.
# Dir[Rails.root.join('lib/components/**/*.rb')].each { |f| require f }
#
# Use this setup block to configure all options available in SimpleForm.
SimpleForm.setup do |config|
# Don't forget to edit this file to adapt it to your needs (specially
# all the grid-related classes)
#
# Please note that hints are commented out by default since Foundation
# doesn't provide styles for hints. You will need to provide your own CSS styles for hints.
# Uncomment them to enable hints.
config.wrappers :vertical_form, class: :input, hint_class: :field_with_hint, error_class: :error, valid_class: :valid do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :minlength
b.optional :pattern
b.optional :min_max
b.optional :readonly
b.use :label_input
b.use :error, wrap_with: { tag: :small, class: :error }
# b.use :hint, wrap_with: { tag: :span, class: :hint }
end
config.wrappers :horizontal_form, tag: 'div', class: 'row', hint_class: :field_with_hint, error_class: :error, valid_class: :valid do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :minlength
b.optional :pattern
b.optional :min_max
b.optional :readonly
b.wrapper :label_wrapper, tag: :div, class: 'small-3 columns' do |ba|
ba.use :label, class: 'text-right inline'
end
b.wrapper :right_input_wrapper, tag: :div, class: 'small-9 columns' do |ba|
ba.use :input
ba.use :error, wrap_with: { tag: :small, class: :error }
# ba.use :hint, wrap_with: { tag: :span, class: :hint }
end
end
config.wrappers :horizontal_radio_and_checkboxes, tag: 'div', class: 'row' do |b|
b.use :html5
b.optional :readonly
b.wrapper :container_wrapper, tag: 'div', class: 'small-offset-3 small-9 columns' do |ba|
ba.wrapper tag: 'label', class: 'checkbox' do |bb|
bb.use :input
bb.use :label_text
end
ba.use :error, wrap_with: { tag: :small, class: :error }
# ba.use :hint, wrap_with: { tag: :span, class: :hint }
end
end
# Foundation does not provide a way to handle inline forms
# This wrapper can be used to create an inline form
# by hiding that labels on every screen sizes ('hidden-for-small-up').
#
# Note that you need to adapt this wrapper to your needs. If you need a 4
# columns form then change the wrapper class to 'small-3', if you need
# only two use 'small-6' and so on.
config.wrappers :inline_form, tag: 'div', class: 'column small-4', hint_class: :field_with_hint, error_class: :error, valid_class: :valid do |b|
b.use :html5
b.use :placeholder
b.optional :maxlength
b.optional :minlength
b.optional :pattern
b.optional :min_max
b.optional :readonly
b.use :label, class: 'hidden-for-small-up'
b.use :input
b.use :error, wrap_with: { tag: :small, class: :error }
# b.use :hint, wrap_with: { tag: :span, class: :hint }
end
# Examples of use:
# - wrapper_html: {class: 'row'}, custom_wrapper_html: {class: 'column small-12'}
# - custom_wrapper_html: {class: 'column small-3 end'}
config.wrappers :customizable_wrapper, tag: 'div', error_class: :error, valid_class: :valid do |b|
b.use :html5
b.optional :readonly
b.wrapper :custom_wrapper, tag: :div do |ba|
ba.use :label_input
end
b.use :error, wrap_with: { tag: :small, class: :error }
# b.use :hint, wrap_with: { tag: :span, class: :hint }
end
# CSS class for buttons
config.button_class = 'button'
# Set this to div to make the checkbox and radio properly work
# otherwise simple_form adds a label tag instead of a div around
# the nested label
config.item_wrapper_tag = :div
# CSS class to add for error notification helper.
config.error_notification_class = 'alert-box alert'
# The default wrapper to be used by the FormBuilder.
config.default_wrapper = :vertical_form
# Defines validation classes to the input_field. By default it's nil.
# config.input_field_valid_class = 'is-valid'
# config.input_field_error_class = 'is-invalid'
end
================================================
FILE: lib/generators/simple_form/templates/config/locales/simple_form.en.yml
================================================
en:
simple_form:
"yes": 'Yes'
"no": 'No'
required:
text: 'required'
mark: '*'
# You can uncomment the line below if you need to overwrite the whole required html.
# When using html, text and mark won't be used.
# html: '<abbr title="required">*</abbr>'
error_notification:
default_message: "Please review the problems below:"
# Examples
# labels:
# defaults:
# password: 'Password'
# user:
# new:
# email: 'E-mail to sign in.'
# edit:
# email: 'E-mail.'
# hints:
# defaults:
# username: 'User name to sign in.'
# password: 'No special characters, please.'
# include_blanks:
# defaults:
# age: 'Rather not say'
# prompts:
# defaults:
# age: 'Select your age'
================================================
FILE: lib/simple_form/action_view_extensions/builder.rb
================================================
# frozen_string_literal: true
module SimpleForm
module ActionViewExtensions
# A collection of methods required by simple_form but added to rails default form.
# This means that you can use such methods outside simple_form context.
module Builder
# Wrapper for using SimpleForm inside a default rails form.
# Example:
#
# form_for @user do |f|
# f.simple_fields_for :posts do |posts_form|
# # Here you have all simple_form methods available
# posts_form.input :title
# end
# end
def simple_fields_for(*args, &block)
options = args.extract_options!
options[:wrapper] = self.options[:wrapper] if options[:wrapper].nil?
options[:defaults] ||= self.options[:defaults]
options[:wrapper_mappings] ||= self.options[:wrapper_mappings]
if self.class < ActionView::Helpers::FormBuilder
options[:builder] ||= self.class
else
options[:builder] ||= SimpleForm::FormBuilder
end
fields_for(*args, options, &block)
end
end
end
end
module ActionView::Helpers
class FormBuilder
include SimpleForm::ActionViewExtensions::Builder
end
end
================================================
FILE: lib/simple_form/action_view_extensions/form_helper.rb
================================================
# frozen_string_literal: true
module SimpleForm
module ActionViewExtensions
# This module creates SimpleForm wrappers around default form_for and fields_for.
#
# Example:
#
# simple_form_for @user do |f|
# f.input :name, hint: 'My hint'
# end
#
module FormHelper
def simple_form_for(record, options = {}, &block)
options[:builder] ||= SimpleForm::FormBuilder
options[:html] ||= {}
unless options[:html].key?(:novalidate)
options[:html][:novalidate] = !SimpleForm.browser_validations
end
if options[:html].key?(:class)
options[:html][:class] = [SimpleForm.form_class, options[:html][:class]].compact
else
options[:html][:class] = [SimpleForm.form_class, SimpleForm.default_form_class, simple_form_css_class(record, options)].compact
end
with_simple_form_field_error_proc do
form_for(record, options, &block)
end
end
def simple_fields_for(record_name, record_object = nil, options = {}, &block)
options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
options[:builder] ||= SimpleForm::FormBuilder
with_simple_form_field_error_proc do
fields_for(record_name, record_object, options, &block)
end
end
private
def with_simple_form_field_error_proc
default_field_error_proc = ::ActionView::Base.field_error_proc
begin
::ActionView::Base.field_error_proc = SimpleForm.field_error_proc
yield
ensure
::ActionView::Base.field_error_proc = default_field_error_proc
end
end
def simple_form_css_class(record, options)
html_options = options[:html]
as = options[:as]
if html_options.key?(:class)
html_options[:class]
elsif record.is_a?(String) || record.is_a?(Symbol)
as || record
else
record = record.last if record.is_a?(Array)
action = record.respond_to?(:persisted?) && record.persisted? ? :edit : :new
as ? "#{action}_#{as}" : dom_class(record, action)
end
end
end
end
end
ActiveSupport.on_load(:action_view) do
include SimpleForm::ActionViewExtensions::FormHelper
end
================================================
FILE: lib/simple_form/components/errors.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Components
module Errors
def error(wrapper_options = nil)
error_text if has_errors?
end
def full_error(wrapper_options = nil)
full_error_text if options[:error] != false && has_errors?
end
def has_errors?
object_with_errors? || !object && has_custom_error?
end
def has_value?
object && object.respond_to?(attribute_name) && object.send(attribute_name).present?
end
def valid?
!has_errors? && has_value?
end
protected
def error_text
text = has_custom_error? ? options[:error] : errors.send(error_method)
"#{html_escape(options[:error_prefix])} #{html_escape(text)}".lstrip.html_safe
end
def full_error_text
has_custom_error? ? options[:error] : full_errors.send(error_method)
end
def object_with_errors?
object && object.respond_to?(:errors) && errors.present?
end
def error_method
options[:error_method] || SimpleForm.error_method
end
def errors
@errors ||= (errors_on_attribute + errors_on_association).compact
end
def full_errors
@full_errors ||= (full_errors_on_attribute + full_errors_on_association).compact
end
def errors_on_attribute
object.errors[attribute_name] || []
end
def full_errors_on_attribute
object.errors.full_messages_for(attribute_name)
end
def errors_on_association
reflection ? object.errors[reflection.name] : []
end
def full_errors_on_association
reflection ? object.errors.full_messages_for(reflection.name) : []
end
def has_custom_error?
options[:error].is_a?(String)
end
end
end
end
================================================
FILE: lib/simple_form/components/hints.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Components
# Needs to be enabled in order to do automatic lookups.
module Hints
def hint(wrapper_options = nil)
@hint ||= begin
hint = options[:hint]
if hint.is_a?(String)
html_escape(hint)
else
content = translate_from_namespace(:hints)
content.html_safe if content
end
end
end
def has_hint?
options[:hint] != false && hint.present?
end
end
end
end
================================================
FILE: lib/simple_form/components/html5.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Components
module HTML5
def initialize(*)
@html5 = false
end
def html5(wrapper_options = nil)
@html5 = true
input_html_options[:required] = input_html_required_option
input_html_options[:'aria-invalid'] = has_errors? || nil
nil
end
def html5?
@html5
end
def input_html_required_option
!options[:required].nil? ? required_field? : has_required?
end
def has_required?
# We need to check browser_validations because
# some browsers are still checking required even
# if novalidate was given.
required_field? && SimpleForm.browser_validations
end
end
end
end
================================================
FILE: lib/simple_form/components/label_input.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Components
module LabelInput
extend ActiveSupport::Concern
included do
include SimpleForm::Components::Labels
end
def label_input(wrapper_options = nil)
if options[:label] == false
deprecated_component(:input, wrapper_options)
else
deprecated_component(:label, wrapper_options) + deprecated_component(:input, wrapper_options)
end
end
private
def deprecated_component(namespace, wrapper_options)
method = method(namespace)
if method.arity.zero?
SimpleForm.deprecator.warn(SimpleForm::CUSTOM_INPUT_DEPRECATION_WARN % { name: namespace })
method.call
else
method.call(wrapper_options)
end
end
end
end
end
================================================
FILE: lib/simple_form/components/labels.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Components
module Labels
extend ActiveSupport::Concern
module ClassMethods #:nodoc:
def translate_required_html
I18n.t(:"required.html", scope: i18n_scope, default:
%(<abbr title="#{translate_required_text}">#{translate_required_mark}</abbr>)
)
end
def translate_required_text
I18n.t(:"required.text", scope: i18n_scope, default: 'required')
end
def translate_required_mark
I18n.t(:"required.mark", scope: i18n_scope, default: '*')
end
private
def i18n_scope
SimpleForm.i18n_scope
end
end
def label(wrapper_options = nil)
label_options = merge_wrapper_options(label_html_options, wrapper_options)
if generate_label_for_attribute?
@builder.label(label_target, label_text, label_options)
else
template.label_tag(nil, label_text, label_options)
end
end
def label_text(wrapper_options = nil)
label_text = options[:label_text] || SimpleForm.label_text
label_text.call(html_escape(raw_label_text), required_label_text, options[:label].present?).strip.html_safe
end
def label_target
attribute_name
end
def label_html_options
label_html_classes = SimpleForm.additional_classes_for(:label) {
[input_type, required_class, disabled_class, SimpleForm.label_class].compact
}
label_options = html_options_for(:label, label_html_classes)
if options.key?(:input_html) && options[:input_html].key?(:id)
label_options[:for] = options[:input_html][:id]
end
label_options
end
protected
def raw_label_text #:nodoc:
options[:label] || label_translation
end
# Default required text when attribute is required.
def required_label_text #:nodoc:
required_field? ? self.class.translate_required_html.dup : ''
end
# First check labels translation and then human attribute name.
def label_translation #:nodoc:
if SimpleForm.translate_labels && (translated_label = translate_from_namespace(:labels))
translated_label
elsif object.class.respond_to?(:human_attribute_name)
object.class.human_attribute_name(reflection_or_attribute_name.to_s, { base: object })
else
attribute_name.to_s.humanize
end
end
def generate_label_for_attribute?
true
end
end
end
end
================================================
FILE: lib/simple_form/components/maxlength.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Components
# Needs to be enabled in order to do automatic lookups.
module Maxlength
def maxlength(wrapper_options = nil)
input_html_options[:maxlength] ||= maximum_length_from_validation || limit
nil
end
private
def maximum_length_from_validation
maxlength = options[:maxlength]
if maxlength.is_a?(String) || maxlength.is_a?(Integer)
maxlength
else
length_validator = find_length_validator
maximum_length_value_from(length_validator)
end
end
def find_length_validator
find_validator(:length)
end
def maximum_length_value_from(length_validator)
if length_validator
value = length_validator.options[:is] || length_validator.options[:maximum]
resolve_validator_value(value)
end
end
end
end
end
================================================
FILE: lib/simple_form/components/min_max.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Components
module MinMax
def min_max(wrapper_options = nil)
if numeric_validator = find_numericality_validator
validator_options = numeric_validator.options
input_html_options[:min] ||= minimum_value(validator_options)
input_html_options[:max] ||= maximum_value(validator_options)
end
nil
end
private
def integer?
input_type == :integer
end
def minimum_value(validator_options)
if integer? && validator_options.key?(:greater_than)
resolve_validator_value(validator_options[:greater_than]) + 1
else
resolve_validator_value(validator_options[:greater_than_or_equal_to])
end
end
def maximum_value(validator_options)
if integer? && validator_options.key?(:less_than)
resolve_validator_value(validator_options[:less_than]) - 1
else
resolve_validator_value(validator_options[:less_than_or_equal_to])
end
end
def find_numericality_validator
find_validator(:numericality)
end
end
end
end
================================================
FILE: lib/simple_form/components/minlength.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Components
# Needs to be enabled in order to do automatic lookups.
module Minlength
def minlength(wrapper_options = nil)
input_html_options[:minlength] ||= minimum_length_from_validation
nil
end
private
def minimum_length_from_validation
minlength = options[:minlength]
if minlength.is_a?(String) || minlength.is_a?(Integer)
minlength
else
length_validator = find_length_validator
minimum_length_value_from(length_validator)
end
end
def find_length_validator
find_validator(:length)
end
def minimum_length_value_from(length_validator)
if length_validator
value = length_validator.options[:is] || length_validator.options[:minimum]
resolve_validator_value(value)
end
end
end
end
end
================================================
FILE: lib/simple_form/components/pattern.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Components
# Needs to be enabled in order to do automatic lookups.
module Pattern
def pattern(wrapper_options = nil)
input_html_options[:pattern] ||= pattern_source
nil
end
private
def pattern_source
pattern = options[:pattern]
if pattern.is_a?(String)
pattern
elsif (pattern_validator = find_pattern_validator) && (with = pattern_validator.options[:with])
resolve_validator_value(with).source
end
end
def find_pattern_validator
find_validator(:format)
end
end
end
end
================================================
FILE: lib/simple_form/components/placeholders.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Components
# Needs to be enabled in order to do automatic lookups.
module Placeholders
def placeholder(wrapper_options = nil)
input_html_options[:placeholder] ||= placeholder_text
nil
end
def placeholder_text(wrapper_options = nil)
placeholder = options[:placeholder]
placeholder.is_a?(String) ? placeholder : translate_from_namespace(:placeholders)
end
end
end
end
================================================
FILE: lib/simple_form/components/readonly.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Components
# Needs to be enabled in order to do automatic lookups.
module Readonly
def readonly(wrapper_options = nil)
if readonly_attribute? && !has_readonly?
input_html_options[:readonly] ||= true
input_html_classes << :readonly
end
nil
end
private
def readonly_attribute?
object.class.respond_to?(:readonly_attributes) &&
object.persisted? &&
object.class.readonly_attributes.include?(attribute_name.to_s)
end
end
end
end
================================================
FILE: lib/simple_form/components.rb
================================================
# frozen_string_literal: true
module SimpleForm
# Components are a special type of helpers that can work on their own.
# For example, by using a component, it will automatically change the
# output under given circumstances without user input. For example,
# the disabled helper always need a disabled: true option given
# to the input in order to be enabled. On the other hand, things like
# hints can generate output automatically by doing I18n lookups.
module Components
extend ActiveSupport::Autoload
autoload :Errors
autoload :Hints
autoload :HTML5
autoload :LabelInput
autoload :Labels
autoload :MinMax
autoload :Maxlength
autoload :Minlength
autoload :Pattern
autoload :Placeholders
autoload :Readonly
end
end
================================================
FILE: lib/simple_form/error_notification.rb
================================================
# frozen_string_literal: true
module SimpleForm
class ErrorNotification
delegate :object, :object_name, :template, to: :@builder
def initialize(builder, options)
@builder = builder
@message = options.delete(:message)
@options = options
end
def render
if has_errors?
template.content_tag(error_notification_tag, error_message, html_options)
end
end
protected
def errors
object.errors
end
def has_errors?
object && object.respond_to?(:errors) && errors.present?
end
def error_message
(@message || translate_error_notification).html_safe
end
def error_notification_tag
SimpleForm.error_notification_tag
end
def html_options
@options[:class] = "#{SimpleForm.error_notification_class} #{@options[:class]}".strip
@options
end
def translate_error_notification
lookups = []
lookups << :"#{object_name}"
lookups << :default_message
lookups << "Please review the problems below:"
I18n.t(lookups.shift, scope: :"simple_form.error_notification", default: lookups)
end
end
end
================================================
FILE: lib/simple_form/form_builder.rb
================================================
# frozen_string_literal: true
require 'active_support/core_ext/object/deep_dup'
require 'simple_form/map_type'
require 'simple_form/tags'
module SimpleForm
class FormBuilder < ActionView::Helpers::FormBuilder
attr_reader :template, :object_name, :object, :wrapper
# When action is create or update, we still should use new and edit
ACTIONS = {
'create' => 'new',
'update' => 'edit'
}
ATTRIBUTE_COMPONENTS = %i[html5 min_max maxlength minlength placeholder pattern readonly]
extend MapType
include SimpleForm::Inputs
map_type :text, :hstore, :json, :jsonb, to: SimpleForm::Inputs::TextInput
map_type :file, to: SimpleForm::Inputs::FileInput
map_type :string, :email, :search, :tel, :url, :uuid, :citext, to: SimpleForm::Inputs::StringInput
map_type :password, to: SimpleForm::Inputs::PasswordInput
map_type :integer, :decimal, :float, to: SimpleForm::Inputs::NumericInput
map_type :range, to: SimpleForm::Inputs::RangeInput
map_type :check_boxes, to: SimpleForm::Inputs::CollectionCheckBoxesInput
map_type :radio_buttons, to: SimpleForm::Inputs::CollectionRadioButtonsInput
map_type :rich_text_area, to: SimpleForm::Inputs::RichTextAreaInput
map_type :select, to: SimpleForm::Inputs::CollectionSelectInput
map_type :grouped_select, to: SimpleForm::Inputs::GroupedCollectionSelectInput
map_type :date, :time, :datetime, to: SimpleForm::Inputs::DateTimeInput
map_type :country, :time_zone, to: SimpleForm::Inputs::PriorityInput
map_type :boolean, to: SimpleForm::Inputs::BooleanInput
map_type :hidden, to: SimpleForm::Inputs::HiddenInput
def self.discovery_cache
@discovery_cache ||= {}
end
def initialize(*) #:nodoc:
super
@object = convert_to_model(@object)
@defaults = options[:defaults]
@wrapper = SimpleForm.wrapper(options[:wrapper] || SimpleForm.default_wrapper)
end
# Basic input helper, combines all components in the stack to generate
# input html based on options the user define and some guesses through
# database column information. By default a call to input will generate
# label + input + hint (when defined) + errors (when exists), and all can
# be configured inside a wrapper html.
#
# If a block is given, the contents of the block will replace the input
# field that would otherwise be generated automatically. The content will
# be given a label and wrapper div to make it consistent with the other
# elements in the form.
#
# == Examples
#
# # Imagine @user has error "can't be blank" on name
# simple_form_for @user do |f|
# f.input :name, hint: 'My hint'
# end
#
# This is the output html (only the input portion, not the form):
#
# <label class="string required" for="user_name">
# <abbr title="required">*</abbr> Super User Name!
# </label>
# <input class="string required" id="user_name" maxlength="100"
# name="user[name]" type="text" value="Carlos" />
# <span class="hint">My hint</span>
# <span class="error">can't be blank</span>
#
# Each database type will render a default input, based on some mappings and
# heuristic to determine which is the best option.
#
# You have some options for the input to enable/disable some functions:
#
# as: allows you to define the input type you want, for instance you
# can use it to generate a text field for a date column.
#
# required: defines whether this attribute is required or not. True
# by default.
#
# The fact SimpleForm is built in components allow the interface to be unified.
# So, for instance, if you need to disable :hint for a given input, you can pass
# hint: false. The same works for :error, :label and :wrapper.
#
# Besides the html for any component can be changed. So, if you want to change
# the label html you just need to give a hash to :label_html. To configure the
# input html, supply :input_html instead and so on.
#
# == Options
#
# Some inputs, as datetime, time and select allow you to give extra options, like
# prompt and/or include blank. Such options are given in plainly:
#
# f.input :created_at, include_blank: true
#
# == Collection
#
# When playing with collections (:radio_buttons, :check_boxes and :select
# inputs), you have three extra options:
#
# collection: use to determine the collection to generate the radio or select
#
# label_method: the method to apply on the array collection to get the label
#
# value_method: the method to apply on the array collection to get the value
#
# == Priority
#
# Some inputs, as :time_zone and :country accepts a :priority option. If none is
# given SimpleForm.time_zone_priority and SimpleForm.country_priority are used respectively.
#
def input(attribute_name, options = {}, &block)
options = @defaults.deep_dup.deep_merge(options) if @defaults
input = find_input(attribute_name, options, &block)
wrapper = find_wrapper(input.input_type, options)
wrapper.render input
end
alias :attribute :input
# Creates a input tag for the given attribute. All the given options
# are sent as :input_html.
#
# == Examples
#
# simple_form_for @user do |f|
# f.input_field :name
# end
#
# This is the output html (only the input portion, not the form):
#
# <input class="string required" id="user_name" maxlength="100"
# name="user[name]" type="text" value="Carlos" />
#
# It also support validation classes once it is configured.
#
# # config/initializers/simple_form.rb
# SimpleForm.setup do |config|
# config.input_field_valid_class = 'is-valid'
# config.input_field_error_class = 'is-invalid'
# end
#
# simple_form_for @user do |f|
# f.input_field :name
# end
#
# When the validation happens, the input will be rendered with
# the class configured according to the validation:
#
# - when the input is valid:
#
# <input class="is-valid string required" id="user_name" value="Carlos" />
#
# - when the input is invalid:
#
# <input class="is-invalid string required" id="user_name" value="" />
#
def input_field(attribute_name, options = {})
components = (wrapper.components.map(&:namespace) & ATTRIBUTE_COMPONENTS)
options = options.dup
options[:input_html] = options.except(:as, :boolean_style, :collection, :disabled, :label_method, :value_method, :prompt, *components)
options = @defaults.deep_dup.deep_merge(options) if @defaults
input = find_input(attribute_name, options)
wrapper = find_wrapper(input.input_type, options)
components = build_input_field_components(components.push(:input))
SimpleForm::Wrappers::Root.new(components, wrapper.options.merge(wrapper: false)).render input
end
# Helper for dealing with association selects/radios, generating the
# collection automatically. It's just a wrapper to input, so all options
# supported in input are also supported by association. Some extra options
# can also be given:
#
# == Examples
#
# simple_form_for @user do |f|
# f.association :company # Company.all
# end
#
# f.association :company, collection: Company.all(order: 'name')
# # Same as using :order option, but overriding collection
#
# == Block
#
# When a block is given, association simple behaves as a proxy to
# simple_fields_for:
#
# f.association :company do |c|
# c.input :name
# c.input :type
# end
#
# From the options above, only :collection can also be supplied.
#
# Please note that the association helper is currently only tested with Active Record. Depending on the ORM you are using your mileage may vary.
#
def association(association, options = {}, &block)
options = options.dup
return simple_fields_for(*[association,
options.delete(:collection), options].compact, &block) if block_given?
raise ArgumentError, "Association cannot be used in forms not associated with an object" unless @object
reflection = find_association_reflection(association)
raise "Association #{association.inspect} not found" unless reflection
options[:as] ||= :select
options[:collection] ||= fetch_association_collection(reflection, options)
attribute = build_association_attribute(reflection, association, options)
input(attribute, options.merge(reflection: reflection))
end
# Creates a button:
#
# form_for @user do |f|
# f.button :submit
# end
#
# It just acts as a proxy to method name given. We also alias original Rails
# button implementation (3.2 forward (to delegate to the original when
# calling `f.button :button`.
#
alias_method :button_button, :button
def button(type, *args, &block)
options = args.extract_options!.dup
options[:class] = [SimpleForm.button_class, options[:class]].compact
args << options
if respond_to?(:"#{type}_button")
send(:"#{type}_button", *args, &block)
else
send(type, *args, &block)
end
end
# Creates an error tag based on the given attribute, only when the attribute
# contains errors. All the given options are sent as :error_html.
#
# == Examples
#
# f.error :name
# f.error :name, id: "cool_error"
#
def error(attribute_name, options = {})
options = options.dup
options[:error_html] = options.except(:error_tag, :error_prefix, :error_method)
column = find_attribute_column(attribute_name)
input_type = default_input_type(attribute_name, column, options)
wrapper.find(:error).
render(SimpleForm::Inputs::Base.new(self, attribute_name, column, input_type, options))
end
# Return the error but also considering its name. This is used
# when errors for a hidden field need to be shown.
#
# == Examples
#
# f.full_error :token #=> <span class="error">Token is invalid</span>
#
def full_error(attribute_name, options = {})
options = options.dup
options[:error_prefix] ||= if object.class.respond_to?(:human_attribute_name)
object.class.human_attribute_name(attribute_name.to_s, { base: object })
else
attribute_name.to_s.humanize
end
error(attribute_name, options)
end
# Creates a hint tag for the given attribute. Accepts a symbol indicating
# an attribute for I18n lookup or a string. All the given options are sent
# as :hint_html.
#
# == Examples
#
# f.hint :name # Do I18n lookup
# f.hint :name, id: "cool_hint"
# f.hint "Don't forget to accept this"
#
def hint(attribute_name, options = {})
options = options.dup
options[:hint_html] = options.except(:hint_tag, :hint)
if attribute_name.is_a?(String)
options[:hint] = attribute_name
attribute_name, column, input_type = nil, nil, nil
else
column = find_attribute_column(attribute_name)
input_type = default_input_type(attribute_name, column, options)
end
wrapper.find(:hint).
render(SimpleForm::Inputs::Base.new(self, attribute_name, column, input_type, options))
end
# Creates a default label tag for the given attribute. You can give a label
# through the :label option or using i18n. All the given options are sent
# as :label_html.
#
# == Examples
#
# f.label :name # Do I18n lookup
# f.label :name, "Name" # Same behavior as Rails, do not add required tag
# f.label :name, label: "Name" # Same as above, but adds required tag
#
# f.label :name, required: false
# f.label :name, id: "cool_label"
#
def label(attribute_name, *args)
return super if args.first.is_a?(String) || block_given?
options = args.extract_options!.dup
options[:label_html] = options.except(:label, :label_text, :required, :as)
column = find_attribute_column(attribute_name)
input_type = default_input_type(attribute_name, column, options)
SimpleForm::Inputs::Base.new(self, attribute_name, column, input_type, options).label
end
# Creates an error notification message that only appears when the form object
# has some error. You can give a specific message with the :message option,
# otherwise it will look for a message using I18n. All other options given are
# passed straight as html options to the html tag.
#
# == Examples
#
# f.error_notification
# f.error_notification message: 'Something went wrong'
# f.error_notification id: 'user_error_message', class: 'form_error'
#
def error_notification(options = {})
SimpleForm::ErrorNotification.new(self, options).render
end
# Create a collection of radio inputs for the attribute. Basically this
# helper will create a radio input associated with a label for each
# text/value option in the collection, using value_method and text_method
# to convert these text/value. You can give a symbol or a proc to both
# value_method and text_method, that will be evaluated for each item in
# the collection.
#
# == Examples
#
# form_for @user do |f|
# f.collection_radio_buttons :options, [[true, 'Yes'] ,[false, 'No']], :first, :last
# end
#
# <input id="user_options_true" name="user[options]" type="radio" value="true" />
# <label class="collection_radio_buttons" for="user_options_true">Yes</label>
# <input id="user_options_false" name="user[options]" type="radio" value="false" />
# <label class="collection_radio_buttons" for="user_options_false">No</label>
#
# It is also possible to give a block that should generate the radio +
# label. To wrap the radio with the label, for instance:
#
# form_for @user do |f|
# f.collection_radio_buttons(
# :options, [[true, 'Yes'] ,[false, 'No']], :first, :last
# ) do |b|
# b.label { b.radio_button + b.text }
# end
# end
#
# == Options
#
# Collection radio accepts some extra options:
#
# * checked => the value that should be checked initially.
#
# * disabled => the value or values that should be disabled. Accepts a single
# item or an array of items.
#
# * collection_wrapper_tag => the tag to wrap the entire collection.
#
# * collection_wrapper_class => the CSS class to use for collection_wrapper_tag
#
# * item_wrapper_tag => the tag to wrap each item in the collection.
#
# * item_wrapper_class => the CSS class to use for item_wrapper_tag
#
# * a block => to generate the label + radio or any other component.
def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
SimpleForm::Tags::CollectionRadioButtons.new(@object_name, method, @template, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options)).render(&block)
end
# Creates a collection of check boxes for each item in the collection,
# associated with a clickable label. Use value_method and text_method to
# convert items in the collection for use as text/value in check boxes.
# You can give a symbol or a proc to both value_method and text_method,
# that will be evaluated for each item in the collection.
#
# == Examples
#
# form_for @user do |f|
# f.collection_check_boxes :options, [[true, 'Yes'] ,[false, 'No']], :first, :last
# end
#
# <input name="user[options][]" type="hidden" value="" />
# <input id="user_options_true" name="user[options][]" type="checkbox" value="true" />
# <label class="collection_check_boxes" for="user_options_true">Yes</label>
# <input name="user[options][]" type="hidden" value="" />
# <input id="user_options_false" name="user[options][]" type="checkbox" value="false" />
# <label class="collection_check_boxes" for="user_options_false">No</label>
#
# It is also possible to give a block that should generate the check box +
# label. To wrap the check box with the label, for instance:
#
# form_for @user do |f|
# f.collection_check_boxes(
# :options, [[true, 'Yes'] ,[false, 'No']], :first, :last
# ) do |b|
# b.label { b.check_box + b.text }
# end
# end
#
# == Options
#
# Collection check box accepts some extra options:
#
# * checked => the value or values that should be checked initially. Accepts
# a single item or an array of items. It overrides existing associations.
#
# * disabled => the value or values that should be disabled. Accepts a single
# item or an array of items.
#
# * collection_wrapper_tag => the tag to wrap the entire collection.
#
# * collection_wrapper_class => the CSS class to use for collection_wrapper_tag. This option
# is ignored if the :collection_wrapper_tag option is blank.
#
# * item_wrapper_tag => the tag to wrap each item in the collection.
#
# * item_wrapper_class => the CSS class to use for item_wrapper_tag
#
# * a block => to generate the label + check box or any other component.
def collection_check_boxes(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
SimpleForm::Tags::CollectionCheckBoxes.new(@object_name, method, @template, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options)).render(&block)
end
# Extract the model names from the object_name mess, ignoring numeric and
# explicit child indexes.
#
# Example:
#
# route[blocks_attributes][0][blocks_learning_object_attributes][1][foo_attributes]
# ["route", "blocks", "blocks_learning_object", "foo"]
#
def lookup_model_names #:nodoc:
@lookup_model_names ||= begin
child_index = options[:child_index]
names = object_name.to_s.scan(/(?!\d)\w+/).flatten
names.delete(child_index) if child_index
names.each { |name| name.gsub!('_attributes', '') }
names.freeze
end
end
# The action to be used in lookup.
def lookup_action #:nodoc:
@lookup_action ||= begin
action = template.controller && template.controller.action_name
return unless action
action = action.to_s
ACTIONS[action] || action
end
end
private
def fetch_association_collection(reflection, options)
options.fetch(:collection) do
relation = reflection.klass.all
if reflection.respond_to?(:scope) && reflection.scope
if reflection.scope.parameters.any?
relation = reflection.klass.instance_exec(object, &reflection.scope)
else
relation = reflection.klass.instance_exec(&reflection.scope)
end
else
order = reflection.options[:order]
conditions = reflection.options[:conditions]
conditions = object.instance_exec(&conditions) if conditions.respond_to?(:call)
relation = relation.where(conditions) if relation.respond_to?(:where) && conditions.present?
relation = relation.order(order) if relation.respond_to?(:order)
end
relation
end
end
def build_association_attribute(reflection, association, options)
case reflection.macro
when :belongs_to
(reflection.respond_to?(:options) && reflection.options[:foreign_key]) || :"#{reflection.name}_id"
when :has_one
raise ArgumentError, ":has_one associations are not supported by f.association"
else
if options[:as] == :select || options[:as] == :grouped_select
html_options = options[:input_html] ||= {}
html_options[:multiple] = true unless html_options.key?(:multiple)
end
# Force the association to be preloaded for performance.
if options[:preload] != false && object.respond_to?(association)
target = object.send(association)
target.to_a if target.respond_to?(:to_a)
end
:"#{reflection.name.to_s.singularize}_ids"
end
end
# Find an input based on the attribute name.
def find_input(attribute_name, options = {}, &block)
column = find_attribute_column(attribute_name)
input_type = default_input_type(attribute_name, column, options)
if block_given?
SimpleForm::Inputs::BlockInput.new(self, attribute_name, column, input_type, options, &block)
else
find_mapping(input_type).new(self, attribute_name, column, input_type, options)
end
end
# Attempt to guess the better input type given the defined options. By
# default always fallback to the user :as option, or to a :select when a
# collection is given.
def default_input_type(attribute_name, column, options)
return options[:as].to_sym if options[:as]
custom_type = find_custom_type(attribute_name.to_s) and return custom_type
return :select if options[:collection]
input_type = column.try(:type)
case input_type
when :timestamp
:datetime
when :string, :citext, nil
case attribute_name.to_s
when /(?:\b|\W|_)password(?:\b|\W|_)/ then :password
when /(?:\b|\W|_)time_zone(?:\b|\W|_)/ then :time_zone
when /(?:\b|\W|_)country(?:\b|\W|_)/ then :country
when /(?:\b|\W|_)email(?:\b|\W|_)/ then :email
when /(?:\b|\W|_)phone(?:\b|\W|_)/ then :tel
when /(?:\b|\W|_)url(?:\b|\W|_)/ then :url
else
file_method?(attribute_name) ? :file : (input_type || :string)
end
else
input_type
end
end
def find_custom_type(attribute_name)
SimpleForm.input_mappings.find { |match, type|
attribute_name =~ match
}.try(:last) if SimpleForm.input_mappings
end
# Internal: Try to discover whether an attribute corresponds to a file or not.
#
# Most upload Gems add some kind of attributes to the ActiveRecord's model they are included in.
# This method tries to guess if an attribute belongs to some of these Gems by checking the presence
# of their methods using `#respond_to?`.
#
# Note: This does not support multiple file upload inputs, as this is very application-specific.
#
# The order here was chosen based on the popularity of Gems:
#
# - `#{attribute_name}_attachment` - ActiveStorage >= `5.2` and Refile >= `0.2.0` <= `0.4.0`
# - `remote_#{attribute_name}_url` - Refile >= `0.3.0` and CarrierWave >= `0.2.2`
# - `#{attribute_name}_attacher` - Refile >= `0.4.0` and Shrine >= `0.9.0`
# - `#{attribute_name}_file_name` - Paperclip ~> `2.0` (added for backwards compatibility)
#
# Returns a Boolean.
def file_method?(attribute_name)
@object.respond_to?("#{attribute_name}_attachment") ||
@object.respond_to?("#{attribute_name}_attachments") ||
@object.respond_to?("remote_#{attribute_name}_url") ||
@object.respond_to?("#{attribute_name}_attacher") ||
@object.respond_to?("#{attribute_name}_file_name")
end
def find_attribute_column(attribute_name)
if @object.respond_to?(:type_for_attribute) && @object.has_attribute?(attribute_name)
detected_type = @object.type_for_attribute(attribute_name.to_s)
# Some attributes like ActiveRecord::Encryption::EncryptedAttribute are detected
# as different type, in that case we need to use the original type
detected_type.respond_to?(:cast_type) ? detected_type.cast_type : detected_type
elsif @object.respond_to?(:column_for_attribute) && @object.has_attribute?(attribute_name)
@object.column_for_attribute(attribute_name)
end
end
def find_association_reflection(association)
if @object.class.respond_to?(:reflect_on_association)
@object.class.reflect_on_association(association)
end
end
# Attempts to find a mapping. It follows the following rules:
#
# 1) It tries to find a registered mapping, if succeeds:
# a) Try to find an alternative with the same name in the Object scope
# b) Or use the found mapping
# 2) If not, fallbacks to #{input_type}Input
# 3) If not, fallbacks to SimpleForm::Inputs::#{input_type}Input
def find_mapping(input_type)
discovery_cache[input_type] ||=
if mapping = self.class.mappings[input_type]
mapping_override(mapping) || mapping
else
camelized = "#{input_type.to_s.camelize}Input"
attempt_mapping_with_custom_namespace(camelized) ||
attempt_mapping(camelized, Object) ||
attempt_mapping(camelized, self.class) ||
raise("No input found for #{input_type}")
end
end
# Attempts to find a wrapper mapping. It follows the following rules:
#
# 1) It tries to find a wrapper for the current form
# 2) If not, it tries to find a config
def find_wrapper_mapping(input_type)
if options[:wrapper_mappings] && options[:wrapper_mappings][input_type]
options[:wrapper_mappings][input_type]
else
SimpleForm.wrapper_mappings && SimpleForm.wrapper_mappings[input_type]
end
end
def find_wrapper(input_type, options)
if name = options[:wrapper] || find_wrapper_mapping(input_type)
name.respond_to?(:render) ? name : SimpleForm.wrapper(name)
else
wrapper
end
end
# If cache_discovery is enabled, use the class level cache that persists
# between requests, otherwise use the instance one.
def discovery_cache
if SimpleForm.cache_discovery
self.class.discovery_cache
else
@discovery_cache ||= {}
end
end
def mapping_override(klass)
name = klass.name
if name =~ /^SimpleForm::Inputs/
input_name = name.split("::").last
attempt_mapping_with_custom_namespace(input_name) ||
attempt_mapping(input_name, Object)
end
end
def attempt_mapping(mapping, at)
return if SimpleForm.inputs_discovery == false && at == Object
begin
at.const_get(mapping)
rescue NameError => e
raise unless e.message.include?(mapping)
end
end
def attempt_mapping_with_custom_namespace(input_name)
SimpleForm.custom_inputs_namespaces.each do |namespace|
if (mapping = attempt_mapping(input_name, namespace.constantize))
return mapping
end
end
nil
end
def build_input_field_components(components)
components.map do |component|
if component == :input
SimpleForm::Wrappers::Leaf.new(component, build_input_field_options)
else
SimpleForm::Wrappers::Leaf.new(component)
end
end
end
def build_input_field_options
input_field_options = {}
valid_class = SimpleForm.input_field_valid_class
error_class = SimpleForm.input_field_error_class
if error_class.present?
input_field_options[:error_class] = error_class
end
if valid_class.present?
input_field_options[:valid_class] = valid_class
end
input_field_options
end
end
end
================================================
FILE: lib/simple_form/helpers/autofocus.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Helpers
module Autofocus
private
def has_autofocus?
options[:autofocus] == true
end
end
end
end
================================================
FILE: lib/simple_form/helpers/disabled.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Helpers
module Disabled
private
def has_disabled?
options[:disabled] == true
end
def disabled_class
:disabled if has_disabled?
end
end
end
end
================================================
FILE: lib/simple_form/helpers/readonly.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Helpers
module Readonly
private
def readonly_class
:readonly if has_readonly?
end
def has_readonly?
options[:readonly] == true
end
end
end
end
================================================
FILE: lib/simple_form/helpers/required.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Helpers
module Required
private
def required_field?
@required
end
def calculate_required
if !options[:required].nil?
options[:required]
elsif has_validators?
required_by_validators?
else
required_by_default?
end
end
def required_by_validators?
(attribute_validators + reflection_validators).any? { |v| v.kind == :presence && valid_validator?(v) }
end
def required_by_default?
SimpleForm.required_by_default
end
# Do not use has_required? because we want to add the class
# regardless of the required option.
def required_class
required_field? ? :required : :optional
end
end
end
end
================================================
FILE: lib/simple_form/helpers/validators.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Helpers
module Validators
def has_validators?
@has_validators ||= attribute_name && object.class.respond_to?(:validators_on)
end
private
def attribute_validators
object.class.validators_on(attribute_name)
end
def reflection_validators
reflection ? object.class.validators_on(reflection.name) : []
end
def valid_validator?(validator)
!conditional_validators?(validator) && action_validator_match?(validator)
end
def conditional_validators?(validator)
validator.options.include?(:if) || validator.options.include?(:unless)
end
def action_validator_match?(validator)
return true unless validator.options.include?(:on)
case validator.options[:on]
when :save
true
when :create
!object.persisted?
when :update
object.persisted?
end
end
def find_validator(kind)
attribute_validators.find { |v| v.kind == kind } if has_validators?
end
# Implements `ActiveModel::Validations::ResolveValue`, introduced by Rails 7.1.
# https://github.com/rails/rails/blob/v7.1.0/activemodel/lib/active_model/validations/resolve_value.rb
def resolve_validator_value(value)
case value
when Proc
if value.arity == 0
value.call
else
value.call(object)
end
when Symbol
object.send(value)
else
if value.respond_to?(:call)
value.call(object)
else
value
end
end
end
end
end
end
================================================
FILE: lib/simple_form/helpers.rb
================================================
# frozen_string_literal: true
module SimpleForm
# Helpers are made of several helpers that cannot be turned on automatically.
# For instance, disabled cannot be turned on automatically, it requires the
# user to explicitly pass the option disabled: true so it may work.
module Helpers
autoload :Autofocus, 'simple_form/helpers/autofocus'
autoload :Disabled, 'simple_form/helpers/disabled'
autoload :Readonly, 'simple_form/helpers/readonly'
autoload :Required, 'simple_form/helpers/required'
autoload :Validators, 'simple_form/helpers/validators'
end
end
================================================
FILE: lib/simple_form/inputs/base.rb
================================================
# frozen_string_literal: true
require 'active_support/core_ext/string/output_safety'
require 'action_view/helpers'
module SimpleForm
module Inputs
class Base
include ERB::Util
include ActionView::Helpers::TranslationHelper
include SimpleForm::Helpers::Autofocus
include SimpleForm::Helpers::Disabled
include SimpleForm::Helpers::Readonly
include SimpleForm::Helpers::Required
include SimpleForm::Helpers::Validators
include SimpleForm::Components::Errors
include SimpleForm::Components::Hints
include SimpleForm::Components::HTML5
include SimpleForm::Components::LabelInput
include SimpleForm::Components::Maxlength
include SimpleForm::Components::Minlength
include SimpleForm::Components::MinMax
include SimpleForm::Components::Pattern
include SimpleForm::Components::Placeholders
include SimpleForm::Components::Readonly
attr_reader :attribute_name, :column, :input_type, :reflection,
:options, :input_html_options, :input_html_classes, :html_classes
delegate :template, :object, :object_name, :lookup_model_names, :lookup_action, to: :@builder
class_attribute :default_options
self.default_options = {}
def self.enable(*keys)
options = self.default_options.dup
keys.each { |key| options.delete(key) }
self.default_options = options
end
def self.disable(*keys)
options = self.default_options.dup
keys.each { |key| options[key] = false }
self.default_options = options
end
# Always enabled.
enable :hint
# Usually disabled, needs to be enabled explicitly passing true as option.
disable :maxlength, :minlength, :placeholder, :pattern, :min_max
def initialize(builder, attribute_name, column, input_type, options = {})
super
options = options.dup
@builder = builder
@attribute_name = attribute_name
@column = column
@input_type = input_type
@reflection = options.delete(:reflection)
@options = options.reverse_merge!(self.class.default_options)
@required = calculate_required
# Notice that html_options_for receives a reference to input_html_classes.
# This means that classes added dynamically to input_html_classes will
# still propagate to input_html_options.
@html_classes = SimpleForm.additional_classes_for(:input) { additional_classes }
@input_html_classes = @html_classes.dup
input_html_classes = self.input_html_classes
if SimpleForm.input_class && input_html_classes.any?
input_html_classes << SimpleForm.input_class
end
@input_html_options = html_options_for(:input, input_html_classes).tap do |o|
o[:readonly] = true if has_readonly?
o[:disabled] = true if has_disabled?
o[:autofocus] = true if has_autofocus?
end
end
def input(wrapper_options = nil)
raise NotImplementedError
end
def input_options
options
end
def additional_classes
@additional_classes ||= [input_type, required_class, readonly_class, disabled_class].compact
end
def input_class
"#{lookup_model_names.join('_')}_#{reflection_or_attribute_name}"
end
private
def limit
if column
decimal_or_float? ? decimal_limit : column_limit
end
end
def column_limit
column.limit
end
# Add one for decimal point
def decimal_limit
column_limit && (column_limit + 1)
end
def decimal_or_float?
column.type == :float || column.type == :decimal
end
def nested_boolean_style?
options.fetch(:boolean_style, SimpleForm.boolean_style) == :nested
end
# Find reflection name when available, otherwise use attribute
def reflection_or_attribute_name
@reflection_or_attribute_name ||= reflection ? reflection.name : attribute_name
end
# Retrieve options for the given namespace from the options hash
def html_options_for(namespace, css_classes)
html_options = options[:"#{namespace}_html"]
html_options = html_options ? html_options.dup : {}
css_classes << html_options[:class] if html_options.key?(:class)
html_options[:class] = css_classes unless css_classes.empty?
html_options
end
# Lookup translations for the given namespace using I18n, based on object name,
# actual action and attribute name. Lookup priority as follows:
#
# simple_form.{namespace}.{model}.{action}.{attribute}
# simple_form.{namespace}.{model}.{attribute}
# simple_form.{namespace}.defaults.{attribute}
#
# Namespace is used for :labels and :hints.
#
# Model is the actual object name, for a @user object you'll have :user.
# Action is the action being rendered, usually :new or :edit.
# And attribute is the attribute itself, :name for example.
#
# The lookup for nested attributes is also done in a nested format using
# both model and nested object names, such as follow:
#
# simple_form.{namespace}.{model}.{nested}.{action}.{attribute}
# simple_form.{namespace}.{model}.{nested}.{attribute}
# simple_form.{namespace}.{nested}.{action}.{attribute}
# simple_form.{namespace}.{nested}.{attribute}
# simple_form.{namespace}.defaults.{attribute}
#
# Example:
#
# simple_form:
# labels:
# user:
# new:
# email: 'E-mail para efetuar o sign in.'
# edit:
# email: 'E-mail.'
#
# Take a look at our locale example file.
def translate_from_namespace(namespace, default = '')
model_names = lookup_model_names.dup
lookups = []
while !model_names.empty?
joined_model_names = model_names.join(".")
model_names.shift
lookups << :"#{joined_model_names}.#{lookup_action}.#{reflection_or_attribute_name}"
lookups << :"#{joined_model_names}.#{reflection_or_attribute_name}"
end
lookups << :"defaults.#{lookup_action}.#{reflection_or_attribute_name}"
lookups << :"defaults.#{reflection_or_attribute_name}"
lookups << default
I18n.t(lookups.shift, scope: :"#{i18n_scope}.#{namespace}", default: lookups).presence
end
def merge_wrapper_options(options, wrapper_options)
if wrapper_options
wrapper_options = set_input_classes(wrapper_options)
wrapper_options.merge(options) do |key, oldval, newval|
case key.to_s
when "class"
Array(oldval) + Array(newval)
when "data", "aria"
oldval.merge(newval)
else
newval
end
end
else
options
end
end
def set_input_classes(wrapper_options)
wrapper_options = wrapper_options.dup
error_class = wrapper_options.delete(:error_class)
valid_class = wrapper_options.delete(:valid_class)
if error_class.present? && has_errors?
wrapper_options[:class] = "#{wrapper_options[:class]} #{error_class}"
end
if valid_class.present? && valid?
wrapper_options[:class] = "#{wrapper_options[:class]} #{valid_class}"
end
wrapper_options
end
def i18n_scope
SimpleForm.i18n_scope
end
end
end
end
================================================
FILE: lib/simple_form/inputs/block_input.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Inputs
class BlockInput < Base
def initialize(*args, &block)
super
@block = block
end
def input(wrapper_options = nil)
template.capture(&@block)
end
end
end
end
================================================
FILE: lib/simple_form/inputs/boolean_input.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Inputs
class BooleanInput < Base
def input(wrapper_options = nil)
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
if nested_boolean_style?
build_hidden_field_for_checkbox +
template.label_tag(nil, class: boolean_label_class) {
build_check_box_without_hidden_field(merged_input_options) +
inline_label
}
else
if include_hidden?
build_check_box(unchecked_value, merged_input_options)
else
build_check_box_without_hidden_field(merged_input_options)
end
end
end
def label_input(wrapper_options = nil)
if options[:label] == false || inline_label?
input(wrapper_options)
elsif nested_boolean_style?
html_options = label_html_options.dup
html_options[:class] ||= []
html_options[:class].push(boolean_label_class) if boolean_label_class
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
build_hidden_field_for_checkbox +
@builder.label(label_target, html_options) {
build_check_box_without_hidden_field(merged_input_options) + label_text
}
else
input(wrapper_options) + label(wrapper_options)
end
end
private
def boolean_label_class
options[:boolean_label_class] || SimpleForm.boolean_label_class
end
# Build a checkbox tag using default unchecked value. This allows us to
# reuse the method for nested boolean style, but with no unchecked value,
# which won't generate the hidden checkbox. This is the default functionality
# in Rails > 3.2.1, and is backported in SimpleForm AV helpers.
def build_check_box(unchecked_value, options)
@builder.check_box(attribute_name, options, checked_value, unchecked_value)
end
# Build a checkbox without generating the hidden field. See
# #build_hidden_field_for_checkbox for more info.
def build_check_box_without_hidden_field(options)
build_check_box(nil, options)
end
# Create a hidden field for the current checkbox, so we can simulate Rails
# functionality with hidden + checkbox, but under a nested context, where
# we need the hidden field to be *outside* the label (otherwise it
# generates invalid html - html5 only).
def build_hidden_field_for_checkbox
return "".html_safe if !include_hidden? || !unchecked_value
options = { value: unchecked_value, id: nil, disabled: input_html_options[:disabled] }
options[:name] = input_html_options[:name] if input_html_options.key?(:name)
options[:form] = input_html_options[:form] if input_html_options.key?(:form)
@builder.hidden_field(attribute_name, options)
end
def inline_label?
nested_boolean_style? && options[:inline_label]
end
def inline_label
inline_option = options[:inline_label]
if inline_option
label = inline_option == true ? label_text : html_escape(inline_option)
" #{label}".html_safe
end
end
# Booleans are not required by default because in most of the cases
# it makes no sense marking them as required. The only exception is
# Terms of Use usually presented at most sites sign up screen.
def required_by_default?
false
end
def include_hidden?
options.fetch(:include_hidden, true)
end
def checked_value
options.fetch(:checked_value, '1')
end
def unchecked_value
options.fetch(:unchecked_value, '0')
end
end
end
end
================================================
FILE: lib/simple_form/inputs/collection_check_boxes_input.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Inputs
class CollectionCheckBoxesInput < CollectionRadioButtonsInput
protected
# Checkbox components do not use the required html tag.
# More info: https://github.com/heartcombo/simple_form/issues/340#issuecomment-2871956
def has_required?
false
end
def build_nested_boolean_style_item_tag(collection_builder)
collection_builder.check_box + collection_builder.text.to_s
end
def item_wrapper_class
"checkbox"
end
end
end
end
================================================
FILE: lib/simple_form/inputs/collection_input.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Inputs
class CollectionInput < Base
BASIC_OBJECT_CLASSES = [String, Integer, Float, NilClass, Symbol, TrueClass, FalseClass]
BASIC_OBJECT_CLASSES.push(Fixnum, Bignum) unless 1.class == Integer
# Default boolean collection for use with selects/radios when no
# collection is given. Always fallback to this boolean collection.
# Texts can be translated using i18n in "simple_form.yes" and
# "simple_form.no" keys. See the example locale file.
def self.boolean_collection
[ [I18n.t(:"simple_form.yes", default: 'Yes'), true],
[I18n.t(:"simple_form.no", default: 'No'), false] ]
end
def input(wrapper_options = nil)
raise NotImplementedError,
"input should be implemented by classes inheriting from CollectionInput"
end
def input_options
options = super
options[:include_blank] = true unless skip_include_blank?
translate_option options, :prompt
translate_option options, :include_blank
options
end
private
def collection
@collection ||= begin
collection = options.delete(:collection) || self.class.boolean_collection
collection.respond_to?(:call) ? collection.call : collection.to_a
end
end
def has_required?
super && (input_options[:include_blank] || input_options[:prompt].present? || multiple?)
end
# Check if :include_blank must be included by default.
def skip_include_blank?
(options.keys & %i[prompt include_blank default selected]).any? || multiple?
end
def multiple?
!!options[:input_html].try(:[], :multiple)
end
# Detect the right method to find the label and value for a collection.
# If no label or value method are defined, will attempt to find them based
# on default label and value methods that can be configured through
# SimpleForm.collection_label_methods and
# SimpleForm.collection_value_methods.
def detect_collection_methods
label, value = options.delete(:label_method), options.delete(:value_method)
unless label && value
common_method_for = detect_common_display_methods
label ||= common_method_for[:label]
value ||= common_method_for[:value]
end
[label, value]
end
def detect_common_display_methods(collection_classes = detect_collection_classes)
collection_translated = translate_collection if collection_classes == [Symbol]
if collection_translated || collection_classes.include?(Array)
{ label: :first, value: :second }
elsif collection_includes_basic_objects?(collection_classes)
{ label: :to_s, value: :to_s }
else
detect_method_from_class(collection_classes)
end
end
def detect_method_from_class(collection_classes)
sample = collection.first || collection.last
{ label: SimpleForm.collection_label_methods.find { |m| sample.respond_to?(m) },
value: SimpleForm.collection_value_methods.find { |m| sample.respond_to?(m) } }
end
def detect_collection_classes(some_collection = collection)
some_collection.map(&:class).uniq
end
def collection_includes_basic_objects?(collection_classes)
(collection_classes & BASIC_OBJECT_CLASSES).any?
end
def translate_collection
if translated_collection = translate_from_namespace(:options)
@collection = collection.map do |key|
html_key = "#{key}_html".to_sym
if translated_collection[html_key]
[translated_collection[html_key].html_safe || key, key.to_s]
else
[translated_collection[key] || key, key.to_s]
end
end
true
end
end
def translate_option(options, key)
if options[key] == :translate
namespace = key.to_s.pluralize
options[key] = translate_from_namespace(namespace, true)
end
end
end
end
end
================================================
FILE: lib/simple_form/inputs/collection_radio_buttons_input.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Inputs
class CollectionRadioButtonsInput < CollectionInput
def input(wrapper_options = nil)
label_method, value_method = detect_collection_methods
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
@builder.send(:"collection_#{input_type}",
attribute_name, collection, value_method, label_method,
input_options, merged_input_options,
&collection_block_for_nested_boolean_style
)
end
def input_options
options = super
apply_default_collection_options!(options)
options
end
protected
def apply_default_collection_options!(options)
options[:item_wrapper_tag] ||= options.fetch(:item_wrapper_tag, SimpleForm.item_wrapper_tag)
options[:item_wrapper_class] = [
item_wrapper_class, options[:item_wrapper_class], SimpleForm.item_wrapper_class
].compact.presence if SimpleForm.include_default_input_wrapper_class
options[:collection_wrapper_tag] ||= options.fetch(:collection_wrapper_tag, SimpleForm.collection_wrapper_tag)
options[:collection_wrapper_class] = [
options[:collection_wrapper_class], SimpleForm.collection_wrapper_class
].compact.presence
end
def collection_block_for_nested_boolean_style
return unless nested_boolean_style?
proc { |builder| build_nested_boolean_style_item_tag(builder) }
end
def build_nested_boolean_style_item_tag(collection_builder)
collection_builder.radio_button + collection_builder.text.to_s
end
def item_wrapper_class
"radio"
end
# Do not attempt to generate label[for] attributes by default, unless an
# explicit html option is given. This avoids generating labels pointing to
# non existent fields.
def generate_label_for_attribute?
false
end
end
end
end
================================================
FILE: lib/simple_form/inputs/collection_select_input.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Inputs
class CollectionSelectInput < CollectionInput
def input(wrapper_options = nil)
label_method, value_method = detect_collection_methods
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
@builder.collection_select(
attribute_name, collection, value_method, label_method,
input_options, merged_input_options
)
end
end
end
end
================================================
FILE: lib/simple_form/inputs/color_input.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Inputs
class ColorInput < Base
def input(wrapper_options = nil)
input_html_options[:type] ||= "color" if html5?
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
@builder.text_field(attribute_name, merged_input_options)
end
end
end
end
================================================
FILE: lib/simple_form/inputs/date_time_input.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Inputs
class DateTimeInput < Base
def input(wrapper_options = nil)
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
if use_html5_inputs?
@builder.send(:"#{input_type}_field", attribute_name, merged_input_options)
else
@builder.send(:"#{input_type}_select", attribute_name, input_options, merged_input_options)
end
end
private
def label_target
if use_html5_inputs?
attribute_name
else
position = case input_type
when :date, :datetime
date_order = input_options[:order] || I18n.t('date.order')
date_order.first.to_sym
else
:hour
end
position = ActionView::Helpers::DateTimeSelector::POSITION[position]
"#{attribute_name}_#{position}i"
end
end
def use_html5_inputs?
input_options[:html5]
end
end
end
end
================================================
FILE: lib/simple_form/inputs/file_input.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Inputs
class FileInput < Base
def input(wrapper_options = nil)
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
@builder.file_field(attribute_name, merged_input_options)
end
end
end
end
================================================
FILE: lib/simple_form/inputs/grouped_collection_select_input.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Inputs
class GroupedCollectionSelectInput < CollectionInput
def input(wrapper_options = nil)
label_method, value_method = detect_collection_methods
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
@builder.grouped_collection_select(attribute_name, grouped_collection,
group_method, group_label_method, value_method, label_method,
input_options, merged_input_options)
end
private
def grouped_collection
@grouped_collection ||= begin
grouped_collection = options.delete(:collection)
grouped_collection.respond_to?(:call) ? grouped_collection.call : grouped_collection.to_a
end
end
# Sample collection
def collection
@collection ||= grouped_collection.map { |collection| group_method.respond_to?(:call) ? group_method.call(collection) : collection.try(:send, group_method) }.detect(&:present?) || []
end
def group_method
@group_method ||= options.delete(:group_method)
end
def group_label_method
label = options.delete(:group_label_method)
unless label
common_method_for = detect_common_display_methods(detect_collection_classes(grouped_collection))
label = common_method_for[:label]
end
label
end
def detect_method_from_class(collection_classes)
return {} if collection_classes.empty?
sample = collection_classes.first
{ label: SimpleForm.collection_label_methods.find { |m| sample.instance_methods.include?(m) },
value: SimpleForm.collection_value_methods.find { |m| sample.instance_methods.include?(m) } }
end
end
end
end
================================================
FILE: lib/simple_form/inputs/hidden_input.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Inputs
class HiddenInput < Base
disable :label, :errors, :hint, :required
def input(wrapper_options = nil)
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
@builder.hidden_field(attribute_name, merged_input_options)
end
private
def required_class
nil
end
end
end
end
================================================
FILE: lib/simple_form/inputs/numeric_input.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Inputs
class NumericInput < Base
enable :placeholder, :min_max
def input(wrapper_options = nil)
input_html_classes.unshift("numeric")
if html5?
input_html_options[:type] ||= "number"
input_html_options[:step] ||= integer? ? 1 : "any"
end
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
@builder.text_field(attribute_name, merged_input_options)
end
end
end
end
================================================
FILE: lib/simple_form/inputs/password_input.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Inputs
class PasswordInput < Base
enable :placeholder, :maxlength, :minlength
def input(wrapper_options = nil)
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
@builder.password_field(attribute_name, merged_input_options)
end
end
end
end
================================================
FILE: lib/simple_form/inputs/priority_input.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Inputs
class PriorityInput < CollectionSelectInput
def input(wrapper_options = nil)
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
send(:"#{input_type}_input", merged_input_options)
end
def input_priority
options[:priority] || SimpleForm.send(:"#{input_type}_priority")
end
protected
def country_input(merged_input_options)
@builder.send(:country_select,
attribute_name,
input_options.merge(priority_countries: input_priority),
merged_input_options)
end
def time_zone_input(merged_input_options)
@builder.send(:time_zone_select,
attribute_name,
input_priority,
input_options,
merged_input_options)
end
def skip_include_blank?
super || input_priority.present?
end
end
end
end
================================================
FILE: lib/simple_form/inputs/range_input.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Inputs
class RangeInput < NumericInput
def input(wrapper_options = nil)
if html5?
input_html_options[:type] ||= "range"
input_html_options[:step] ||= 1
end
super
end
end
end
end
================================================
FILE: lib/simple_form/inputs/rich_text_area_input.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Inputs
class RichTextAreaInput < Base
enable :placeholder
def input(wrapper_options = nil)
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
@builder.rich_text_area(attribute_name, merged_input_options)
end
end
end
end
================================================
FILE: lib/simple_form/inputs/string_input.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Inputs
class StringInput < Base
enable :placeholder, :maxlength, :minlength, :pattern
def input(wrapper_options = nil)
unless string?
input_html_classes.unshift("string")
input_html_options[:type] ||= input_type if html5?
end
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
@builder.text_field(attribute_name, merged_input_options)
end
private
def string?
input_type == :string || input_type == :citext
end
end
end
end
================================================
FILE: lib/simple_form/inputs/text_input.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Inputs
class TextInput < Base
enable :placeholder, :maxlength, :minlength
def input(wrapper_options = nil)
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
@builder.text_area(attribute_name, merged_input_options)
end
end
end
end
================================================
FILE: lib/simple_form/inputs/weekday_input.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Inputs
class WeekdayInput < CollectionSelectInput
enable :placeholder
def input(wrapper_options = nil)
merged_input_options = merge_wrapper_options(input_html_options, wrapper_options)
@builder.weekday_select(attribute_name, input_options, merged_input_options)
end
end
end
end
================================================
FILE: lib/simple_form/inputs.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Inputs
extend ActiveSupport::Autoload
autoload :Base
autoload :BlockInput
autoload :BooleanInput
autoload :CollectionCheckBoxesInput
autoload :CollectionInput
autoload :CollectionRadioButtonsInput
autoload :CollectionSelectInput
autoload :ColorInput
autoload :DateTimeInput
autoload :FileInput
autoload :GroupedCollectionSelectInput
autoload :HiddenInput
autoload :NumericInput
autoload :PasswordInput
autoload :PriorityInput
autoload :RangeInput
autoload :RichTextAreaInput
autoload :StringInput
autoload :TextInput
autoload :WeekdayInput
end
end
================================================
FILE: lib/simple_form/map_type.rb
================================================
# frozen_string_literal: true
require 'active_support/core_ext/class/attribute'
module SimpleForm
module MapType
def self.extended(base)
base.class_attribute :mappings
base.mappings = {}
end
def map_type(*types)
map_to = types.extract_options![:to]
raise ArgumentError, "You need to give :to as option to map_type" unless map_to
self.mappings = mappings.merge types.each_with_object({}) { |t, m| m[t] = map_to }
end
end
end
================================================
FILE: lib/simple_form/railtie.rb
================================================
# frozen_string_literal: true
require 'rails/railtie'
module SimpleForm
class Railtie < Rails::Railtie
config.eager_load_namespaces << SimpleForm
config.after_initialize do
unless SimpleForm.configured?
warn '[Simple Form] Simple Form is not configured in the application and will use the default values.' +
' Use `rails generate simple_form:install` to generate the Simple Form configuration.'
end
end
initializer "simple_form.deprecator" do |app|
app.deprecators[:simple_form] = SimpleForm.deprecator if app.respond_to?(:deprecators)
end
end
end
================================================
FILE: lib/simple_form/tags.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Tags
module CollectionExtensions
private
def render_collection
item_wrapper_tag = @options.fetch(:item_wrapper_tag, :span)
item_wrapper_class = @options[:item_wrapper_class]
@collection.map do |item|
value = value_for_collection(item, @value_method)
text = value_for_collection(item, @text_method)
default_html_options = default_html_options_for_collection(item, value)
additional_html_options = option_html_attributes(item)
rendered_item = yield item, value, text, default_html_options.merge(additional_html_options)
if @options.fetch(:boolean_style, SimpleForm.boolean_style) == :nested
label_options = default_html_options.slice(:index, :namespace)
label_options['class'] = @options[:item_label_class]
rendered_item = @template_object.label(@object_name, sanitize_attribute_name(value), rendered_item, label_options)
end
item_wrapper_tag ? @template_object.content_tag(item_wrapper_tag, rendered_item, class: item_wrapper_class) : rendered_item
end.join.html_safe
end
def wrap_rendered_collection(collection)
wrapper_tag = @options[:collection_wrapper_tag]
if wrapper_tag
wrapper_class = @options[:collection_wrapper_class]
@template_object.content_tag(wrapper_tag, collection, class: wrapper_class)
else
collection
end
end
end
class CollectionRadioButtons < ActionView::Helpers::Tags::CollectionRadioButtons
include CollectionExtensions
def render
wrap_rendered_collection(super)
end
private
def render_component(builder)
label_class = "#{@options[:item_label_class]} collection_radio_buttons".strip
builder.radio_button + builder.label(class: label_class)
end
end
class CollectionCheckBoxes < ActionView::Helpers::Tags::CollectionCheckBoxes
include CollectionExtensions
def render
wrap_rendered_collection(super)
end
private
def render_component(builder)
label_class = "#{@options[:item_label_class]} collection_check_boxes".strip
builder.check_box + builder.label(class: label_class)
end
end
end
end
================================================
FILE: lib/simple_form/version.rb
================================================
# frozen_string_literal: true
module SimpleForm
VERSION = "5.4.1".freeze
end
================================================
FILE: lib/simple_form/wrappers/builder.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Wrappers
# Provides the builder syntax for components. The builder provides
# three methods `use`, `optional` and `wrapper` and they allow the following invocations:
#
# config.wrappers do |b|
# # Use a single component
# b.use :html5
#
# # Use the component, but do not automatically lookup. It will only be triggered when
# # :placeholder is explicitly set.
# b.optional :placeholder
#
# # Use a component with specific wrapper options
# b.use :error, wrap_with: { tag: "span", class: "error" }
#
# # Use a set of components by wrapping them in a tag+class.
# b.wrapper tag: "div", class: "another" do |ba|
# ba.use :label
# ba.use :input
# end
#
# # Use a set of components by wrapping them in a tag+class.
# # This wrapper is identified by :label_input, which means it can
# # be turned off on demand with `f.input :name, label_input: false`
# b.wrapper :label_input, tag: "div", class: "another" do |ba|
# ba.use :label
# ba.use :input
# end
# end
#
# The builder also accepts default options at the root level. This is usually
# used if you want a component to be disabled by default:
#
# config.wrappers hint: false do |b|
# b.use :hint
# b.use :label_input
# end
#
# In the example above, hint defaults to false, which means it won't automatically
# do the lookup anymore. It will only be triggered when :hint is explicitly set.
class Builder
def initialize(options)
@options = options
@components = []
end
def use(name, options = {})
if options && wrapper = options[:wrap_with]
@components << Single.new(name, wrapper, options.except(:wrap_with))
else
@components << Leaf.new(name, options)
end
end
def optional(name, options = {}, &block)
@options[name] = false
use(name, options)
end
def wrapper(name, options = nil)
if block_given?
name, options = nil, name if name.is_a?(Hash)
builder = self.class.new(@options)
options ||= {}
options[:tag] = :div if options[:tag].nil?
yield builder
@components << Many.new(name, builder.to_a, options)
else
raise ArgumentError, "A block is required as argument to wrapper"
end
end
def to_a
@components
end
end
end
end
================================================
FILE: lib/simple_form/wrappers/leaf.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Wrappers
class Leaf
attr_reader :namespace
def initialize(namespace, options = {})
@namespace = namespace
@options = options
end
def render(input)
method = input.method(@namespace)
if method.arity.zero?
SimpleForm.deprecator.warn(SimpleForm::CUSTOM_INPUT_DEPRECATION_WARN % { name: @namespace })
method.call
else
method.call(@options)
end
end
def find(name)
self if @namespace == name
end
end
end
end
================================================
FILE: lib/simple_form/wrappers/many.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Wrappers
# A wrapper is an object that holds several components and render them.
# A component may be any object that responds to `render`.
# This API allows inputs/components to be easily wrapped, removing the
# need to modify the code only to wrap input in an extra tag.
#
# `Many` represents a wrapper around several components at the same time.
# It may optionally receive a namespace, allowing it to be configured
# on demand on input generation.
class Many
attr_reader :namespace, :defaults, :components
def initialize(namespace, components, defaults = {})
@namespace = namespace
@components = components
@defaults = defaults
@defaults[:tag] = :div unless @defaults.key?(:tag)
@defaults[:class] = Array(@defaults[:class])
end
def render(input)
content = "".html_safe
options = input.options
components.each do |component|
next if options[component.namespace] == false
rendered = component.render(input)
content.safe_concat rendered.to_s if rendered
end
wrap(input, options, content)
end
def find(name)
return self if namespace == name
@components.each do |c|
if c.is_a?(Symbol)
return nil if c == namespace
elsif value = c.find(name)
return value
end
end
nil
end
private
def wrap(input, options, content)
return content if options[namespace] == false
return if defaults[:unless_blank] && content.empty?
tag = (namespace && options[:"#{namespace}_tag"]) || @defaults[:tag]
return content unless tag
klass = html_classes(input, options)
opts = html_options(options)
opts[:class] = (klass << opts[:class]).join(' ').strip unless klass.empty?
input.template.content_tag(tag, content, opts)
end
def html_options(options)
(@defaults[:html] || {}).merge(options[:"#{namespace}_html"] || {})
end
def html_classes(input, options)
@defaults[:class].dup
end
end
end
end
================================================
FILE: lib/simple_form/wrappers/root.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Wrappers
# `Root` is the root wrapper for all components. It is special cased to
# always have a namespace and to add special html classes.
class Root < Many
attr_reader :options
def initialize(*args)
super(:wrapper, *args)
@options = @defaults.except(:tag, :class, :error_class, :hint_class)
end
def render(input)
input.options.reverse_merge!(@options)
super
end
# Provide a fallback if name cannot be found.
def find(name)
super || SimpleForm::Wrappers::Many.new(name, [Leaf.new(name)])
end
private
def html_classes(input, options)
css = options[:wrapper_class] ? Array(options[:wrapper_class]) : @defaults[:class]
css += SimpleForm.additional_classes_for(:wrapper) do
input.additional_classes + [input.input_class]
end
css << html_class(:error_class, options) { input.has_errors? }
css << html_class(:hint_class, options) { input.has_hint? }
css << html_class(:valid_class, options) { input.valid? }
css.compact
end
def html_class(key, options)
css = (options[:"wrapper_#{key}"] || @defaults[key])
css if css && yield
end
end
end
end
================================================
FILE: lib/simple_form/wrappers/single.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Wrappers
# `Single` is an optimization for a wrapper that has only one component.
class Single < Many
def initialize(name, wrapper_options = {}, options = {})
@component = Leaf.new(name, options)
super(name, [@component], wrapper_options)
end
def render(input)
options = input.options
if options[namespace] != false
content = @component.render(input)
wrap(input, options, content) if content
end
end
private
def html_options(options)
%i[label input].include?(namespace) ? {} : super
end
end
end
end
================================================
FILE: lib/simple_form/wrappers.rb
================================================
# frozen_string_literal: true
module SimpleForm
module Wrappers
autoload :Builder, 'simple_form/wrappers/builder'
autoload :Many, 'simple_form/wrappers/many'
autoload :Root, 'simple_form/wrappers/root'
autoload :Single, 'simple_form/wrappers/single'
autoload :Leaf, 'simple_form/wrappers/leaf'
end
end
================================================
FILE: lib/simple_form.rb
================================================
# frozen_string_literal: true
require 'action_view'
require 'action_pack'
require 'simple_form/action_view_extensions/form_helper'
require 'simple_form/action_view_extensions/builder'
require 'active_support/core_ext/hash/slice'
require 'active_support/core_ext/hash/except'
require 'active_support/core_ext/hash/reverse_merge'
module SimpleForm
extend ActiveSupport::Autoload
autoload :Helpers
autoload :Wrappers
eager_autoload do
autoload :Components
autoload :ErrorNotification
autoload :FormBuilder
autoload :Inputs
end
def self.eager_load!
super
SimpleForm::Inputs.eager_load!
SimpleForm::Components.eager_load!
end
CUSTOM_INPUT_DEPRECATION_WARN = <<-WARN
%{name} method now accepts a `wrapper_options` argument. The method definition without the argument is deprecated and will be removed in the next Simple Form version. Change your code from:
def %{name}
to
def %{name}(wrapper_options)
See https://github.com/heartcombo/simple_form/pull/997 for more information.
WARN
FILE_METHODS_DEPRECATION_WARN = <<-WARN
[SIMPLE_FORM] SimpleForm.file_methods is deprecated and has no effect.
Since version 5, Simple Form now supports automatically discover of file inputs for the following Gems: activestorage, carrierwave, paperclip, refile and shrine.
If you are using a custom method that is not from one of the supported Gems, please change your forms to pass the input type explicitly:
<%= form.input :avatar, as: :file %>
See http://blog.plataformatec.com.br/2019/09/incorrect-access-control-in-simple-form-cve-2019-16676 for more information.
WARN
@@configured = false
def self.configured? #:nodoc:
@@configured
end
def self.deprecator
@deprecator ||= ActiveSupport::Deprecation.new("5.3", "SimpleForm")
end
## CONFIGURATION OPTIONS
# Method used to tidy up errors.
mattr_accessor :error_method
@@error_method = :first
# Default tag used for error notification helper.
mattr_accessor :error_notification_tag
@@error_notification_tag = :p
# CSS class to add for error notification helper.
mattr_accessor :error_notification_class
@@error_notification_class = :error_notification
# Series of attempts to detect a default label method for collection.
mattr_accessor :collection_label_methods
@@collection_label_methods = %i[to_label name title to_s]
# Series of attempts to detect a default value method for collection.
mattr_accessor :collection_value_methods
@@collection_value_methods = %i[id to_s]
# You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
mattr_accessor :collection_wrapper_tag
@@collection_wrapper_tag = nil
# You can define the class to use on all collection wrappers, defaulting to none.
mattr_accessor :collection_wrapper_class
@@collection_wrapper_class = nil
# You can wrap each item in a collection of radio/check boxes with a tag,
# defaulting to span. Please note that when using :boolean_style = :nested,
# SimpleForm will force this option to be a :label.
mattr_accessor :item_wrapper_tag
@@item_wrapper_tag = :span
# You can define the class to use on all item wrappers, defaulting to none.
mattr_accessor :item_wrapper_class
@@item_wrapper_class = nil
# How the label text should be generated altogether with the required text.
mattr_accessor :label_text
@@label_text = ->(label, required, explicit_label) { "#{required} #{label}" }
# You can define the class to be used on all labels. Defaults to none.
mattr_accessor :label_class
@@label_class = nil
# Define the way to render check boxes / radio buttons with labels.
# inline: input + label (default)
# nested: label > input
mattr_accessor :boolean_style
@@boolean_style = :inline
# DEPRECATED: You can define the class to be used on all forms. Default is
# simple_form.
mattr_reader :form_class
@@form_class = :simple_form
# You can define the default class to be used on all forms. Can be overridden
# with `html: { :class }`. Defaults to none.
mattr_accessor :default_form_class
@@default_form_class = nil
# You can define which elements should obtain additional classes.
mattr_accessor :generate_additional_classes_for
@@generate_additional_classes_for = %i[wrapper label input]
# Whether attributes are required by default or not.
mattr_accessor :required_by_default
@@required_by_default = true
# Tell browsers whether to use default HTML5 validations (novalidate option).
mattr_accessor :browser_validations
@@browser_validations = true
# Custom mappings for input types. This should be a hash containing a regexp
# to match as key, and the input type that will be used when the field name
# matches the regexp as value, such as { /count/ => :integer }.
mattr_accessor :input_mappings
@@input_mappings = nil
# Custom wrappers for input types. This should be a hash containing an input
# type as key and the wrapper that will be used for all inputs with specified type.
# e.g { string: :string_wrapper, boolean: :boolean_wrapper }
# You can also set a wrapper mapping per form basis.
# e.g simple_form_for(@foo, wrapper_mappings: { check_boxes: :bootstrap_checkbox })
mattr_accessor :wrapper_mappings
@@wrapper_mappings = nil
# Namespaces where SimpleForm should look for custom input classes that override
# default inputs. Namespaces are given as string to allow lazy loading inputs.
# e.g. config.custom_inputs_namespaces << "CustomInputs"
# will try to find CustomInputs::NumericInput when an :integer
# field is called.
mattr_accessor :custom_inputs_namespaces
@@custom_inputs_namespaces = []
# Default priority for time_zone inputs.
mattr_accessor :time_zone_priority
@@time_zone_priority = nil
# Default priority for country inputs.
gitextract_7k1r7n03/
├── .github/
│ ├── code-scanning.yml
│ └── workflows/
│ └── test.yml
├── .gitignore
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Gemfile
├── ISSUE_TEMPLATE.md
├── MIT-LICENSE
├── README.md
├── Rakefile
├── bin/
│ └── test
├── gemfiles/
│ ├── Gemfile-rails-7-0
│ ├── Gemfile-rails-7-1
│ ├── Gemfile-rails-7-2
│ ├── Gemfile-rails-8-0
│ └── Gemfile-rails-main
├── lib/
│ ├── generators/
│ │ └── simple_form/
│ │ ├── USAGE
│ │ ├── install_generator.rb
│ │ └── templates/
│ │ ├── README
│ │ ├── _form.html.erb
│ │ ├── _form.html.haml
│ │ ├── _form.html.slim
│ │ └── config/
│ │ ├── initializers/
│ │ │ ├── simple_form.rb
│ │ │ ├── simple_form_bootstrap.rb
│ │ │ └── simple_form_foundation.rb
│ │ └── locales/
│ │ └── simple_form.en.yml
│ ├── simple_form/
│ │ ├── action_view_extensions/
│ │ │ ├── builder.rb
│ │ │ └── form_helper.rb
│ │ ├── components/
│ │ │ ├── errors.rb
│ │ │ ├── hints.rb
│ │ │ ├── html5.rb
│ │ │ ├── label_input.rb
│ │ │ ├── labels.rb
│ │ │ ├── maxlength.rb
│ │ │ ├── min_max.rb
│ │ │ ├── minlength.rb
│ │ │ ├── pattern.rb
│ │ │ ├── placeholders.rb
│ │ │ └── readonly.rb
│ │ ├── components.rb
│ │ ├── error_notification.rb
│ │ ├── form_builder.rb
│ │ ├── helpers/
│ │ │ ├── autofocus.rb
│ │ │ ├── disabled.rb
│ │ │ ├── readonly.rb
│ │ │ ├── required.rb
│ │ │ └── validators.rb
│ │ ├── helpers.rb
│ │ ├── inputs/
│ │ │ ├── base.rb
│ │ │ ├── block_input.rb
│ │ │ ├── boolean_input.rb
│ │ │ ├── collection_check_boxes_input.rb
│ │ │ ├── collection_input.rb
│ │ │ ├── collection_radio_buttons_input.rb
│ │ │ ├── collection_select_input.rb
│ │ │ ├── color_input.rb
│ │ │ ├── date_time_input.rb
│ │ │ ├── file_input.rb
│ │ │ ├── grouped_collection_select_input.rb
│ │ │ ├── hidden_input.rb
│ │ │ ├── numeric_input.rb
│ │ │ ├── password_input.rb
│ │ │ ├── priority_input.rb
│ │ │ ├── range_input.rb
│ │ │ ├── rich_text_area_input.rb
│ │ │ ├── string_input.rb
│ │ │ ├── text_input.rb
│ │ │ └── weekday_input.rb
│ │ ├── inputs.rb
│ │ ├── map_type.rb
│ │ ├── railtie.rb
│ │ ├── tags.rb
│ │ ├── version.rb
│ │ ├── wrappers/
│ │ │ ├── builder.rb
│ │ │ ├── leaf.rb
│ │ │ ├── many.rb
│ │ │ ├── root.rb
│ │ │ └── single.rb
│ │ └── wrappers.rb
│ └── simple_form.rb
├── simple_form.gemspec
└── test/
├── action_view_extensions/
│ ├── builder_test.rb
│ └── form_helper_test.rb
├── components/
│ ├── custom_components_test.rb
│ └── label_test.rb
├── form_builder/
│ ├── association_test.rb
│ ├── button_test.rb
│ ├── error_notification_test.rb
│ ├── error_test.rb
│ ├── general_test.rb
│ ├── hint_test.rb
│ ├── input_field_test.rb
│ ├── label_test.rb
│ └── wrapper_test.rb
├── generators/
│ └── simple_form_generator_test.rb
├── inputs/
│ ├── boolean_input_test.rb
│ ├── collection_check_boxes_input_test.rb
│ ├── collection_radio_buttons_input_test.rb
│ ├── collection_select_input_test.rb
│ ├── color_input_test.rb
│ ├── country_input_test.rb
│ ├── datetime_input_test.rb
│ ├── disabled_test.rb
│ ├── discovery_test.rb
│ ├── file_input_test.rb
│ ├── general_test.rb
│ ├── grouped_collection_select_input_test.rb
│ ├── hidden_input_test.rb
│ ├── numeric_input_test.rb
│ ├── readonly_test.rb
│ ├── required_test.rb
│ ├── rich_text_area_input_test.rb
│ ├── string_input_test.rb
│ ├── text_input_test.rb
│ ├── time_zone_input_test.rb
│ └── weekday_input_test.rb
├── simple_form_test.rb
├── support/
│ ├── discovery_inputs.rb
│ ├── misc_helpers.rb
│ ├── mock_controller.rb
│ └── models.rb
└── test_helper.rb
SYMBOL INDEX (590 symbols across 96 files)
FILE: lib/generators/simple_form/install_generator.rb
type SimpleForm (line 2) | module SimpleForm
type Generators (line 3) | module Generators
class InstallGenerator (line 4) | class InstallGenerator < Rails::Generators::Base
method info_bootstrap (line 11) | def info_bootstrap
method copy_config (line 18) | def copy_config
method copy_scaffold_template (line 30) | def copy_scaffold_template
method show_readme (line 35) | def show_readme
FILE: lib/simple_form.rb
type SimpleForm (line 10) | module SimpleForm
function eager_load! (line 23) | def self.eager_load!
function configured? (line 54) | def self.configured? #:nodoc:
function deprecator (line 58) | def self.deprecator
function wrapper (line 221) | def self.wrapper(name)
class WrapperNotFound (line 226) | class WrapperNotFound < StandardError
function wrappers (line 231) | def self.wrappers(*args, &block)
function build (line 242) | def self.build(options = {})
function additional_classes_for (line 264) | def self.additional_classes_for(component)
function default_input_size= (line 270) | def self.default_input_size=(*)
function form_class= (line 274) | def self.form_class=(value)
function file_methods= (line 279) | def self.file_methods=(file_methods)
function file_methods (line 284) | def self.file_methods
function setup (line 291) | def self.setup
function include_component (line 331) | def self.include_component(component)
FILE: lib/simple_form/action_view_extensions/builder.rb
type SimpleForm (line 2) | module SimpleForm
type ActionViewExtensions (line 3) | module ActionViewExtensions
type Builder (line 6) | module Builder
function simple_fields_for (line 17) | def simple_fields_for(*args, &block)
type ActionView::Helpers (line 34) | module ActionView::Helpers
class FormBuilder (line 35) | class FormBuilder
FILE: lib/simple_form/action_view_extensions/form_helper.rb
type SimpleForm (line 2) | module SimpleForm
type ActionViewExtensions (line 3) | module ActionViewExtensions
type FormHelper (line 12) | module FormHelper
function simple_form_for (line 14) | def simple_form_for(record, options = {}, &block)
function simple_fields_for (line 31) | def simple_fields_for(record_name, record_object = nil, options = ...
function with_simple_form_field_error_proc (line 42) | def with_simple_form_field_error_proc
function simple_form_css_class (line 52) | def simple_form_css_class(record, options)
FILE: lib/simple_form/components.rb
type SimpleForm (line 2) | module SimpleForm
type Components (line 9) | module Components
FILE: lib/simple_form/components/errors.rb
type SimpleForm (line 2) | module SimpleForm
type Components (line 3) | module Components
type Errors (line 4) | module Errors
function error (line 5) | def error(wrapper_options = nil)
function full_error (line 9) | def full_error(wrapper_options = nil)
function has_errors? (line 13) | def has_errors?
function has_value? (line 17) | def has_value?
function valid? (line 21) | def valid?
function error_text (line 27) | def error_text
function full_error_text (line 33) | def full_error_text
function object_with_errors? (line 37) | def object_with_errors?
function error_method (line 41) | def error_method
function errors (line 45) | def errors
function full_errors (line 49) | def full_errors
function errors_on_attribute (line 53) | def errors_on_attribute
function full_errors_on_attribute (line 57) | def full_errors_on_attribute
function errors_on_association (line 61) | def errors_on_association
function full_errors_on_association (line 65) | def full_errors_on_association
function has_custom_error? (line 69) | def has_custom_error?
FILE: lib/simple_form/components/hints.rb
type SimpleForm (line 2) | module SimpleForm
type Components (line 3) | module Components
type Hints (line 5) | module Hints
function hint (line 6) | def hint(wrapper_options = nil)
function has_hint? (line 19) | def has_hint?
FILE: lib/simple_form/components/html5.rb
type SimpleForm (line 2) | module SimpleForm
type Components (line 3) | module Components
type HTML5 (line 4) | module HTML5
function initialize (line 5) | def initialize(*)
function html5 (line 9) | def html5(wrapper_options = nil)
function html5? (line 17) | def html5?
function input_html_required_option (line 21) | def input_html_required_option
function has_required? (line 25) | def has_required?
FILE: lib/simple_form/components/label_input.rb
type SimpleForm (line 2) | module SimpleForm
type Components (line 3) | module Components
type LabelInput (line 4) | module LabelInput
function label_input (line 11) | def label_input(wrapper_options = nil)
function deprecated_component (line 21) | def deprecated_component(namespace, wrapper_options)
FILE: lib/simple_form/components/labels.rb
type SimpleForm (line 2) | module SimpleForm
type Components (line 3) | module Components
type Labels (line 4) | module Labels
type ClassMethods (line 7) | module ClassMethods #:nodoc:
function translate_required_html (line 8) | def translate_required_html
function translate_required_text (line 14) | def translate_required_text
function translate_required_mark (line 18) | def translate_required_mark
function i18n_scope (line 24) | def i18n_scope
function label (line 29) | def label(wrapper_options = nil)
function label_text (line 39) | def label_text(wrapper_options = nil)
function label_target (line 44) | def label_target
function label_html_options (line 48) | def label_html_options
function raw_label_text (line 63) | def raw_label_text #:nodoc:
function required_label_text (line 68) | def required_label_text #:nodoc:
function label_translation (line 73) | def label_translation #:nodoc:
function generate_label_for_attribute? (line 83) | def generate_label_for_attribute?
FILE: lib/simple_form/components/maxlength.rb
type SimpleForm (line 2) | module SimpleForm
type Components (line 3) | module Components
type Maxlength (line 5) | module Maxlength
function maxlength (line 6) | def maxlength(wrapper_options = nil)
function maximum_length_from_validation (line 13) | def maximum_length_from_validation
function find_length_validator (line 23) | def find_length_validator
function maximum_length_value_from (line 27) | def maximum_length_value_from(length_validator)
FILE: lib/simple_form/components/min_max.rb
type SimpleForm (line 2) | module SimpleForm
type Components (line 3) | module Components
type MinMax (line 4) | module MinMax
function min_max (line 5) | def min_max(wrapper_options = nil)
function integer? (line 16) | def integer?
function minimum_value (line 20) | def minimum_value(validator_options)
function maximum_value (line 28) | def maximum_value(validator_options)
function find_numericality_validator (line 36) | def find_numericality_validator
FILE: lib/simple_form/components/minlength.rb
type SimpleForm (line 2) | module SimpleForm
type Components (line 3) | module Components
type Minlength (line 5) | module Minlength
function minlength (line 6) | def minlength(wrapper_options = nil)
function minimum_length_from_validation (line 13) | def minimum_length_from_validation
function find_length_validator (line 23) | def find_length_validator
function minimum_length_value_from (line 27) | def minimum_length_value_from(length_validator)
FILE: lib/simple_form/components/pattern.rb
type SimpleForm (line 2) | module SimpleForm
type Components (line 3) | module Components
type Pattern (line 5) | module Pattern
function pattern (line 6) | def pattern(wrapper_options = nil)
function pattern_source (line 13) | def pattern_source
function find_pattern_validator (line 22) | def find_pattern_validator
FILE: lib/simple_form/components/placeholders.rb
type SimpleForm (line 2) | module SimpleForm
type Components (line 3) | module Components
type Placeholders (line 5) | module Placeholders
function placeholder (line 6) | def placeholder(wrapper_options = nil)
function placeholder_text (line 11) | def placeholder_text(wrapper_options = nil)
FILE: lib/simple_form/components/readonly.rb
type SimpleForm (line 2) | module SimpleForm
type Components (line 3) | module Components
type Readonly (line 5) | module Readonly
function readonly (line 6) | def readonly(wrapper_options = nil)
function readonly_attribute? (line 16) | def readonly_attribute?
FILE: lib/simple_form/error_notification.rb
type SimpleForm (line 2) | module SimpleForm
class ErrorNotification (line 3) | class ErrorNotification
method initialize (line 6) | def initialize(builder, options)
method render (line 12) | def render
method errors (line 20) | def errors
method has_errors? (line 24) | def has_errors?
method error_message (line 28) | def error_message
method error_notification_tag (line 32) | def error_notification_tag
method html_options (line 36) | def html_options
method translate_error_notification (line 41) | def translate_error_notification
FILE: lib/simple_form/form_builder.rb
type SimpleForm (line 6) | module SimpleForm
class FormBuilder (line 7) | class FormBuilder < ActionView::Helpers::FormBuilder
method discovery_cache (line 37) | def self.discovery_cache
method initialize (line 41) | def initialize(*) #:nodoc:
method input (line 118) | def input(attribute_name, options = {}, &block)
method input_field (line 165) | def input_field(attribute_name, options = {})
method association (line 207) | def association(association, options = {}, &block)
method button (line 237) | def button(type, *args, &block)
method error (line 256) | def error(attribute_name, options = {})
method full_error (line 273) | def full_error(attribute_name, options = {})
method hint (line 295) | def hint(attribute_name, options = {})
method label (line 324) | def label(attribute_name, *args)
method error_notification (line 346) | def error_notification(options = {})
method collection_radio_buttons (line 397) | def collection_radio_buttons(method, collection, value_method, text_...
method collection_check_boxes (line 451) | def collection_check_boxes(method, collection, value_method, text_me...
method lookup_model_names (line 463) | def lookup_model_names #:nodoc:
method lookup_action (line 474) | def lookup_action #:nodoc:
method fetch_association_collection (line 485) | def fetch_association_collection(reflection, options)
method build_association_attribute (line 508) | def build_association_attribute(reflection, association, options)
method find_input (line 531) | def find_input(attribute_name, options = {}, &block)
method default_input_type (line 545) | def default_input_type(attribute_name, column, options)
method find_custom_type (line 570) | def find_custom_type(attribute_name)
method file_method? (line 592) | def file_method?(attribute_name)
method find_attribute_column (line 600) | def find_attribute_column(attribute_name)
method find_association_reflection (line 612) | def find_association_reflection(association)
method find_mapping (line 625) | def find_mapping(input_type)
method find_wrapper_mapping (line 642) | def find_wrapper_mapping(input_type)
method find_wrapper (line 650) | def find_wrapper(input_type, options)
method discovery_cache (line 660) | def discovery_cache
method mapping_override (line 668) | def mapping_override(klass)
method attempt_mapping (line 677) | def attempt_mapping(mapping, at)
method attempt_mapping_with_custom_namespace (line 687) | def attempt_mapping_with_custom_namespace(input_name)
method build_input_field_components (line 697) | def build_input_field_components(components)
method build_input_field_options (line 707) | def build_input_field_options
FILE: lib/simple_form/helpers.rb
type SimpleForm (line 2) | module SimpleForm
type Helpers (line 6) | module Helpers
FILE: lib/simple_form/helpers/autofocus.rb
type SimpleForm (line 2) | module SimpleForm
type Helpers (line 3) | module Helpers
type Autofocus (line 4) | module Autofocus
function has_autofocus? (line 7) | def has_autofocus?
FILE: lib/simple_form/helpers/disabled.rb
type SimpleForm (line 2) | module SimpleForm
type Helpers (line 3) | module Helpers
type Disabled (line 4) | module Disabled
function has_disabled? (line 7) | def has_disabled?
function disabled_class (line 11) | def disabled_class
FILE: lib/simple_form/helpers/readonly.rb
type SimpleForm (line 2) | module SimpleForm
type Helpers (line 3) | module Helpers
type Readonly (line 4) | module Readonly
function readonly_class (line 7) | def readonly_class
function has_readonly? (line 11) | def has_readonly?
FILE: lib/simple_form/helpers/required.rb
type SimpleForm (line 2) | module SimpleForm
type Helpers (line 3) | module Helpers
type Required (line 4) | module Required
function required_field? (line 7) | def required_field?
function calculate_required (line 11) | def calculate_required
function required_by_validators? (line 21) | def required_by_validators?
function required_by_default? (line 25) | def required_by_default?
function required_class (line 31) | def required_class
FILE: lib/simple_form/helpers/validators.rb
type SimpleForm (line 2) | module SimpleForm
type Helpers (line 3) | module Helpers
type Validators (line 4) | module Validators
function has_validators? (line 5) | def has_validators?
function attribute_validators (line 11) | def attribute_validators
function reflection_validators (line 15) | def reflection_validators
function valid_validator? (line 19) | def valid_validator?(validator)
function conditional_validators? (line 23) | def conditional_validators?(validator)
function action_validator_match? (line 27) | def action_validator_match?(validator)
function find_validator (line 40) | def find_validator(kind)
function resolve_validator_value (line 46) | def resolve_validator_value(value)
FILE: lib/simple_form/inputs.rb
type SimpleForm (line 2) | module SimpleForm
type Inputs (line 3) | module Inputs
FILE: lib/simple_form/inputs/base.rb
type SimpleForm (line 5) | module SimpleForm
type Inputs (line 6) | module Inputs
class Base (line 7) | class Base
method enable (line 36) | def self.enable(*keys)
method disable (line 42) | def self.disable(*keys)
method initialize (line 54) | def initialize(builder, attribute_name, column, input_type, option...
method input (line 86) | def input(wrapper_options = nil)
method input_options (line 90) | def input_options
method additional_classes (line 94) | def additional_classes
method input_class (line 98) | def input_class
method limit (line 104) | def limit
method column_limit (line 110) | def column_limit
method decimal_limit (line 115) | def decimal_limit
method decimal_or_float? (line 119) | def decimal_or_float?
method nested_boolean_style? (line 123) | def nested_boolean_style?
method reflection_or_attribute_name (line 128) | def reflection_or_attribute_name
method html_options_for (line 133) | def html_options_for(namespace, css_classes)
method translate_from_namespace (line 174) | def translate_from_namespace(namespace, default = '')
method merge_wrapper_options (line 192) | def merge_wrapper_options(options, wrapper_options)
method set_input_classes (line 211) | def set_input_classes(wrapper_options)
method i18n_scope (line 227) | def i18n_scope
FILE: lib/simple_form/inputs/block_input.rb
type SimpleForm (line 2) | module SimpleForm
type Inputs (line 3) | module Inputs
class BlockInput (line 4) | class BlockInput < Base
method initialize (line 5) | def initialize(*args, &block)
method input (line 10) | def input(wrapper_options = nil)
FILE: lib/simple_form/inputs/boolean_input.rb
type SimpleForm (line 2) | module SimpleForm
type Inputs (line 3) | module Inputs
class BooleanInput (line 4) | class BooleanInput < Base
method input (line 5) | def input(wrapper_options = nil)
method label_input (line 23) | def label_input(wrapper_options = nil)
method boolean_label_class (line 44) | def boolean_label_class
method build_check_box (line 52) | def build_check_box(unchecked_value, options)
method build_check_box_without_hidden_field (line 58) | def build_check_box_without_hidden_field(options)
method build_hidden_field_for_checkbox (line 66) | def build_hidden_field_for_checkbox
method inline_label? (line 75) | def inline_label?
method inline_label (line 79) | def inline_label
method required_by_default? (line 91) | def required_by_default?
method include_hidden? (line 95) | def include_hidden?
method checked_value (line 99) | def checked_value
method unchecked_value (line 103) | def unchecked_value
FILE: lib/simple_form/inputs/collection_check_boxes_input.rb
type SimpleForm (line 2) | module SimpleForm
type Inputs (line 3) | module Inputs
class CollectionCheckBoxesInput (line 4) | class CollectionCheckBoxesInput < CollectionRadioButtonsInput
method has_required? (line 9) | def has_required?
method build_nested_boolean_style_item_tag (line 13) | def build_nested_boolean_style_item_tag(collection_builder)
method item_wrapper_class (line 17) | def item_wrapper_class
FILE: lib/simple_form/inputs/collection_input.rb
type SimpleForm (line 2) | module SimpleForm
type Inputs (line 3) | module Inputs
class CollectionInput (line 4) | class CollectionInput < Base
method boolean_collection (line 12) | def self.boolean_collection
method input (line 17) | def input(wrapper_options = nil)
method input_options (line 22) | def input_options
method collection (line 34) | def collection
method has_required? (line 41) | def has_required?
method skip_include_blank? (line 46) | def skip_include_blank?
method multiple? (line 50) | def multiple?
method detect_collection_methods (line 59) | def detect_collection_methods
method detect_common_display_methods (line 71) | def detect_common_display_methods(collection_classes = detect_coll...
method detect_method_from_class (line 83) | def detect_method_from_class(collection_classes)
method detect_collection_classes (line 90) | def detect_collection_classes(some_collection = collection)
method collection_includes_basic_objects? (line 94) | def collection_includes_basic_objects?(collection_classes)
method translate_collection (line 98) | def translate_collection
method translate_option (line 113) | def translate_option(options, key)
FILE: lib/simple_form/inputs/collection_radio_buttons_input.rb
type SimpleForm (line 2) | module SimpleForm
type Inputs (line 3) | module Inputs
class CollectionRadioButtonsInput (line 4) | class CollectionRadioButtonsInput < CollectionInput
method input (line 5) | def input(wrapper_options = nil)
method input_options (line 17) | def input_options
method apply_default_collection_options! (line 25) | def apply_default_collection_options!(options)
method collection_block_for_nested_boolean_style (line 37) | def collection_block_for_nested_boolean_style
method build_nested_boolean_style_item_tag (line 43) | def build_nested_boolean_style_item_tag(collection_builder)
method item_wrapper_class (line 47) | def item_wrapper_class
method generate_label_for_attribute? (line 54) | def generate_label_for_attribute?
FILE: lib/simple_form/inputs/collection_select_input.rb
type SimpleForm (line 2) | module SimpleForm
type Inputs (line 3) | module Inputs
class CollectionSelectInput (line 4) | class CollectionSelectInput < CollectionInput
method input (line 5) | def input(wrapper_options = nil)
FILE: lib/simple_form/inputs/color_input.rb
type SimpleForm (line 2) | module SimpleForm
type Inputs (line 3) | module Inputs
class ColorInput (line 4) | class ColorInput < Base
method input (line 5) | def input(wrapper_options = nil)
FILE: lib/simple_form/inputs/date_time_input.rb
type SimpleForm (line 2) | module SimpleForm
type Inputs (line 3) | module Inputs
class DateTimeInput (line 4) | class DateTimeInput < Base
method input (line 5) | def input(wrapper_options = nil)
method label_target (line 17) | def label_target
method use_html5_inputs? (line 34) | def use_html5_inputs?
FILE: lib/simple_form/inputs/file_input.rb
type SimpleForm (line 2) | module SimpleForm
type Inputs (line 3) | module Inputs
class FileInput (line 4) | class FileInput < Base
method input (line 5) | def input(wrapper_options = nil)
FILE: lib/simple_form/inputs/grouped_collection_select_input.rb
type SimpleForm (line 2) | module SimpleForm
type Inputs (line 3) | module Inputs
class GroupedCollectionSelectInput (line 4) | class GroupedCollectionSelectInput < CollectionInput
method input (line 5) | def input(wrapper_options = nil)
method grouped_collection (line 17) | def grouped_collection
method collection (line 25) | def collection
method group_method (line 29) | def group_method
method group_label_method (line 33) | def group_label_method
method detect_method_from_class (line 44) | def detect_method_from_class(collection_classes)
FILE: lib/simple_form/inputs/hidden_input.rb
type SimpleForm (line 2) | module SimpleForm
type Inputs (line 3) | module Inputs
class HiddenInput (line 4) | class HiddenInput < Base
method input (line 7) | def input(wrapper_options = nil)
method required_class (line 15) | def required_class
FILE: lib/simple_form/inputs/numeric_input.rb
type SimpleForm (line 2) | module SimpleForm
type Inputs (line 3) | module Inputs
class NumericInput (line 4) | class NumericInput < Base
method input (line 7) | def input(wrapper_options = nil)
FILE: lib/simple_form/inputs/password_input.rb
type SimpleForm (line 2) | module SimpleForm
type Inputs (line 3) | module Inputs
class PasswordInput (line 4) | class PasswordInput < Base
method input (line 7) | def input(wrapper_options = nil)
FILE: lib/simple_form/inputs/priority_input.rb
type SimpleForm (line 2) | module SimpleForm
type Inputs (line 3) | module Inputs
class PriorityInput (line 4) | class PriorityInput < CollectionSelectInput
method input (line 5) | def input(wrapper_options = nil)
method input_priority (line 11) | def input_priority
method country_input (line 17) | def country_input(merged_input_options)
method time_zone_input (line 24) | def time_zone_input(merged_input_options)
method skip_include_blank? (line 32) | def skip_include_blank?
FILE: lib/simple_form/inputs/range_input.rb
type SimpleForm (line 2) | module SimpleForm
type Inputs (line 3) | module Inputs
class RangeInput (line 4) | class RangeInput < NumericInput
method input (line 5) | def input(wrapper_options = nil)
FILE: lib/simple_form/inputs/rich_text_area_input.rb
type SimpleForm (line 2) | module SimpleForm
type Inputs (line 3) | module Inputs
class RichTextAreaInput (line 4) | class RichTextAreaInput < Base
method input (line 7) | def input(wrapper_options = nil)
FILE: lib/simple_form/inputs/string_input.rb
type SimpleForm (line 2) | module SimpleForm
type Inputs (line 3) | module Inputs
class StringInput (line 4) | class StringInput < Base
method input (line 7) | def input(wrapper_options = nil)
method string? (line 20) | def string?
FILE: lib/simple_form/inputs/text_input.rb
type SimpleForm (line 2) | module SimpleForm
type Inputs (line 3) | module Inputs
class TextInput (line 4) | class TextInput < Base
method input (line 7) | def input(wrapper_options = nil)
FILE: lib/simple_form/inputs/weekday_input.rb
type SimpleForm (line 2) | module SimpleForm
type Inputs (line 3) | module Inputs
class WeekdayInput (line 4) | class WeekdayInput < CollectionSelectInput
method input (line 7) | def input(wrapper_options = nil)
FILE: lib/simple_form/map_type.rb
type SimpleForm (line 4) | module SimpleForm
type MapType (line 5) | module MapType
function extended (line 6) | def self.extended(base)
function map_type (line 11) | def map_type(*types)
FILE: lib/simple_form/railtie.rb
type SimpleForm (line 4) | module SimpleForm
class Railtie (line 5) | class Railtie < Rails::Railtie
FILE: lib/simple_form/tags.rb
type SimpleForm (line 2) | module SimpleForm
type Tags (line 3) | module Tags
type CollectionExtensions (line 4) | module CollectionExtensions
function render_collection (line 7) | def render_collection
function wrap_rendered_collection (line 29) | def wrap_rendered_collection(collection)
class CollectionRadioButtons (line 41) | class CollectionRadioButtons < ActionView::Helpers::Tags::Collection...
method render (line 44) | def render
method render_component (line 50) | def render_component(builder)
class CollectionCheckBoxes (line 57) | class CollectionCheckBoxes < ActionView::Helpers::Tags::CollectionCh...
method render (line 60) | def render
method render_component (line 66) | def render_component(builder)
FILE: lib/simple_form/version.rb
type SimpleForm (line 2) | module SimpleForm
FILE: lib/simple_form/wrappers.rb
type SimpleForm (line 2) | module SimpleForm
type Wrappers (line 3) | module Wrappers
FILE: lib/simple_form/wrappers/builder.rb
type SimpleForm (line 2) | module SimpleForm
type Wrappers (line 3) | module Wrappers
class Builder (line 43) | class Builder
method initialize (line 44) | def initialize(options)
method use (line 49) | def use(name, options = {})
method optional (line 57) | def optional(name, options = {}, &block)
method wrapper (line 62) | def wrapper(name, options = nil)
method to_a (line 75) | def to_a
FILE: lib/simple_form/wrappers/leaf.rb
type SimpleForm (line 2) | module SimpleForm
type Wrappers (line 3) | module Wrappers
class Leaf (line 4) | class Leaf
method initialize (line 7) | def initialize(namespace, options = {})
method render (line 12) | def render(input)
method find (line 24) | def find(name)
FILE: lib/simple_form/wrappers/many.rb
type SimpleForm (line 2) | module SimpleForm
type Wrappers (line 3) | module Wrappers
class Many (line 12) | class Many
method initialize (line 15) | def initialize(namespace, components, defaults = {})
method render (line 23) | def render(input)
method find (line 36) | def find(name)
method wrap (line 52) | def wrap(input, options, content)
method html_options (line 65) | def html_options(options)
method html_classes (line 69) | def html_classes(input, options)
FILE: lib/simple_form/wrappers/root.rb
type SimpleForm (line 2) | module SimpleForm
type Wrappers (line 3) | module Wrappers
class Root (line 6) | class Root < Many
method initialize (line 9) | def initialize(*args)
method render (line 14) | def render(input)
method find (line 20) | def find(name)
method html_classes (line 26) | def html_classes(input, options)
method html_class (line 37) | def html_class(key, options)
FILE: lib/simple_form/wrappers/single.rb
type SimpleForm (line 2) | module SimpleForm
type Wrappers (line 3) | module Wrappers
class Single (line 5) | class Single < Many
method initialize (line 6) | def initialize(name, wrapper_options = {}, options = {})
method render (line 12) | def render(input)
method html_options (line 22) | def html_options(options)
FILE: test/action_view_extensions/builder_test.rb
class BuilderTest (line 4) | class BuilderTest < ActionView::TestCase
method with_custom_form_for (line 5) | def with_custom_form_for(object, *args, &block)
method with_collection_radio_buttons (line 12) | def with_collection_radio_buttons(object, attribute, collection, value...
method with_collection_check_boxes (line 18) | def with_collection_check_boxes(object, attribute, collection, value_m...
FILE: test/action_view_extensions/form_helper_test.rb
class FormHelperTest (line 4) | class FormHelperTest < ActionView::TestCase
method swap_field_error_proc (line 165) | def swap_field_error_proc(expected_error_proc = -> {})
FILE: test/components/custom_components_test.rb
type Numbers (line 6) | module Numbers
function number (line 7) | def number(wrapper_options = nil)
type InputGroup (line 13) | module InputGroup
function prepend (line 14) | def prepend(wrapper_options = nil)
function append (line 19) | def append(wrapper_options = nil)
class CustomComponentsTest (line 25) | class CustomComponentsTest < ActionView::TestCase
FILE: test/components/label_test.rb
class IsolatedLabelTest (line 6) | class IsolatedLabelTest < ActionView::TestCase
method with_label_for (line 7) | def with_label_for(object, attribute_name, type, options = {})
method action_name (line 60) | def @controller.action_name; nil; end
FILE: test/form_builder/association_test.rb
class AssociationTest (line 5) | class AssociationTest < ActionView::TestCase
method with_association_for (line 6) | def with_association_for(object, *args)
FILE: test/form_builder/button_test.rb
class ButtonTest (line 5) | class ButtonTest < ActionView::TestCase
method with_button_for (line 6) | def with_button_for(object, *args)
FILE: test/form_builder/error_notification_test.rb
class ErrorNotificationTest (line 6) | class ErrorNotificationTest < ActionView::TestCase
method with_error_notification_for (line 7) | def with_error_notification_for(object, options = {}, &block)
FILE: test/form_builder/error_test.rb
class ErrorTest (line 5) | class ErrorTest < ActionView::TestCase
method with_error_for (line 6) | def with_error_for(object, *args)
method with_full_error_for (line 12) | def with_full_error_for(object, *args)
FILE: test/form_builder/general_test.rb
class FormBuilderTest (line 5) | class FormBuilderTest < ActionView::TestCase
method with_custom_form_for (line 6) | def with_custom_form_for(object, *args, &block)
FILE: test/form_builder/hint_test.rb
class HintTest (line 5) | class HintTest < ActionView::TestCase
method with_hint_for (line 6) | def with_hint_for(object, *args)
FILE: test/form_builder/input_field_test.rb
class InputFieldTest (line 5) | class InputFieldTest < ActionView::TestCase
FILE: test/form_builder/label_test.rb
class LabelTest (line 5) | class LabelTest < ActionView::TestCase
method with_label_for (line 6) | def with_label_for(object, *args, &block)
FILE: test/form_builder/wrapper_test.rb
class WrapperTest (line 4) | class WrapperTest < ActionView::TestCase
FILE: test/generators/simple_form_generator_test.rb
class SimpleFormGeneratorTest (line 4) | class SimpleFormGeneratorTest < Rails::Generators::TestCase
FILE: test/inputs/boolean_input_test.rb
class BooleanInputTest (line 5) | class BooleanInputTest < ActionView::TestCase
FILE: test/inputs/collection_check_boxes_input_test.rb
class CollectionCheckBoxesInputTest (line 5) | class CollectionCheckBoxesInputTest < ActionView::TestCase
FILE: test/inputs/collection_radio_buttons_input_test.rb
class CollectionRadioButtonsInputTest (line 5) | class CollectionRadioButtonsInputTest < ActionView::TestCase
FILE: test/inputs/collection_select_input_test.rb
class CollectionSelectInputTest (line 5) | class CollectionSelectInputTest < ActionView::TestCase
FILE: test/inputs/color_input_test.rb
class ColorInputTest (line 5) | class ColorInputTest < ActionView::TestCase
FILE: test/inputs/country_input_test.rb
class CountryInputTest (line 5) | class CountryInputTest < ActionView::TestCase
FILE: test/inputs/datetime_input_test.rb
class DateTimeInputWithHtml5Test (line 6) | class DateTimeInputWithHtml5Test < ActionView::TestCase
class DateTimeInputWithoutHtml5Test (line 50) | class DateTimeInputWithoutHtml5Test < ActionView::TestCase
FILE: test/inputs/disabled_test.rb
class DisabledTest (line 4) | class DisabledTest < ActionView::TestCase
FILE: test/inputs/discovery_test.rb
class DiscoveryTest (line 4) | class DiscoveryTest < ActionView::TestCase
method discovery (line 6) | def discovery(value = false)
FILE: test/inputs/file_input_test.rb
class FileInputTest (line 5) | class FileInputTest < ActionView::TestCase
FILE: test/inputs/general_test.rb
class InputTest (line 5) | class InputTest < ActionView::TestCase
FILE: test/inputs/grouped_collection_select_input_test.rb
class GroupedCollectionSelectInputTest (line 5) | class GroupedCollectionSelectInputTest < ActionView::TestCase
FILE: test/inputs/hidden_input_test.rb
class HiddenInputTest (line 5) | class HiddenInputTest < ActionView::TestCase
FILE: test/inputs/numeric_input_test.rb
class NumericInputTest (line 5) | class NumericInputTest < ActionView::TestCase
FILE: test/inputs/readonly_test.rb
class ReadonlyTest (line 4) | class ReadonlyTest < ActionView::TestCase
FILE: test/inputs/required_test.rb
class RequiredTest (line 4) | class RequiredTest < ActionView::TestCase
FILE: test/inputs/rich_text_area_input_test.rb
class RichTextAreaInputTest (line 5) | class RichTextAreaInputTest < ActionView::TestCase
FILE: test/inputs/string_input_test.rb
class StringInputTest (line 5) | class StringInputTest < ActionView::TestCase
FILE: test/inputs/text_input_test.rb
class TextInputTest (line 5) | class TextInputTest < ActionView::TestCase
FILE: test/inputs/time_zone_input_test.rb
class TimeZoneInputTest (line 5) | class TimeZoneInputTest < ActionView::TestCase
FILE: test/inputs/weekday_input_test.rb
class WeekdayInputTest (line 5) | class WeekdayInputTest < ActionView::TestCase
FILE: test/simple_form_test.rb
class SimpleFormTest (line 4) | class SimpleFormTest < ActiveSupport::TestCase
FILE: test/support/discovery_inputs.rb
class StringInput (line 2) | class StringInput < SimpleForm::Inputs::StringInput
method input (line 3) | def input(wrapper_options = nil)
class NumericInput (line 8) | class NumericInput < SimpleForm::Inputs::NumericInput
method input (line 9) | def input(wrapper_options = nil)
class CustomizedInput (line 14) | class CustomizedInput < SimpleForm::Inputs::StringInput
method input (line 15) | def input(wrapper_options = nil)
method input_method (line 19) | def input_method
class DeprecatedInput (line 24) | class DeprecatedInput < SimpleForm::Inputs::StringInput
method input (line 25) | def input
method input_method (line 29) | def input_method
class CollectionSelectInput (line 34) | class CollectionSelectInput < SimpleForm::Inputs::CollectionSelectInput
method input_html_classes (line 35) | def input_html_classes
class FileInput (line 40) | class FileInput < SimpleForm::Inputs::FileInput
method input_html_classes (line 41) | def input_html_classes
type CustomInputs (line 47) | module CustomInputs
class CustomizedInput (line 48) | class CustomizedInput < SimpleForm::Inputs::StringInput
method input_html_classes (line 49) | def input_html_classes
class PasswordInput (line 54) | class PasswordInput < SimpleForm::Inputs::PasswordInput
method input_html_classes (line 55) | def input_html_classes
class NumericInput (line 60) | class NumericInput < SimpleForm::Inputs::PasswordInput
method input_html_classes (line 61) | def input_html_classes
FILE: test/support/misc_helpers.rb
type MiscHelpers (line 2) | module MiscHelpers
function store_translations (line 3) | def store_translations(locale, translations, &block)
function assert_no_select (line 11) | def assert_no_select(selector, value = nil)
function swap (line 15) | def swap(object, new_values)
function stub_any_instance (line 28) | def stub_any_instance(klass, method, value)
function swap_wrapper (line 50) | def swap_wrapper(name = :default, wrapper = custom_wrapper)
function custom_wrapper (line 58) | def custom_wrapper
function custom_wrapper_with_wrapped_optional_component (line 72) | def custom_wrapper_with_wrapped_optional_component
function custom_wrapper_with_unless_blank (line 80) | def custom_wrapper_with_unless_blank
function custom_wrapper_with_input_class (line 88) | def custom_wrapper_with_input_class
function custom_wrapper_with_input_data_modal (line 95) | def custom_wrapper_with_input_data_modal
function custom_wrapper_with_input_aria_modal (line 102) | def custom_wrapper_with_input_aria_modal
function custom_wrapper_with_label_class (line 109) | def custom_wrapper_with_label_class
function custom_wrapper_with_input_attributes (line 116) | def custom_wrapper_with_input_attributes
function custom_wrapper_with_label_input_class (line 122) | def custom_wrapper_with_label_input_class
function custom_wrapper_with_wrapped_input (line 128) | def custom_wrapper_with_wrapped_input
function custom_wrapper_with_wrapped_label (line 137) | def custom_wrapper_with_wrapped_label
function custom_wrapper_without_top_level (line 146) | def custom_wrapper_without_top_level
function custom_wrapper_without_class (line 154) | def custom_wrapper_without_class
function custom_wrapper_with_label_html_option (line 160) | def custom_wrapper_with_label_html_option
function custom_wrapper_with_wrapped_label_input (line 166) | def custom_wrapper_with_wrapped_label_input
function custom_wrapper_with_additional_attributes (line 172) | def custom_wrapper_with_additional_attributes
function custom_wrapper_with_full_error (line 178) | def custom_wrapper_with_full_error
function custom_wrapper_with_label_text (line 184) | def custom_wrapper_with_label_text
function custom_wrapper_with_custom_label_component (line 190) | def custom_wrapper_with_custom_label_component
function custom_wrapper_with_html5_components (line 196) | def custom_wrapper_with_html5_components
function custom_wrapper_with_required_input (line 202) | def custom_wrapper_with_required_input
function custom_wrapper_with_input_error_class (line 209) | def custom_wrapper_with_input_error_class
function custom_wrapper_with_input_valid_class (line 216) | def custom_wrapper_with_input_valid_class(valid_class: :field_without_...
function custom_form_for (line 223) | def custom_form_for(object, *args, &block)
function custom_mapping_form_for (line 227) | def custom_mapping_form_for(object, *args, &block)
function with_concat_form_for (line 231) | def with_concat_form_for(*args, &block)
function with_concat_fields_for (line 235) | def with_concat_fields_for(*args, &block)
function with_concat_custom_form_for (line 239) | def with_concat_custom_form_for(*args, &block)
function with_concat_custom_mapping_form_for (line 243) | def with_concat_custom_mapping_form_for(*args, &block)
function with_form_for (line 247) | def with_form_for(object, *args, &block)
function with_input_for (line 253) | def with_input_for(object, attribute_name, type, options = {})
function with_input_field_for (line 259) | def with_input_field_for(object, *args)
class CustomFormBuilder (line 266) | class CustomFormBuilder < SimpleForm::FormBuilder
method input (line 267) | def input(attribute_name, *args, &block)
class CustomMapTypeFormBuilder (line 272) | class CustomMapTypeFormBuilder < SimpleForm::FormBuilder
FILE: test/support/mock_controller.rb
class MockController (line 2) | class MockController
method _routes (line 5) | def _routes
method action_name (line 9) | def action_name
method url_for (line 13) | def url_for(*)
method url_options (line 17) | def url_options
method polymorphic_mappings (line 21) | def polymorphic_mappings(*); {}; end
method hash_for_user_path (line 23) | def hash_for_user_path(*); end
method hash_for_validating_user_path (line 25) | def hash_for_validating_user_path(*); end
method hash_for_other_validating_user_path (line 27) | def hash_for_other_validating_user_path(*); end
method hash_for_users_path (line 29) | def hash_for_users_path(*); end
FILE: test/support/models.rb
function where (line 10) | def where(conditions = nil)
function order (line 14) | def order(conditions = nil)
function to_model (line 23) | def to_model
function where (line 32) | def self.where(conditions = nil)
function all (line 40) | def self.all
function _relation (line 53) | def self._relation
function all (line 57) | def self.all
function persisted? (line 61) | def persisted?
function all (line 70) | def self.all
function persisted? (line 74) | def persisted?
class Tag (line 79) | class Tag < Company
method group_method (line 80) | def group_method
class User (line 87) | class User
method build (line 100) | def self.build(extra_attributes = {})
method initialize (line 111) | def initialize(options = {})
method new_record! (line 118) | def new_record!
method persisted? (line 122) | def persisted?
method company_attributes= (line 126) | def company_attributes=(*)
method tags_attributes= (line 129) | def tags_attributes=(*)
method column_for_attribute (line 132) | def column_for_attribute(attribute)
class ::ActiveModel::Type::Text (line 160) | class ::ActiveModel::Type::Text < ActiveModel::Type::String
method type (line 161) | def type; :text; end
method type_for_attribute (line 165) | def type_for_attribute(attribute)
method has_attribute? (line 194) | def has_attribute?(attribute)
method human_attribute_name (line 205) | def self.human_attribute_name(attribute, options = {})
method reflect_on_association (line 220) | def self.reflect_on_association(association)
method errors (line 243) | def errors
method readonly_attributes (line 256) | def self.readonly_attributes
class ValidatingUser (line 261) | class ValidatingUser < User
method min_amount (line 294) | def min_amount
method max_amount (line 298) | def max_amount
method min_attempts (line 302) | def min_attempts
method max_attempts (line 306) | def max_attempts
class OtherValidatingUser (line 311) | class OtherValidatingUser < User
class HashBackedAuthor (line 331) | class HashBackedAuthor < Hash
method persisted? (line 335) | def persisted?; false; end
method name (line 337) | def name
class UserNumber1And2 (line 342) | class UserNumber1And2 < User
class UserWithAttachment (line 345) | class UserWithAttachment < User
method avatar_attachment (line 346) | def avatar_attachment
method avatars_attachments (line 350) | def avatars_attachments
method remote_cover_url (line 354) | def remote_cover_url
method profile_image_attacher (line 358) | def profile_image_attacher
method portrait_file_name (line 362) | def portrait_file_name
FILE: test/test_helper.rb
type Rails (line 13) | module Rails
function env (line 14) | def self.env
class ActionView::TestCase (line 40) | class ActionView::TestCase
method set_controller (line 49) | def set_controller
method setup_users (line 53) | def setup_users(extra_attributes = {})
method protect_against_forgery? (line 74) | def protect_against_forgery?
method user_path (line 78) | def user_path(*args)
method company_user_path (line 82) | def company_user_path(*args)
Condensed preview — 122 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (472K chars).
[
{
"path": ".github/code-scanning.yml",
"chars": 26,
"preview": "paths-ignore:\n - test/**\n"
},
{
"path": ".github/workflows/test.yml",
"chars": 1796,
"preview": "name: Test\n\npermissions:\n contents: read\non:\n push:\n branches:\n - main\n pull_request:\n workflow_dispatch:\njo"
},
{
"path": ".gitignore",
"chars": 44,
"preview": ".bundle/\npkg/\nrdoc/\ngemfiles/*.lock\n/.idea/\n"
},
{
"path": "CHANGELOG.md",
"chars": 14169,
"preview": "## 5.4.1\n\n* Ruby 4.0 support (no changes required)\n* Support procs on validators for minlength/maxlength, and improve va"
},
{
"path": "CONTRIBUTING.md",
"chars": 872,
"preview": "## Contributing\n\n1. If you have any questions about Simple Form, search the\n[Wiki](https://github.com/heartcombo/simple_"
},
{
"path": "Gemfile",
"chars": 126,
"preview": "source \"https://rubygems.org\"\n\ngemspec\n\ngem \"activemodel\", \"~> 8.1.0\"\ngem \"actionpack\", \"~> 8.1.0\"\ngem \"railties\", \"~> 8"
},
{
"path": "ISSUE_TEMPLATE.md",
"chars": 651,
"preview": "## Precheck\n\n- Do not use the issues tracker for help or support, try Stack Overflow.\n- For bugs, do a quick search and "
},
{
"path": "MIT-LICENSE",
"chars": 1128,
"preview": "Copyright (c) 2020-CURRENT Rafael França, Carlos Antonio da Silva\nCopyright (c) 2009-2019 Plataformatec\n\nPermission is h"
},
{
"path": "README.md",
"chars": 44019,
"preview": "\n\n[)\n\nrequire 'bundler/setup'\nrequire 'r"
},
{
"path": "gemfiles/Gemfile-rails-7-0",
"chars": 137,
"preview": "source \"https://rubygems.org\"\n\ngemspec path: \"..\"\n\ngem \"activemodel\", \"~> 7.0.0\"\ngem \"actionpack\", \"~> 7.0.0\"\ngem \"railt"
},
{
"path": "gemfiles/Gemfile-rails-7-1",
"chars": 137,
"preview": "source \"https://rubygems.org\"\n\ngemspec path: \"..\"\n\ngem \"activemodel\", \"~> 7.1.0\"\ngem \"actionpack\", \"~> 7.1.0\"\ngem \"railt"
},
{
"path": "gemfiles/Gemfile-rails-7-2",
"chars": 137,
"preview": "source \"https://rubygems.org\"\n\ngemspec path: \"..\"\n\ngem \"activemodel\", \"~> 7.2.0\"\ngem \"actionpack\", \"~> 7.2.0\"\ngem \"railt"
},
{
"path": "gemfiles/Gemfile-rails-8-0",
"chars": 137,
"preview": "source \"https://rubygems.org\"\n\ngemspec path: \"..\"\n\ngem \"activemodel\", \"~> 8.0.0\"\ngem \"actionpack\", \"~> 8.0.0\"\ngem \"railt"
},
{
"path": "gemfiles/Gemfile-rails-main",
"chars": 231,
"preview": "source \"https://rubygems.org\"\n\ngemspec path: \"..\"\n\ngem \"railties\", github: \"rails/rails\", branch: \"main\"\ngem \"activemode"
},
{
"path": "lib/generators/simple_form/USAGE",
"chars": 133,
"preview": "To copy a SimpleForm initializer to your Rails App, with some configuration values, just do:\n\n rails generate simple_"
},
{
"path": "lib/generators/simple_form/install_generator.rb",
"chars": 1573,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Generators\n class InstallGenerator < Rails::Generators::Base"
},
{
"path": "lib/generators/simple_form/templates/README",
"chars": 411,
"preview": "===============================================================================\n\n Be sure to have a copy of the Bootstr"
},
{
"path": "lib/generators/simple_form/templates/_form.html.erb",
"chars": 497,
"preview": "<%# frozen_string_literal: true %>\n<%%= simple_form_for(@<%= singular_table_name %>) do |f| %>\n <%%= f.error_notificati"
},
{
"path": "lib/generators/simple_form/templates/_form.html.haml",
"chars": 408,
"preview": "-# frozen_string_literal: true\n= simple_form_for(@<%= singular_table_name %>) do |f|\n = f.error_notification\n = f.erro"
},
{
"path": "lib/generators/simple_form/templates/_form.html.slim",
"chars": 373,
"preview": "= simple_form_for(@<%= singular_table_name %>) do |f|\n = f.error_notification\n = f.error_notification message: f.objec"
},
{
"path": "lib/generators/simple_form/templates/config/initializers/simple_form.rb",
"chars": 6905,
"preview": "# frozen_string_literal: true\n#\n# Uncomment this and change the path if necessary to include your own\n# components.\n# Se"
},
{
"path": "lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb",
"chars": 14068,
"preview": "# frozen_string_literal: true\n\n# These defaults are defined and maintained by the community at\n# https://github.com/hear"
},
{
"path": "lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb",
"chars": 4339,
"preview": "# frozen_string_literal: true\n#\n# Uncomment this and change the path if necessary to include your own\n# components.\n# Se"
},
{
"path": "lib/generators/simple_form/templates/config/locales/simple_form.en.yml",
"chars": 838,
"preview": "en:\n simple_form:\n \"yes\": 'Yes'\n \"no\": 'No'\n required:\n text: 'required'\n mark: '*'\n # You can "
},
{
"path": "lib/simple_form/action_view_extensions/builder.rb",
"chars": 1222,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module ActionViewExtensions\n # A collection of methods required by "
},
{
"path": "lib/simple_form/action_view_extensions/form_helper.rb",
"chars": 2345,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module ActionViewExtensions\n # This module creates SimpleForm wrapp"
},
{
"path": "lib/simple_form/components/errors.rb",
"chars": 1829,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Components\n module Errors\n def error(wrapper_options = "
},
{
"path": "lib/simple_form/components/hints.rb",
"chars": 547,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Components\n # Needs to be enabled in order to do automatic l"
},
{
"path": "lib/simple_form/components/html5.rb",
"chars": 783,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Components\n module HTML5\n def initialize(*)\n @ht"
},
{
"path": "lib/simple_form/components/label_input.rb",
"chars": 837,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Components\n module LabelInput\n extend ActiveSupport::Co"
},
{
"path": "lib/simple_form/components/labels.rb",
"chars": 2605,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Components\n module Labels\n extend ActiveSupport::Concer"
},
{
"path": "lib/simple_form/components/maxlength.rb",
"chars": 942,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Components\n # Needs to be enabled in order to do automatic l"
},
{
"path": "lib/simple_form/components/min_max.rb",
"chars": 1176,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Components\n module MinMax\n def min_max(wrapper_options "
},
{
"path": "lib/simple_form/components/minlength.rb",
"chars": 933,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Components\n # Needs to be enabled in order to do automatic l"
},
{
"path": "lib/simple_form/components/pattern.rb",
"chars": 661,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Components\n # Needs to be enabled in order to do automatic l"
},
{
"path": "lib/simple_form/components/placeholders.rb",
"chars": 494,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Components\n # Needs to be enabled in order to do automatic l"
},
{
"path": "lib/simple_form/components/readonly.rb",
"chars": 600,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Components\n # Needs to be enabled in order to do automatic l"
},
{
"path": "lib/simple_form/components.rb",
"chars": 782,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n # Components are a special type of helpers that can work on their own."
},
{
"path": "lib/simple_form/error_notification.rb",
"chars": 1153,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n class ErrorNotification\n delegate :object, :object_name, :template,"
},
{
"path": "lib/simple_form/form_builder.rb",
"chars": 28692,
"preview": "# frozen_string_literal: true\nrequire 'active_support/core_ext/object/deep_dup'\nrequire 'simple_form/map_type'\nrequire '"
},
{
"path": "lib/simple_form/helpers/autofocus.rb",
"chars": 190,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Helpers\n module Autofocus\n private\n\n def has_autof"
},
{
"path": "lib/simple_form/helpers/disabled.rb",
"chars": 258,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Helpers\n module Disabled\n private\n\n def has_disabl"
},
{
"path": "lib/simple_form/helpers/readonly.rb",
"chars": 258,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Helpers\n module Readonly\n private\n\n def readonly_c"
},
{
"path": "lib/simple_form/helpers/required.rb",
"chars": 827,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Helpers\n module Required\n private\n\n def required_f"
},
{
"path": "lib/simple_form/helpers/validators.rb",
"chars": 1717,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Helpers\n module Validators\n def has_validators?\n "
},
{
"path": "lib/simple_form/helpers.rb",
"chars": 591,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n # Helpers are made of several helpers that cannot be turned on automat"
},
{
"path": "lib/simple_form/inputs/base.rb",
"chars": 7752,
"preview": "# frozen_string_literal: true\nrequire 'active_support/core_ext/string/output_safety'\nrequire 'action_view/helpers'\n\nmodu"
},
{
"path": "lib/simple_form/inputs/block_input.rb",
"chars": 277,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Inputs\n class BlockInput < Base\n def initialize(*args, "
},
{
"path": "lib/simple_form/inputs/boolean_input.rb",
"chars": 3831,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Inputs\n class BooleanInput < Base\n def input(wrapper_op"
},
{
"path": "lib/simple_form/inputs/collection_check_boxes_input.rb",
"chars": 572,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Inputs\n class CollectionCheckBoxesInput < CollectionRadioBut"
},
{
"path": "lib/simple_form/inputs/collection_input.rb",
"chars": 4168,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Inputs\n class CollectionInput < Base\n BASIC_OBJECT_CLAS"
},
{
"path": "lib/simple_form/inputs/collection_radio_buttons_input.rb",
"chars": 1999,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Inputs\n class CollectionRadioButtonsInput < CollectionInput\n"
},
{
"path": "lib/simple_form/inputs/collection_select_input.rb",
"chars": 494,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Inputs\n class CollectionSelectInput < CollectionInput\n "
},
{
"path": "lib/simple_form/inputs/color_input.rb",
"chars": 373,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Inputs\n class ColorInput < Base\n def input(wrapper_opti"
},
{
"path": "lib/simple_form/inputs/date_time_input.rb",
"chars": 1038,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Inputs\n class DateTimeInput < Base\n def input(wrapper_o"
},
{
"path": "lib/simple_form/inputs/file_input.rb",
"chars": 315,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Inputs\n class FileInput < Base\n def input(wrapper_optio"
},
{
"path": "lib/simple_form/inputs/grouped_collection_select_input.rb",
"chars": 1824,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Inputs\n class GroupedCollectionSelectInput < CollectionInput"
},
{
"path": "lib/simple_form/inputs/hidden_input.rb",
"chars": 431,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Inputs\n class HiddenInput < Base\n disable :label, :erro"
},
{
"path": "lib/simple_form/inputs/numeric_input.rb",
"chars": 542,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Inputs\n class NumericInput < Base\n enable :placeholder,"
},
{
"path": "lib/simple_form/inputs/password_input.rb",
"chars": 374,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Inputs\n class PasswordInput < Base\n enable :placeholder"
},
{
"path": "lib/simple_form/inputs/priority_input.rb",
"chars": 1051,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Inputs\n class PriorityInput < CollectionSelectInput\n de"
},
{
"path": "lib/simple_form/inputs/range_input.rb",
"chars": 302,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Inputs\n class RangeInput < NumericInput\n def input(wrap"
},
{
"path": "lib/simple_form/inputs/rich_text_area_input.rb",
"chars": 354,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Inputs\n class RichTextAreaInput < Base\n enable :placeho"
},
{
"path": "lib/simple_form/inputs/string_input.rb",
"chars": 621,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Inputs\n class StringInput < Base\n enable :placeholder, "
},
{
"path": "lib/simple_form/inputs/text_input.rb",
"chars": 365,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Inputs\n class TextInput < Base\n enable :placeholder, :m"
},
{
"path": "lib/simple_form/inputs/weekday_input.rb",
"chars": 381,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Inputs\n class WeekdayInput < CollectionSelectInput\n ena"
},
{
"path": "lib/simple_form/inputs.rb",
"chars": 692,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Inputs\n extend ActiveSupport::Autoload\n\n autoload :Base\n "
},
{
"path": "lib/simple_form/map_type.rb",
"chars": 476,
"preview": "# frozen_string_literal: true\nrequire 'active_support/core_ext/class/attribute'\n\nmodule SimpleForm\n module MapType\n "
},
{
"path": "lib/simple_form/railtie.rb",
"chars": 611,
"preview": "# frozen_string_literal: true\nrequire 'rails/railtie'\n\nmodule SimpleForm\n class Railtie < Rails::Railtie\n config.eag"
},
{
"path": "lib/simple_form/tags.rb",
"chars": 2369,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Tags\n module CollectionExtensions\n private\n\n def r"
},
{
"path": "lib/simple_form/version.rb",
"chars": 79,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n VERSION = \"5.4.1\".freeze\nend\n"
},
{
"path": "lib/simple_form/wrappers/builder.rb",
"chars": 2684,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Wrappers\n # Provides the builder syntax for components. The "
},
{
"path": "lib/simple_form/wrappers/leaf.rb",
"chars": 601,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Wrappers\n class Leaf\n attr_reader :namespace\n\n def"
},
{
"path": "lib/simple_form/wrappers/many.rb",
"chars": 2243,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Wrappers\n # A wrapper is an object that holds several compon"
},
{
"path": "lib/simple_form/wrappers/root.rb",
"chars": 1318,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Wrappers\n # `Root` is the root wrapper for all components. I"
},
{
"path": "lib/simple_form/wrappers/single.rb",
"chars": 684,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Wrappers\n # `Single` is an optimization for a wrapper that h"
},
{
"path": "lib/simple_form/wrappers.rb",
"chars": 336,
"preview": "# frozen_string_literal: true\nmodule SimpleForm\n module Wrappers\n autoload :Builder, 'simple_form/wrappers/builder'\n"
},
{
"path": "lib/simple_form.rb",
"chars": 11300,
"preview": "# frozen_string_literal: true\nrequire 'action_view'\nrequire 'action_pack'\nrequire 'simple_form/action_view_extensions/fo"
},
{
"path": "simple_form.gemspec",
"chars": 1429,
"preview": "# -*- encoding: utf-8 -*-\n$:.push File.expand_path(\"../lib\", __FILE__)\nrequire \"simple_form/version\"\n\nGem::Specification"
},
{
"path": "test/action_view_extensions/builder_test.rb",
"chars": 26151,
"preview": "# frozen_string_literal: true\nrequire 'test_helper'\n\nclass BuilderTest < ActionView::TestCase\n def with_custom_form_for"
},
{
"path": "test/action_view_extensions/form_helper_test.rb",
"chars": 5529,
"preview": "# frozen_string_literal: true\nrequire 'test_helper'\n\nclass FormHelperTest < ActionView::TestCase\n\n test 'SimpleForm for"
},
{
"path": "test/components/custom_components_test.rb",
"chars": 1882,
"preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\n# Module that represents a custom component.\nmodule Numbers\n def "
},
{
"path": "test/components/label_test.rb",
"chars": 12926,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\n# Isolated tests for label without triggering f.l"
},
{
"path": "test/form_builder/association_test.rb",
"chars": 9764,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\nclass AssociationTest < ActionView::TestCase\n de"
},
{
"path": "test/form_builder/button_test.rb",
"chars": 1503,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\nclass ButtonTest < ActionView::TestCase\n def wit"
},
{
"path": "test/form_builder/error_notification_test.rb",
"chars": 3075,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\n# Tests for f.error_notification\nclass ErrorNotif"
},
{
"path": "test/form_builder/error_test.rb",
"chars": 9732,
"preview": "# frozen_string_literal: true\nrequire 'test_helper'\n\n# Tests for f.error and f.full_error\nclass ErrorTest < ActionView::"
},
{
"path": "test/form_builder/general_test.rb",
"chars": 19327,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\nclass FormBuilderTest < ActionView::TestCase\n de"
},
{
"path": "test/form_builder/hint_test.rb",
"chars": 4828,
"preview": "# frozen_string_literal: true\nrequire 'test_helper'\n\n# Tests for f.hint\nclass HintTest < ActionView::TestCase\n def with"
},
{
"path": "test/form_builder/input_field_test.rb",
"chars": 6330,
"preview": "# frozen_string_literal: true\nrequire 'test_helper'\n\n# Tests for f.input_field\nclass InputFieldTest < ActionView::TestCa"
},
{
"path": "test/form_builder/label_test.rb",
"chars": 4888,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\nclass LabelTest < ActionView::TestCase\n def with"
},
{
"path": "test/form_builder/wrapper_test.rb",
"chars": 13274,
"preview": "# frozen_string_literal: true\nrequire 'test_helper'\n\nclass WrapperTest < ActionView::TestCase\n test 'wrapper does not h"
},
{
"path": "test/generators/simple_form_generator_test.rb",
"chars": 1715,
"preview": "# frozen_string_literal: true\nrequire 'test_helper'\n\nclass SimpleFormGeneratorTest < Rails::Generators::TestCase\n tests"
},
{
"path": "test/inputs/boolean_input_test.rb",
"chars": 10361,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\nclass BooleanInputTest < ActionView::TestCase\n t"
},
{
"path": "test/inputs/collection_check_boxes_input_test.rb",
"chars": 12403,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\nclass CollectionCheckBoxesInputTest < ActionView:"
},
{
"path": "test/inputs/collection_radio_buttons_input_test.rb",
"chars": 17708,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\nclass CollectionRadioButtonsInputTest < ActionVie"
},
{
"path": "test/inputs/collection_select_input_test.rb",
"chars": 15644,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\nclass CollectionSelectInputTest < ActionView::Tes"
},
{
"path": "test/inputs/color_input_test.rb",
"chars": 264,
"preview": "# frozen_string_literal: true\n\nrequire 'test_helper'\n\nclass ColorInputTest < ActionView::TestCase\n test 'input generate"
},
{
"path": "test/inputs/country_input_test.rb",
"chars": 1324,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\nclass CountryInputTest < ActionView::TestCase\n C"
},
{
"path": "test/inputs/datetime_input_test.rb",
"chars": 6255,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\n# Tests for datetime, date and time inputs when H"
},
{
"path": "test/inputs/disabled_test.rb",
"chars": 3501,
"preview": "# frozen_string_literal: true\nrequire 'test_helper'\n\nclass DisabledTest < ActionView::TestCase\n test 'string input is d"
},
{
"path": "test/inputs/discovery_test.rb",
"chars": 4583,
"preview": "# frozen_string_literal: true\nrequire 'test_helper'\n\nclass DiscoveryTest < ActionView::TestCase\n # Setup new inputs and"
},
{
"path": "test/inputs/file_input_test.rb",
"chars": 514,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\nclass FileInputTest < ActionView::TestCase\n test"
},
{
"path": "test/inputs/general_test.rb",
"chars": 4968,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\nclass InputTest < ActionView::TestCase\n test 'in"
},
{
"path": "test/inputs/grouped_collection_select_input_test.rb",
"chars": 6406,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\nclass GroupedCollectionSelectInputTest < ActionVi"
},
{
"path": "test/inputs/hidden_input_test.rb",
"chars": 951,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\nclass HiddenInputTest < ActionView::TestCase\n te"
},
{
"path": "test/inputs/numeric_input_test.rb",
"chars": 6153,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\nclass NumericInputTest < ActionView::TestCase\n t"
},
{
"path": "test/inputs/readonly_test.rb",
"chars": 4052,
"preview": "# frozen_string_literal: true\nrequire 'test_helper'\n\nclass ReadonlyTest < ActionView::TestCase\n test 'string input gene"
},
{
"path": "test/inputs/required_test.rb",
"chars": 6138,
"preview": "# frozen_string_literal: true\nrequire 'test_helper'\n\nclass RequiredTest < ActionView::TestCase\n # REQUIRED AND PRESENCE"
},
{
"path": "test/inputs/rich_text_area_input_test.rb",
"chars": 525,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\nclass RichTextAreaInputTest < ActionView::TestCas"
},
{
"path": "test/inputs/string_input_test.rb",
"chars": 6189,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\nclass StringInputTest < ActionView::TestCase\n te"
},
{
"path": "test/inputs/text_input_test.rb",
"chars": 1505,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\nclass TextInputTest < ActionView::TestCase\n test"
},
{
"path": "test/inputs/time_zone_input_test.rb",
"chars": 1140,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\nclass TimeZoneInputTest < ActionView::TestCase\n "
},
{
"path": "test/inputs/weekday_input_test.rb",
"chars": 487,
"preview": "# frozen_string_literal: true\n# encoding: UTF-8\nrequire 'test_helper'\n\nclass WeekdayInputTest < ActionView::TestCase\n t"
},
{
"path": "test/simple_form_test.rb",
"chars": 404,
"preview": "# frozen_string_literal: true\nrequire 'test_helper'\n\nclass SimpleFormTest < ActiveSupport::TestCase\n test 'setup block "
},
{
"path": "test/support/discovery_inputs.rb",
"chars": 1432,
"preview": "# frozen_string_literal: true\nclass StringInput < SimpleForm::Inputs::StringInput\n def input(wrapper_options = nil)\n "
},
{
"path": "test/support/misc_helpers.rb",
"chars": 7767,
"preview": "# frozen_string_literal: true\nmodule MiscHelpers\n def store_translations(locale, translations, &block)\n I18n.backend"
},
{
"path": "test/support/mock_controller.rb",
"chars": 476,
"preview": "# frozen_string_literal: true\nclass MockController\n attr_writer :action_name\n\n def _routes\n self\n end\n\n def actio"
},
{
"path": "test/support/models.rb",
"chars": 9747,
"preview": "# frozen_string_literal: true\nAssociation = Struct.new(:klass, :name, :macro, :scope, :options)\n\nColumn = Struct.new(:na"
},
{
"path": "test/test_helper.rb",
"chars": 2141,
"preview": "# frozen_string_literal: true\nrequire 'minitest/autorun'\n\nrequire 'active_model'\nrequire 'action_controller'\nrequire 'ac"
}
]
About this extraction
This page contains the full source code of the heartcombo/simple_form GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 122 files (438.9 KB), approximately 115.7k tokens, and a symbol index with 590 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.