simple_form 3.0.0 → 3.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +22 -0
- data/lib/simple_form/components/errors.rb +7 -1
- data/lib/simple_form/components/hints.rb +7 -2
- data/lib/simple_form/components/labels.rb +1 -1
- data/lib/simple_form/form_builder.rb +4 -2
- data/lib/simple_form/inputs/base.rb +4 -1
- data/lib/simple_form/inputs/boolean_input.rb +1 -1
- data/lib/simple_form/version.rb +1 -1
- data/test/form_builder/error_test.rb +54 -7
- data/test/form_builder/general_test.rb +2 -2
- data/test/form_builder/hint_test.rb +8 -2
- data/test/form_builder/input_field_test.rb +40 -0
- data/test/form_builder/label_test.rb +10 -0
- data/test/support/misc_helpers.rb +6 -0
- data/test/test_helper.rb +2 -0
- metadata +21 -35
- data/lib/simple_form/action_view_extensions/builder.rb.orig +0 -247
- data/lib/simple_form/form_builder.rb.orig +0 -486
- data/lib/simple_form/version.rb.orig +0 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ffed0afbf1694b8176a255206fc6d658e4a539af
|
|
4
|
+
data.tar.gz: 7af2a7a0199551b2b17491d9510c2477aa125265
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bbf22389176622fe689c4d401526d5c973124245cb3b587b3ff2428053377930a358c31eaf8088ffb2b030cef016d0b59ee790984fdedd1219c4d3daab76ff23
|
|
7
|
+
data.tar.gz: a3e826bd625da954e73d5df1cfe546117024c5b7eb08a1ea9eb37e187e25b3479d9549a13b6e853520e68e8eb3d54ed1ead89e2f0de7318df3478d624654c611
|
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,25 @@
|
|
|
1
|
+
## 3.0.4
|
|
2
|
+
|
|
3
|
+
### bug fix
|
|
4
|
+
* Fix method reflection for Ruby 2.1+. [@badosu](https://github.com/badosu)
|
|
5
|
+
* Do not remove HTML attributes if components are not present.
|
|
6
|
+
|
|
7
|
+
## 3.0.3
|
|
8
|
+
|
|
9
|
+
### bug fix
|
|
10
|
+
* Fix XSS vulnerability on error components.
|
|
11
|
+
|
|
12
|
+
## 3.0.2
|
|
13
|
+
|
|
14
|
+
### enhancements
|
|
15
|
+
|
|
16
|
+
* Support Rails 4.1
|
|
17
|
+
|
|
18
|
+
## 3.0.1
|
|
19
|
+
|
|
20
|
+
### bug fix
|
|
21
|
+
* Fix XSS vulnerability on label, hint and error components.
|
|
22
|
+
|
|
1
23
|
## 3.0.0
|
|
2
24
|
|
|
3
25
|
### enhancements
|
|
@@ -12,7 +12,9 @@ module SimpleForm
|
|
|
12
12
|
protected
|
|
13
13
|
|
|
14
14
|
def error_text
|
|
15
|
-
|
|
15
|
+
text = has_error_in_options? ? options[:error] : errors.send(error_method)
|
|
16
|
+
|
|
17
|
+
"#{html_escape(options[:error_prefix])} #{html_escape(text)}".lstrip.html_safe
|
|
16
18
|
end
|
|
17
19
|
|
|
18
20
|
def error_method
|
|
@@ -30,6 +32,10 @@ module SimpleForm
|
|
|
30
32
|
def errors_on_association
|
|
31
33
|
reflection ? object.errors[reflection.name] : []
|
|
32
34
|
end
|
|
35
|
+
|
|
36
|
+
def has_error_in_options?
|
|
37
|
+
options[:error] && !options[:error].nil?
|
|
38
|
+
end
|
|
33
39
|
end
|
|
34
40
|
end
|
|
35
41
|
end
|
|
@@ -5,8 +5,13 @@ module SimpleForm
|
|
|
5
5
|
def hint
|
|
6
6
|
@hint ||= begin
|
|
7
7
|
hint = options[:hint]
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
|
|
9
|
+
if hint.is_a?(String)
|
|
10
|
+
html_escape(hint)
|
|
11
|
+
else
|
|
12
|
+
content = translate(:hints)
|
|
13
|
+
content.html_safe if content
|
|
14
|
+
end
|
|
10
15
|
end
|
|
11
16
|
end
|
|
12
17
|
|
|
@@ -136,11 +136,13 @@ module SimpleForm
|
|
|
136
136
|
# name="user[name]" type="text" value="Carlos" />
|
|
137
137
|
#
|
|
138
138
|
def input_field(attribute_name, options={})
|
|
139
|
+
components = (wrapper.components & ATTRIBUTE_COMPONENTS)
|
|
140
|
+
|
|
139
141
|
options = options.dup
|
|
140
|
-
options[:input_html] = options.except(:as, :collection, :label_method, :value_method, *
|
|
142
|
+
options[:input_html] = options.except(:as, :collection, :label_method, :value_method, *components)
|
|
141
143
|
options = @defaults.deep_dup.deep_merge(options) if @defaults
|
|
142
144
|
|
|
143
|
-
SimpleForm::Wrappers::Root.new(
|
|
145
|
+
SimpleForm::Wrappers::Root.new(components + [:input], wrapper: false).render find_input(attribute_name, options)
|
|
144
146
|
end
|
|
145
147
|
|
|
146
148
|
# Helper for dealing with association selects/radios, generating the
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
require 'simple_form/i18n_cache'
|
|
2
|
+
require 'active_support/core_ext/string/output_safety'
|
|
2
3
|
|
|
3
4
|
module SimpleForm
|
|
4
5
|
module Inputs
|
|
5
6
|
class Base
|
|
7
|
+
include ERB::Util
|
|
8
|
+
|
|
6
9
|
extend I18nCache
|
|
7
10
|
|
|
8
11
|
include SimpleForm::Helpers::Autofocus
|
|
@@ -42,7 +45,7 @@ module SimpleForm
|
|
|
42
45
|
end
|
|
43
46
|
|
|
44
47
|
# Always enabled.
|
|
45
|
-
enable :hint
|
|
48
|
+
enable :hint, :error
|
|
46
49
|
|
|
47
50
|
# Usually disabled, needs to be enabled explicitly passing true as option.
|
|
48
51
|
disable :maxlength, :placeholder, :pattern, :min_max
|
|
@@ -35,7 +35,7 @@ module SimpleForm
|
|
|
35
35
|
# reuse the method for nested boolean style, but with no unchecked value,
|
|
36
36
|
# which won't generate the hidden checkbox. This is the default functionality
|
|
37
37
|
# in Rails > 3.2.1, and is backported in SimpleForm AV helpers.
|
|
38
|
-
def build_check_box(unchecked_value = unchecked_value)
|
|
38
|
+
def build_check_box(unchecked_value = unchecked_value())
|
|
39
39
|
@builder.check_box(attribute_name, input_html_options, checked_value, unchecked_value)
|
|
40
40
|
end
|
|
41
41
|
|
data/lib/simple_form/version.rb
CHANGED
|
@@ -14,6 +14,10 @@ class ErrorTest < ActionView::TestCase
|
|
|
14
14
|
end
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
+
def with_custom_error_for(object, *args)
|
|
18
|
+
with_form_for(object, *args)
|
|
19
|
+
end
|
|
20
|
+
|
|
17
21
|
test 'error should not generate content for attribute without errors' do
|
|
18
22
|
with_error_for @user, :active
|
|
19
23
|
assert_no_select 'span.error'
|
|
@@ -32,7 +36,7 @@ class ErrorTest < ActionView::TestCase
|
|
|
32
36
|
|
|
33
37
|
test 'error should generate messages for attribute with single error' do
|
|
34
38
|
with_error_for @user, :name
|
|
35
|
-
assert_select 'span.error', "can
|
|
39
|
+
assert_select 'span.error', "can't be blank"
|
|
36
40
|
end
|
|
37
41
|
|
|
38
42
|
test 'error should generate messages for attribute with one error when using first' do
|
|
@@ -80,9 +84,23 @@ class ErrorTest < ActionView::TestCase
|
|
|
80
84
|
assert_no_select 'p.error[error_method]'
|
|
81
85
|
end
|
|
82
86
|
|
|
83
|
-
test 'error should
|
|
87
|
+
test 'error should escape error prefix text' do
|
|
84
88
|
with_error_for @user, :name, error_prefix: '<b>Name</b>'
|
|
85
|
-
assert_select 'span.error', "Name can
|
|
89
|
+
assert_select 'span.error', "<b>Name</b> can't be blank"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
test 'error escapes error text' do
|
|
93
|
+
@user.errors.merge!(action: ['must not contain <b>markup</b>'])
|
|
94
|
+
|
|
95
|
+
with_error_for @user, :action
|
|
96
|
+
|
|
97
|
+
assert_select 'span.error'
|
|
98
|
+
assert_no_select 'span.error b', 'markup'
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
test 'error should generate an error message with raw HTML tags' do
|
|
102
|
+
with_error_for @user, :name, error_prefix: '<b>Name</b>'.html_safe
|
|
103
|
+
assert_select 'span.error', "Name can't be blank"
|
|
86
104
|
assert_select 'span.error b', "Name"
|
|
87
105
|
end
|
|
88
106
|
|
|
@@ -90,7 +108,7 @@ class ErrorTest < ActionView::TestCase
|
|
|
90
108
|
|
|
91
109
|
test 'full error should generate an full error tag for the attribute' do
|
|
92
110
|
with_full_error_for @user, :name
|
|
93
|
-
assert_select 'span.error', "Super User Name! can
|
|
111
|
+
assert_select 'span.error', "Super User Name! can't be blank"
|
|
94
112
|
end
|
|
95
113
|
|
|
96
114
|
test 'full error should generate an full error tag with a clean HTML' do
|
|
@@ -100,13 +118,13 @@ class ErrorTest < ActionView::TestCase
|
|
|
100
118
|
|
|
101
119
|
test 'full error should allow passing options to full error tag' do
|
|
102
120
|
with_full_error_for @user, :name, id: 'name_error', error_prefix: "Your name"
|
|
103
|
-
assert_select 'span.error#name_error', "Your name can
|
|
121
|
+
assert_select 'span.error#name_error', "Your name can't be blank"
|
|
104
122
|
end
|
|
105
123
|
|
|
106
124
|
test 'full error should not modify the options hash' do
|
|
107
125
|
options = { id: 'name_error' }
|
|
108
126
|
with_full_error_for @user, :name, options
|
|
109
|
-
assert_select 'span.error#name_error', "Super User Name! can
|
|
127
|
+
assert_select 'span.error#name_error', "Super User Name! can't be blank"
|
|
110
128
|
assert_equal({ id: 'name_error' }, options)
|
|
111
129
|
end
|
|
112
130
|
|
|
@@ -115,7 +133,36 @@ class ErrorTest < ActionView::TestCase
|
|
|
115
133
|
test 'error with custom wrappers works' do
|
|
116
134
|
swap_wrapper do
|
|
117
135
|
with_error_for @user, :name
|
|
118
|
-
assert_select 'span.omg_error', "can
|
|
136
|
+
assert_select 'span.omg_error', "can't be blank"
|
|
119
137
|
end
|
|
120
138
|
end
|
|
139
|
+
|
|
140
|
+
# CUSTOM ERRORS
|
|
141
|
+
|
|
142
|
+
test 'input with custom error works' do
|
|
143
|
+
with_custom_error_for(@user, :name, error: "Super User Name! can't be blank")
|
|
144
|
+
|
|
145
|
+
assert_select 'span.error', "Super User Name! can't be blank"
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
test 'input with custom error does not generate the error if there is no error on the attribute' do
|
|
149
|
+
error_text = "Super User Active! can't be blank"
|
|
150
|
+
with_form_for @user, :active, error: error_text
|
|
151
|
+
|
|
152
|
+
assert_no_select 'span.error'
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
test 'input with custom error escapes the error text' do
|
|
156
|
+
with_form_for @user, :name, error: 'error must not contain <b>markup</b>'
|
|
157
|
+
|
|
158
|
+
assert_select 'span.error'
|
|
159
|
+
assert_no_select 'span.error b', 'markup'
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
test 'input with custom error does not escape the error text if it is safe' do
|
|
163
|
+
with_form_for @user, :name, error: 'error must contain <b>markup</b>'.html_safe
|
|
164
|
+
|
|
165
|
+
assert_select 'span.error'
|
|
166
|
+
assert_select 'span.error b', 'markup'
|
|
167
|
+
end
|
|
121
168
|
end
|
|
@@ -302,7 +302,7 @@ class FormBuilderTest < ActionView::TestCase
|
|
|
302
302
|
|
|
303
303
|
test 'builder should generate errors for attribute with errors' do
|
|
304
304
|
with_form_for @user, :name
|
|
305
|
-
assert_select 'span.error', "can
|
|
305
|
+
assert_select 'span.error', "can't be blank"
|
|
306
306
|
end
|
|
307
307
|
|
|
308
308
|
test 'builder should be able to disable showing errors for a input' do
|
|
@@ -312,7 +312,7 @@ class FormBuilderTest < ActionView::TestCase
|
|
|
312
312
|
|
|
313
313
|
test 'builder should pass options to errors' do
|
|
314
314
|
with_form_for @user, :name, error_html: { id: "cool" }
|
|
315
|
-
assert_select 'span.error#cool', "can
|
|
315
|
+
assert_select 'span.error#cool', "can't be blank"
|
|
316
316
|
end
|
|
317
317
|
|
|
318
318
|
test 'placeholder should not be generated when set to false' do
|
|
@@ -43,8 +43,14 @@ class HintTest < ActionView::TestCase
|
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
test 'hint should be output as html_safe' do
|
|
46
|
-
with_hint_for @user, :name, hint: '<b>Bold</b> and not...'
|
|
46
|
+
with_hint_for @user, :name, hint: '<b>Bold</b> and not...'.html_safe
|
|
47
47
|
assert_select 'span.hint', 'Bold and not...'
|
|
48
|
+
assert_select 'span.hint b', 'Bold'
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
test 'builder should escape hint text' do
|
|
52
|
+
with_hint_for @user, :name, hint: '<script>alert(1337)</script>'
|
|
53
|
+
assert_select 'span.hint', "<script>alert(1337)</script>"
|
|
48
54
|
end
|
|
49
55
|
|
|
50
56
|
# Without attribute name
|
|
@@ -132,7 +138,7 @@ class HintTest < ActionView::TestCase
|
|
|
132
138
|
test 'hint with custom wrappers works' do
|
|
133
139
|
swap_wrapper do
|
|
134
140
|
with_hint_for @user, :name, hint: "can't be blank"
|
|
135
|
-
assert_select 'div.omg_hint', "can
|
|
141
|
+
assert_select 'div.omg_hint', "can't be blank"
|
|
136
142
|
end
|
|
137
143
|
end
|
|
138
144
|
end
|
|
@@ -122,4 +122,44 @@ class InputFieldTest < ActionView::TestCase
|
|
|
122
122
|
assert_no_select 'select.status[label_method]'
|
|
123
123
|
assert_no_select 'select.status[value_method]'
|
|
124
124
|
end
|
|
125
|
+
|
|
126
|
+
test 'build input_field without pattern component use the pattern string' do
|
|
127
|
+
swap_wrapper :default, self.custom_wrapper_with_html5_components do
|
|
128
|
+
with_concat_form_for(@user) do |f|
|
|
129
|
+
f.input_field :name, pattern: '\w+'
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
assert_select 'input[pattern="\w+"]'
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
test 'build input_field without placeholder component use the placeholder string' do
|
|
137
|
+
swap_wrapper :default, self.custom_wrapper_with_html5_components do
|
|
138
|
+
with_concat_form_for(@user) do |f|
|
|
139
|
+
f.input_field :name, placeholder: 'Placeholder'
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
assert_select 'input[placeholder="Placeholder"]'
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
test 'build input_field without maxlength component use the maxlength string' do
|
|
147
|
+
swap_wrapper :default, self.custom_wrapper_with_html5_components do
|
|
148
|
+
with_concat_form_for(@user) do |f|
|
|
149
|
+
f.input_field :name, maxlength: 5
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
assert_select 'input[maxlength="5"]'
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
test 'build input_field without readonly component use the readonly string' do
|
|
157
|
+
swap_wrapper :default, self.custom_wrapper_with_html5_components do
|
|
158
|
+
with_concat_form_for(@user) do |f|
|
|
159
|
+
f.input_field :name, readonly: true
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
assert_select 'input[readonly="readonly"]'
|
|
163
|
+
end
|
|
164
|
+
end
|
|
125
165
|
end
|
|
@@ -29,6 +29,16 @@ class LabelTest < ActionView::TestCase
|
|
|
29
29
|
assert_select 'label.string.required[for=validating_user_name]', /Name/
|
|
30
30
|
end
|
|
31
31
|
|
|
32
|
+
test 'builder should escape label text' do
|
|
33
|
+
with_label_for @user, :name, label: '<script>alert(1337)</script>', required: false
|
|
34
|
+
assert_select 'label.string', "<script>alert(1337)</script>"
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
test 'builder should not escape label text if it is safe' do
|
|
38
|
+
with_label_for @user, :name, label: '<script>alert(1337)</script>'.html_safe, required: false
|
|
39
|
+
assert_select 'label.string script', "alert(1337)"
|
|
40
|
+
end
|
|
41
|
+
|
|
32
42
|
test 'builder should allow passing options to label tag' do
|
|
33
43
|
with_label_for @user, :name, label: 'My label', id: 'name_label'
|
|
34
44
|
assert_select 'label.string#name_label', /My label/
|
|
@@ -112,6 +112,12 @@ module MiscHelpers
|
|
|
112
112
|
end
|
|
113
113
|
end
|
|
114
114
|
|
|
115
|
+
def custom_wrapper_with_html5_components
|
|
116
|
+
SimpleForm.build tag: :span, class: 'custom_wrapper' do |b|
|
|
117
|
+
b.use :label_text
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
|
|
115
121
|
def custom_form_for(object, *args, &block)
|
|
116
122
|
simple_form_for(object, *args, { builder: CustomFormBuilder }, &block)
|
|
117
123
|
end
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: simple_form
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.0.
|
|
4
|
+
version: 3.0.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- José Valim
|
|
@@ -10,48 +10,36 @@ authors:
|
|
|
10
10
|
autorequire:
|
|
11
11
|
bindir: bin
|
|
12
12
|
cert_chain: []
|
|
13
|
-
date:
|
|
13
|
+
date: 2015-07-30 00:00:00.000000000 Z
|
|
14
14
|
dependencies:
|
|
15
15
|
- !ruby/object:Gem::Dependency
|
|
16
16
|
name: activemodel
|
|
17
17
|
requirement: !ruby/object:Gem::Requirement
|
|
18
18
|
requirements:
|
|
19
|
-
- -
|
|
19
|
+
- - "~>"
|
|
20
20
|
- !ruby/object:Gem::Version
|
|
21
|
-
version: 4.0
|
|
22
|
-
- - <
|
|
23
|
-
- !ruby/object:Gem::Version
|
|
24
|
-
version: '4.1'
|
|
21
|
+
version: '4.0'
|
|
25
22
|
type: :runtime
|
|
26
23
|
prerelease: false
|
|
27
24
|
version_requirements: !ruby/object:Gem::Requirement
|
|
28
25
|
requirements:
|
|
29
|
-
- -
|
|
30
|
-
- !ruby/object:Gem::Version
|
|
31
|
-
version: 4.0.0
|
|
32
|
-
- - <
|
|
26
|
+
- - "~>"
|
|
33
27
|
- !ruby/object:Gem::Version
|
|
34
|
-
version: '4.
|
|
28
|
+
version: '4.0'
|
|
35
29
|
- !ruby/object:Gem::Dependency
|
|
36
30
|
name: actionpack
|
|
37
31
|
requirement: !ruby/object:Gem::Requirement
|
|
38
32
|
requirements:
|
|
39
|
-
- -
|
|
40
|
-
- !ruby/object:Gem::Version
|
|
41
|
-
version: 4.0.0
|
|
42
|
-
- - <
|
|
33
|
+
- - "~>"
|
|
43
34
|
- !ruby/object:Gem::Version
|
|
44
|
-
version: '4.
|
|
35
|
+
version: '4.0'
|
|
45
36
|
type: :runtime
|
|
46
37
|
prerelease: false
|
|
47
38
|
version_requirements: !ruby/object:Gem::Requirement
|
|
48
39
|
requirements:
|
|
49
|
-
- -
|
|
40
|
+
- - "~>"
|
|
50
41
|
- !ruby/object:Gem::Version
|
|
51
|
-
version: 4.0
|
|
52
|
-
- - <
|
|
53
|
-
- !ruby/object:Gem::Version
|
|
54
|
-
version: '4.1'
|
|
42
|
+
version: '4.0'
|
|
55
43
|
description: Forms made easy!
|
|
56
44
|
email: contact@plataformatec.com.br
|
|
57
45
|
executables: []
|
|
@@ -61,7 +49,9 @@ files:
|
|
|
61
49
|
- CHANGELOG.md
|
|
62
50
|
- MIT-LICENSE
|
|
63
51
|
- README.md
|
|
52
|
+
- lib/generators/simple_form/USAGE
|
|
64
53
|
- lib/generators/simple_form/install_generator.rb
|
|
54
|
+
- lib/generators/simple_form/templates/README
|
|
65
55
|
- lib/generators/simple_form/templates/_form.html.erb
|
|
66
56
|
- lib/generators/simple_form/templates/_form.html.haml
|
|
67
57
|
- lib/generators/simple_form/templates/_form.html.slim
|
|
@@ -69,11 +59,10 @@ files:
|
|
|
69
59
|
- lib/generators/simple_form/templates/config/initializers/simple_form_bootstrap.rb
|
|
70
60
|
- lib/generators/simple_form/templates/config/initializers/simple_form_foundation.rb
|
|
71
61
|
- lib/generators/simple_form/templates/config/locales/simple_form.en.yml
|
|
72
|
-
- lib/
|
|
73
|
-
- lib/generators/simple_form/USAGE
|
|
62
|
+
- lib/simple_form.rb
|
|
74
63
|
- lib/simple_form/action_view_extensions/builder.rb
|
|
75
|
-
- lib/simple_form/action_view_extensions/builder.rb.orig
|
|
76
64
|
- lib/simple_form/action_view_extensions/form_helper.rb
|
|
65
|
+
- lib/simple_form/components.rb
|
|
77
66
|
- lib/simple_form/components/errors.rb
|
|
78
67
|
- lib/simple_form/components/hints.rb
|
|
79
68
|
- lib/simple_form/components/html5.rb
|
|
@@ -84,17 +73,16 @@ files:
|
|
|
84
73
|
- lib/simple_form/components/pattern.rb
|
|
85
74
|
- lib/simple_form/components/placeholders.rb
|
|
86
75
|
- lib/simple_form/components/readonly.rb
|
|
87
|
-
- lib/simple_form/components.rb
|
|
88
76
|
- lib/simple_form/error_notification.rb
|
|
89
77
|
- lib/simple_form/form_builder.rb
|
|
90
|
-
- lib/simple_form/
|
|
78
|
+
- lib/simple_form/helpers.rb
|
|
91
79
|
- lib/simple_form/helpers/autofocus.rb
|
|
92
80
|
- lib/simple_form/helpers/disabled.rb
|
|
93
81
|
- lib/simple_form/helpers/readonly.rb
|
|
94
82
|
- lib/simple_form/helpers/required.rb
|
|
95
83
|
- lib/simple_form/helpers/validators.rb
|
|
96
|
-
- lib/simple_form/helpers.rb
|
|
97
84
|
- lib/simple_form/i18n_cache.rb
|
|
85
|
+
- lib/simple_form/inputs.rb
|
|
98
86
|
- lib/simple_form/inputs/base.rb
|
|
99
87
|
- lib/simple_form/inputs/block_input.rb
|
|
100
88
|
- lib/simple_form/inputs/boolean_input.rb
|
|
@@ -112,18 +100,15 @@ files:
|
|
|
112
100
|
- lib/simple_form/inputs/range_input.rb
|
|
113
101
|
- lib/simple_form/inputs/string_input.rb
|
|
114
102
|
- lib/simple_form/inputs/text_input.rb
|
|
115
|
-
- lib/simple_form/inputs.rb
|
|
116
103
|
- lib/simple_form/map_type.rb
|
|
117
104
|
- lib/simple_form/railtie.rb
|
|
118
105
|
- lib/simple_form/tags.rb
|
|
119
106
|
- lib/simple_form/version.rb
|
|
120
|
-
- lib/simple_form/
|
|
107
|
+
- lib/simple_form/wrappers.rb
|
|
121
108
|
- lib/simple_form/wrappers/builder.rb
|
|
122
109
|
- lib/simple_form/wrappers/many.rb
|
|
123
110
|
- lib/simple_form/wrappers/root.rb
|
|
124
111
|
- lib/simple_form/wrappers/single.rb
|
|
125
|
-
- lib/simple_form/wrappers.rb
|
|
126
|
-
- lib/simple_form.rb
|
|
127
112
|
- test/action_view_extensions/builder_test.rb
|
|
128
113
|
- test/action_view_extensions/form_helper_test.rb
|
|
129
114
|
- test/components/label_test.rb
|
|
@@ -170,17 +155,17 @@ require_paths:
|
|
|
170
155
|
- lib
|
|
171
156
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
172
157
|
requirements:
|
|
173
|
-
- -
|
|
158
|
+
- - ">="
|
|
174
159
|
- !ruby/object:Gem::Version
|
|
175
160
|
version: '0'
|
|
176
161
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
177
162
|
requirements:
|
|
178
|
-
- -
|
|
163
|
+
- - ">="
|
|
179
164
|
- !ruby/object:Gem::Version
|
|
180
165
|
version: '0'
|
|
181
166
|
requirements: []
|
|
182
167
|
rubyforge_project: simple_form
|
|
183
|
-
rubygems_version: 2.
|
|
168
|
+
rubygems_version: 2.4.8
|
|
184
169
|
signing_key:
|
|
185
170
|
specification_version: 4
|
|
186
171
|
summary: Forms made easy!
|
|
@@ -221,3 +206,4 @@ test_files:
|
|
|
221
206
|
- test/support/mock_controller.rb
|
|
222
207
|
- test/support/models.rb
|
|
223
208
|
- test/test_helper.rb
|
|
209
|
+
has_rdoc:
|
|
@@ -1,247 +0,0 @@
|
|
|
1
|
-
module SimpleForm
|
|
2
|
-
module ActionViewExtensions
|
|
3
|
-
# A collection of methods required by simple_form but added to rails default form.
|
|
4
|
-
# This means that you can use such methods outside simple_form context.
|
|
5
|
-
module Builder
|
|
6
|
-
|
|
7
|
-
# Wrapper for using SimpleForm inside a default rails form.
|
|
8
|
-
# Example:
|
|
9
|
-
#
|
|
10
|
-
# form_for @user do |f|
|
|
11
|
-
# f.simple_fields_for :posts do |posts_form|
|
|
12
|
-
# # Here you have all simple_form methods available
|
|
13
|
-
# posts_form.input :title
|
|
14
|
-
# end
|
|
15
|
-
# end
|
|
16
|
-
def simple_fields_for(*args, &block)
|
|
17
|
-
options = args.extract_options!
|
|
18
|
-
options[:wrapper] = self.options[:wrapper] if options[:wrapper].nil?
|
|
19
|
-
options[:defaults] ||= self.options[:defaults]
|
|
20
|
-
|
|
21
|
-
if self.class < ActionView::Helpers::FormBuilder
|
|
22
|
-
options[:builder] ||= self.class
|
|
23
|
-
else
|
|
24
|
-
options[:builder] ||= SimpleForm::FormBuilder
|
|
25
|
-
end
|
|
26
|
-
fields_for(*(args << options), &block)
|
|
27
|
-
end
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
module SimpleForm
|
|
33
|
-
module Tags
|
|
34
|
-
module CollectionExtensions
|
|
35
|
-
private
|
|
36
|
-
|
|
37
|
-
def render_collection
|
|
38
|
-
item_wrapper_tag = @options.fetch(:item_wrapper_tag, :span)
|
|
39
|
-
item_wrapper_class = @options[:item_wrapper_class]
|
|
40
|
-
|
|
41
|
-
@collection.map do |item|
|
|
42
|
-
value = value_for_collection(item, @value_method)
|
|
43
|
-
text = value_for_collection(item, @text_method)
|
|
44
|
-
default_html_options = default_html_options_for_collection(item, value)
|
|
45
|
-
|
|
46
|
-
rendered_item = yield item, value, text, default_html_options
|
|
47
|
-
|
|
48
|
-
item_wrapper_tag ? @template_object.content_tag(item_wrapper_tag, rendered_item, :class => item_wrapper_class) : rendered_item
|
|
49
|
-
end.join.html_safe
|
|
50
|
-
end
|
|
51
|
-
|
|
52
|
-
def wrap_rendered_collection(collection)
|
|
53
|
-
wrapper_tag = @options[:collection_wrapper_tag]
|
|
54
|
-
|
|
55
|
-
if wrapper_tag
|
|
56
|
-
wrapper_class = @options[:collection_wrapper_class]
|
|
57
|
-
@template_object.content_tag(wrapper_tag, collection, :class => wrapper_class)
|
|
58
|
-
else
|
|
59
|
-
collection
|
|
60
|
-
end
|
|
61
|
-
end
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
class CollectionRadioButtons < ActionView::Helpers::Tags::CollectionRadioButtons
|
|
65
|
-
include CollectionExtensions
|
|
66
|
-
|
|
67
|
-
def render
|
|
68
|
-
wrap_rendered_collection(super)
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
private
|
|
72
|
-
|
|
73
|
-
def render_component(builder)
|
|
74
|
-
builder.radio_button + builder.label(:class => "collection_radio_buttons")
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
class CollectionCheckBoxes < ActionView::Helpers::Tags::CollectionCheckBoxes
|
|
79
|
-
include CollectionExtensions
|
|
80
|
-
|
|
81
|
-
def render
|
|
82
|
-
wrap_rendered_collection(super)
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
private
|
|
86
|
-
|
|
87
|
-
def render_component(builder)
|
|
88
|
-
builder.check_box + builder.label(:class => "collection_check_boxes")
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
module ActionView::Helpers
|
|
95
|
-
class FormBuilder
|
|
96
|
-
include SimpleForm::ActionViewExtensions::Builder
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
<<<<<<< HEAD
|
|
100
|
-
# Create a collection of radio inputs for the attribute. Basically this
|
|
101
|
-
# helper will create a radio input associated with a label for each
|
|
102
|
-
# text/value option in the collection, using value_method and text_method
|
|
103
|
-
# to convert these text/value. You can give a symbol or a proc to both
|
|
104
|
-
# value_method and text_method, that will be evaluated for each item in
|
|
105
|
-
# the collection.
|
|
106
|
-
#
|
|
107
|
-
# == Examples
|
|
108
|
-
#
|
|
109
|
-
# form_for @user do |f|
|
|
110
|
-
# f.collection_radio_buttons :options, [[true, 'Yes'] ,[false, 'No']], :first, :last
|
|
111
|
-
# end
|
|
112
|
-
#
|
|
113
|
-
# <input id="user_options_true" name="user[options]" type="radio" value="true" />
|
|
114
|
-
# <label class="collection_radio_buttons" for="user_options_true">Yes</label>
|
|
115
|
-
# <input id="user_options_false" name="user[options]" type="radio" value="false" />
|
|
116
|
-
# <label class="collection_radio_buttons" for="user_options_false">No</label>
|
|
117
|
-
#
|
|
118
|
-
# It is also possible to give a block that should generate the radio +
|
|
119
|
-
# label. To wrap the radio with the label, for instance:
|
|
120
|
-
#
|
|
121
|
-
# form_for @user do |f|
|
|
122
|
-
# f.collection_radio_buttons(
|
|
123
|
-
# :options, [[true, 'Yes'] ,[false, 'No']], :first, :last
|
|
124
|
-
# ) do |b|
|
|
125
|
-
# b.label { b.radio_button + b.text }
|
|
126
|
-
# end
|
|
127
|
-
# end
|
|
128
|
-
#
|
|
129
|
-
# == Options
|
|
130
|
-
#
|
|
131
|
-
# Collection radio accepts some extra options:
|
|
132
|
-
#
|
|
133
|
-
# * checked => the value that should be checked initially.
|
|
134
|
-
#
|
|
135
|
-
# * disabled => the value or values that should be disabled. Accepts a single
|
|
136
|
-
# item or an array of items.
|
|
137
|
-
#
|
|
138
|
-
# * collection_wrapper_tag => the tag to wrap the entire collection.
|
|
139
|
-
#
|
|
140
|
-
# * collection_wrapper_class => the CSS class to use for collection_wrapper_tag
|
|
141
|
-
#
|
|
142
|
-
# * item_wrapper_tag => the tag to wrap each item in the collection.
|
|
143
|
-
#
|
|
144
|
-
# * item_wrapper_class => the CSS class to use for item_wrapper_tag
|
|
145
|
-
#
|
|
146
|
-
# * a block => to generate the label + radio or any other component.
|
|
147
|
-
def collection_radio_buttons(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
|
148
|
-
SimpleForm::Tags::CollectionRadioButtons.new(@object_name, method, @template, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options)).render(&block)
|
|
149
|
-
=======
|
|
150
|
-
module FormOptionsHelper
|
|
151
|
-
# Override Rails options_from_collection_for_select to handle lambdas/procs in
|
|
152
|
-
# text and value methods, so it works the same way as collection_radio_buttons
|
|
153
|
-
# and collection_check_boxes in SimpleForm. If none of text/value methods is a
|
|
154
|
-
# callable object, then it just delegates back to original collection select.
|
|
155
|
-
# FIXME: remove when support only Rails 4.0 forward
|
|
156
|
-
# https://github.com/rails/rails/commit/9035324367526af0300477a58b6d3efc15d1a5a8
|
|
157
|
-
alias :original_options_from_collection_for_select :options_from_collection_for_select
|
|
158
|
-
def options_from_collection_for_select(collection, value_method, text_method, selected = nil)
|
|
159
|
-
if value_method.respond_to?(:call) || text_method.respond_to?(:call)
|
|
160
|
-
collection = collection.map do |item|
|
|
161
|
-
value = value_for_collection(item, value_method)
|
|
162
|
-
text = value_for_collection(item, text_method)
|
|
163
|
-
|
|
164
|
-
[value, text]
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
value_method, text_method = :first, :last
|
|
168
|
-
selected = extract_selected_and_disabled_and_call_procs selected, collection
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
original_options_from_collection_for_select collection, value_method, text_method, selected
|
|
172
|
-
end
|
|
173
|
-
|
|
174
|
-
private
|
|
175
|
-
|
|
176
|
-
def extract_selected_and_disabled_and_call_procs(selected, collection)
|
|
177
|
-
selected, disabled = extract_selected_and_disabled selected
|
|
178
|
-
selected_disabled = { :selected => selected, :disabled => disabled }
|
|
179
|
-
|
|
180
|
-
selected_disabled.each do |key, check|
|
|
181
|
-
if check.is_a? Proc
|
|
182
|
-
values = collection.map { |option| option.first if check.call(option.first) }
|
|
183
|
-
selected_disabled[key] = values
|
|
184
|
-
end
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
def value_for_collection(item, value) #:nodoc:
|
|
189
|
-
value.respond_to?(:call) ? value.call(item) : item.send(value)
|
|
190
|
-
>>>>>>> master
|
|
191
|
-
end
|
|
192
|
-
|
|
193
|
-
# Creates a collection of check boxes for each item in the collection,
|
|
194
|
-
# associated with a clickable label. Use value_method and text_method to
|
|
195
|
-
# convert items in the collection for use as text/value in check boxes.
|
|
196
|
-
# You can give a symbol or a proc to both value_method and text_method,
|
|
197
|
-
# that will be evaluated for each item in the collection.
|
|
198
|
-
#
|
|
199
|
-
# == Examples
|
|
200
|
-
#
|
|
201
|
-
# form_for @user do |f|
|
|
202
|
-
# f.collection_check_boxes :options, [[true, 'Yes'] ,[false, 'No']], :first, :last
|
|
203
|
-
# end
|
|
204
|
-
#
|
|
205
|
-
# <input name="user[options][]" type="hidden" value="" />
|
|
206
|
-
# <input id="user_options_true" name="user[options][]" type="checkbox" value="true" />
|
|
207
|
-
# <label class="collection_check_boxes" for="user_options_true">Yes</label>
|
|
208
|
-
# <input name="user[options][]" type="hidden" value="" />
|
|
209
|
-
# <input id="user_options_false" name="user[options][]" type="checkbox" value="false" />
|
|
210
|
-
# <label class="collection_check_boxes" for="user_options_false">No</label>
|
|
211
|
-
#
|
|
212
|
-
# It is also possible to give a block that should generate the check box +
|
|
213
|
-
# label. To wrap the check box with the label, for instance:
|
|
214
|
-
#
|
|
215
|
-
# form_for @user do |f|
|
|
216
|
-
# f.collection_check_boxes(
|
|
217
|
-
# :options, [[true, 'Yes'] ,[false, 'No']], :first, :last
|
|
218
|
-
# ) do |b|
|
|
219
|
-
# b.label { b.check_box + b.text }
|
|
220
|
-
# end
|
|
221
|
-
# end
|
|
222
|
-
#
|
|
223
|
-
# == Options
|
|
224
|
-
#
|
|
225
|
-
# Collection check box accepts some extra options:
|
|
226
|
-
#
|
|
227
|
-
# * checked => the value or values that should be checked initially. Accepts
|
|
228
|
-
# a single item or an array of items. It overrides existing associations.
|
|
229
|
-
#
|
|
230
|
-
# * disabled => the value or values that should be disabled. Accepts a single
|
|
231
|
-
# item or an array of items.
|
|
232
|
-
#
|
|
233
|
-
# * collection_wrapper_tag => the tag to wrap the entire collection.
|
|
234
|
-
#
|
|
235
|
-
# * collection_wrapper_class => the CSS class to use for collection_wrapper_tag. This option
|
|
236
|
-
# is ignored if the :collection_wrapper_tag option is blank.
|
|
237
|
-
#
|
|
238
|
-
# * item_wrapper_tag => the tag to wrap each item in the collection.
|
|
239
|
-
#
|
|
240
|
-
# * item_wrapper_class => the CSS class to use for item_wrapper_tag
|
|
241
|
-
#
|
|
242
|
-
# * a block => to generate the label + check box or any other component.
|
|
243
|
-
def collection_check_boxes(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
|
|
244
|
-
SimpleForm::Tags::CollectionCheckBoxes.new(@object_name, method, @template, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options)).render(&block)
|
|
245
|
-
end
|
|
246
|
-
end
|
|
247
|
-
end
|
|
@@ -1,486 +0,0 @@
|
|
|
1
|
-
<<<<<<< HEAD
|
|
2
|
-
require 'simple_form/core_ext/hash'
|
|
3
|
-
=======
|
|
4
|
-
require 'active_support/core_ext/object/deep_dup'
|
|
5
|
-
require 'simple_form/map_type'
|
|
6
|
-
>>>>>>> beeac4d... These modules don't need to be autoloaded
|
|
7
|
-
|
|
8
|
-
module SimpleForm
|
|
9
|
-
class FormBuilder < ActionView::Helpers::FormBuilder
|
|
10
|
-
attr_reader :template, :object_name, :object, :wrapper
|
|
11
|
-
|
|
12
|
-
# When action is create or update, we still should use new and edit
|
|
13
|
-
ACTIONS = {
|
|
14
|
-
:create => :new,
|
|
15
|
-
:update => :edit
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
extend MapType
|
|
19
|
-
include SimpleForm::Inputs
|
|
20
|
-
|
|
21
|
-
map_type :text, :to => SimpleForm::Inputs::TextInput
|
|
22
|
-
map_type :file, :to => SimpleForm::Inputs::FileInput
|
|
23
|
-
map_type :string, :email, :search, :tel, :url, :to => SimpleForm::Inputs::StringInput
|
|
24
|
-
map_type :password, :to => SimpleForm::Inputs::PasswordInput
|
|
25
|
-
map_type :integer, :decimal, :float, :to => SimpleForm::Inputs::NumericInput
|
|
26
|
-
map_type :range, :to => SimpleForm::Inputs::RangeInput
|
|
27
|
-
map_type :check_boxes, :to => SimpleForm::Inputs::CollectionCheckBoxesInput
|
|
28
|
-
map_type :radio_buttons, :to => SimpleForm::Inputs::CollectionRadioButtonsInput
|
|
29
|
-
map_type :select, :to => SimpleForm::Inputs::CollectionSelectInput
|
|
30
|
-
map_type :grouped_select, :to => SimpleForm::Inputs::GroupedCollectionSelectInput
|
|
31
|
-
map_type :date, :time, :datetime, :to => SimpleForm::Inputs::DateTimeInput
|
|
32
|
-
map_type :country, :time_zone, :to => SimpleForm::Inputs::PriorityInput
|
|
33
|
-
map_type :boolean, :to => SimpleForm::Inputs::BooleanInput
|
|
34
|
-
|
|
35
|
-
def self.discovery_cache
|
|
36
|
-
@discovery_cache ||= {}
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
def initialize(*) #:nodoc:
|
|
40
|
-
super
|
|
41
|
-
@defaults = options[:defaults]
|
|
42
|
-
@wrapper = SimpleForm.wrapper(options[:wrapper] || SimpleForm.default_wrapper)
|
|
43
|
-
end
|
|
44
|
-
|
|
45
|
-
# Basic input helper, combines all components in the stack to generate
|
|
46
|
-
# input html based on options the user define and some guesses through
|
|
47
|
-
# database column information. By default a call to input will generate
|
|
48
|
-
# label + input + hint (when defined) + errors (when exists), and all can
|
|
49
|
-
# be configured inside a wrapper html.
|
|
50
|
-
#
|
|
51
|
-
# == Examples
|
|
52
|
-
#
|
|
53
|
-
# # Imagine @user has error "can't be blank" on name
|
|
54
|
-
# simple_form_for @user do |f|
|
|
55
|
-
# f.input :name, :hint => 'My hint'
|
|
56
|
-
# end
|
|
57
|
-
#
|
|
58
|
-
# This is the output html (only the input portion, not the form):
|
|
59
|
-
#
|
|
60
|
-
# <label class="string required" for="user_name">
|
|
61
|
-
# <abbr title="required">*</abbr> Super User Name!
|
|
62
|
-
# </label>
|
|
63
|
-
# <input class="string required" id="user_name" maxlength="100"
|
|
64
|
-
# name="user[name]" size="100" type="text" value="Carlos" />
|
|
65
|
-
# <span class="hint">My hint</span>
|
|
66
|
-
# <span class="error">can't be blank</span>
|
|
67
|
-
#
|
|
68
|
-
# Each database type will render a default input, based on some mappings and
|
|
69
|
-
# heuristic to determine which is the best option.
|
|
70
|
-
#
|
|
71
|
-
# You have some options for the input to enable/disable some functions:
|
|
72
|
-
#
|
|
73
|
-
# :as => allows you to define the input type you want, for instance you
|
|
74
|
-
# can use it to generate a text field for a date column.
|
|
75
|
-
#
|
|
76
|
-
# :required => defines whether this attribute is required or not. True
|
|
77
|
-
# by default.
|
|
78
|
-
#
|
|
79
|
-
# The fact SimpleForm is built in components allow the interface to be unified.
|
|
80
|
-
# So, for instance, if you need to disable :hint for a given input, you can pass
|
|
81
|
-
# :hint => false. The same works for :error, :label and :wrapper.
|
|
82
|
-
#
|
|
83
|
-
# Besides the html for any component can be changed. So, if you want to change
|
|
84
|
-
# the label html you just need to give a hash to :label_html. To configure the
|
|
85
|
-
# input html, supply :input_html instead and so on.
|
|
86
|
-
#
|
|
87
|
-
# == Options
|
|
88
|
-
#
|
|
89
|
-
# Some inputs, as datetime, time and select allow you to give extra options, like
|
|
90
|
-
# prompt and/or include blank. Such options are given in plainly:
|
|
91
|
-
#
|
|
92
|
-
# f.input :created_at, :include_blank => true
|
|
93
|
-
#
|
|
94
|
-
# == Collection
|
|
95
|
-
#
|
|
96
|
-
# When playing with collections (:radio_buttons, :check_boxes and :select
|
|
97
|
-
# inputs), you have three extra options:
|
|
98
|
-
#
|
|
99
|
-
# :collection => use to determine the collection to generate the radio or select
|
|
100
|
-
#
|
|
101
|
-
# :label_method => the method to apply on the array collection to get the label
|
|
102
|
-
#
|
|
103
|
-
# :value_method => the method to apply on the array collection to get the value
|
|
104
|
-
#
|
|
105
|
-
# == Priority
|
|
106
|
-
#
|
|
107
|
-
# Some inputs, as :time_zone and :country accepts a :priority option. If none is
|
|
108
|
-
# given SimpleForm.time_zone_priority and SimpleForm.country_priority are used respectively.
|
|
109
|
-
#
|
|
110
|
-
def input(attribute_name, options={}, &block)
|
|
111
|
-
options = @defaults.deep_dup.deep_merge(options) if @defaults
|
|
112
|
-
input = find_input(attribute_name, options, &block)
|
|
113
|
-
|
|
114
|
-
chosen =
|
|
115
|
-
if name = options[:wrapper] || find_wrapper_mapping(input.input_type)
|
|
116
|
-
name.respond_to?(:render) ? name : SimpleForm.wrapper(name)
|
|
117
|
-
else
|
|
118
|
-
wrapper
|
|
119
|
-
end
|
|
120
|
-
|
|
121
|
-
chosen.render input
|
|
122
|
-
end
|
|
123
|
-
alias :attribute :input
|
|
124
|
-
|
|
125
|
-
# Creates a input tag for the given attribute. All the given options
|
|
126
|
-
# are sent as :input_html.
|
|
127
|
-
#
|
|
128
|
-
# == Examples
|
|
129
|
-
#
|
|
130
|
-
# simple_form_for @user do |f|
|
|
131
|
-
# f.input_field :name
|
|
132
|
-
# end
|
|
133
|
-
#
|
|
134
|
-
# This is the output html (only the input portion, not the form):
|
|
135
|
-
#
|
|
136
|
-
# <input class="string required" id="user_name" maxlength="100"
|
|
137
|
-
# name="user[name]" size="100" type="text" value="Carlos" />
|
|
138
|
-
#
|
|
139
|
-
def input_field(attribute_name, options={})
|
|
140
|
-
options = options.dup
|
|
141
|
-
options[:input_html] = options.except(:as, :collection, :label_method, :value_method)
|
|
142
|
-
options = @defaults.deep_dup.deep_merge(options) if @defaults
|
|
143
|
-
|
|
144
|
-
SimpleForm::Wrappers::Root.new([:input], :wrapper => false).render find_input(attribute_name, options)
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
# Helper for dealing with association selects/radios, generating the
|
|
148
|
-
# collection automatically. It's just a wrapper to input, so all options
|
|
149
|
-
# supported in input are also supported by association. Some extra options
|
|
150
|
-
# can also be given:
|
|
151
|
-
#
|
|
152
|
-
# == Examples
|
|
153
|
-
#
|
|
154
|
-
# simple_form_for @user do |f|
|
|
155
|
-
# f.association :company # Company.all
|
|
156
|
-
# end
|
|
157
|
-
#
|
|
158
|
-
# f.association :company, :collection => Company.all(:order => 'name')
|
|
159
|
-
# # Same as using :order option, but overriding collection
|
|
160
|
-
#
|
|
161
|
-
# == Block
|
|
162
|
-
#
|
|
163
|
-
# When a block is given, association simple behaves as a proxy to
|
|
164
|
-
# simple_fields_for:
|
|
165
|
-
#
|
|
166
|
-
# f.association :company do |c|
|
|
167
|
-
# c.input :name
|
|
168
|
-
# c.input :type
|
|
169
|
-
# end
|
|
170
|
-
#
|
|
171
|
-
# From the options above, only :collection can also be supplied.
|
|
172
|
-
#
|
|
173
|
-
def association(association, options={}, &block)
|
|
174
|
-
options = options.dup
|
|
175
|
-
|
|
176
|
-
return simple_fields_for(*[association,
|
|
177
|
-
options.delete(:collection), options].compact, &block) if block_given?
|
|
178
|
-
|
|
179
|
-
raise ArgumentError, "Association cannot be used in forms not associated with an object" unless @object
|
|
180
|
-
|
|
181
|
-
reflection = find_association_reflection(association)
|
|
182
|
-
raise "Association #{association.inspect} not found" unless reflection
|
|
183
|
-
|
|
184
|
-
options[:as] ||= :select
|
|
185
|
-
options[:collection] ||= options.fetch(:collection) {
|
|
186
|
-
reflection.klass.all(reflection.options.slice(:conditions, :order))
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
attribute = case reflection.macro
|
|
190
|
-
when :belongs_to
|
|
191
|
-
(reflection.respond_to?(:options) && reflection.options[:foreign_key]) || :"#{reflection.name}_id"
|
|
192
|
-
when :has_one
|
|
193
|
-
raise ArgumentError, ":has_one associations are not supported by f.association"
|
|
194
|
-
else
|
|
195
|
-
if options[:as] == :select
|
|
196
|
-
html_options = options[:input_html] ||= {}
|
|
197
|
-
html_options[:size] ||= 5
|
|
198
|
-
html_options[:multiple] = true unless html_options.key?(:multiple)
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
# Force the association to be preloaded for performance.
|
|
202
|
-
if options[:preload] != false && object.respond_to?(association)
|
|
203
|
-
target = object.send(association)
|
|
204
|
-
target.to_a if target.respond_to?(:to_a)
|
|
205
|
-
end
|
|
206
|
-
|
|
207
|
-
:"#{reflection.name.to_s.singularize}_ids"
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
input(attribute, options.merge(:reflection => reflection))
|
|
211
|
-
end
|
|
212
|
-
|
|
213
|
-
# Creates a button:
|
|
214
|
-
#
|
|
215
|
-
# form_for @user do |f|
|
|
216
|
-
# f.button :submit
|
|
217
|
-
# end
|
|
218
|
-
#
|
|
219
|
-
# It just acts as a proxy to method name given. We also alias original Rails
|
|
220
|
-
# button implementation (3.2 forward (to delegate to the original when
|
|
221
|
-
# calling `f.button :button`.
|
|
222
|
-
#
|
|
223
|
-
# TODO: remove if condition when supporting only Rails 3.2 forward.
|
|
224
|
-
alias_method :button_button, :button if method_defined?(:button)
|
|
225
|
-
def button(type, *args, &block)
|
|
226
|
-
options = args.extract_options!.dup
|
|
227
|
-
options[:class] = [SimpleForm.button_class, options[:class]].compact
|
|
228
|
-
args << options
|
|
229
|
-
if respond_to?("#{type}_button")
|
|
230
|
-
send("#{type}_button", *args, &block)
|
|
231
|
-
else
|
|
232
|
-
send(type, *args, &block)
|
|
233
|
-
end
|
|
234
|
-
end
|
|
235
|
-
|
|
236
|
-
# Creates an error tag based on the given attribute, only when the attribute
|
|
237
|
-
# contains errors. All the given options are sent as :error_html.
|
|
238
|
-
#
|
|
239
|
-
# == Examples
|
|
240
|
-
#
|
|
241
|
-
# f.error :name
|
|
242
|
-
# f.error :name, :id => "cool_error"
|
|
243
|
-
#
|
|
244
|
-
def error(attribute_name, options={})
|
|
245
|
-
options = options.dup
|
|
246
|
-
|
|
247
|
-
options[:error_html] = options.except(:error_tag, :error_prefix, :error_method)
|
|
248
|
-
column = find_attribute_column(attribute_name)
|
|
249
|
-
input_type = default_input_type(attribute_name, column, options)
|
|
250
|
-
wrapper.find(:error).
|
|
251
|
-
render(SimpleForm::Inputs::Base.new(self, attribute_name, column, input_type, options))
|
|
252
|
-
end
|
|
253
|
-
|
|
254
|
-
# Return the error but also considering its name. This is used
|
|
255
|
-
# when errors for a hidden field need to be shown.
|
|
256
|
-
#
|
|
257
|
-
# == Examples
|
|
258
|
-
#
|
|
259
|
-
# f.full_error :token #=> <span class="error">Token is invalid</span>
|
|
260
|
-
#
|
|
261
|
-
def full_error(attribute_name, options={})
|
|
262
|
-
options = options.dup
|
|
263
|
-
|
|
264
|
-
options[:error_prefix] ||= if object.class.respond_to?(:human_attribute_name)
|
|
265
|
-
object.class.human_attribute_name(attribute_name.to_s)
|
|
266
|
-
else
|
|
267
|
-
attribute_name.to_s.humanize
|
|
268
|
-
end
|
|
269
|
-
|
|
270
|
-
error(attribute_name, options)
|
|
271
|
-
end
|
|
272
|
-
|
|
273
|
-
# Creates a hint tag for the given attribute. Accepts a symbol indicating
|
|
274
|
-
# an attribute for I18n lookup or a string. All the given options are sent
|
|
275
|
-
# as :hint_html.
|
|
276
|
-
#
|
|
277
|
-
# == Examples
|
|
278
|
-
#
|
|
279
|
-
# f.hint :name # Do I18n lookup
|
|
280
|
-
# f.hint :name, :id => "cool_hint"
|
|
281
|
-
# f.hint "Don't forget to accept this"
|
|
282
|
-
#
|
|
283
|
-
def hint(attribute_name, options={})
|
|
284
|
-
options = options.dup
|
|
285
|
-
|
|
286
|
-
options[:hint_html] = options.except(:hint_tag, :hint)
|
|
287
|
-
if attribute_name.is_a?(String)
|
|
288
|
-
options[:hint] = attribute_name
|
|
289
|
-
attribute_name, column, input_type = nil, nil, nil
|
|
290
|
-
else
|
|
291
|
-
column = find_attribute_column(attribute_name)
|
|
292
|
-
input_type = default_input_type(attribute_name, column, options)
|
|
293
|
-
end
|
|
294
|
-
|
|
295
|
-
wrapper.find(:hint).
|
|
296
|
-
render(SimpleForm::Inputs::Base.new(self, attribute_name, column, input_type, options))
|
|
297
|
-
end
|
|
298
|
-
|
|
299
|
-
# Creates a default label tag for the given attribute. You can give a label
|
|
300
|
-
# through the :label option or using i18n. All the given options are sent
|
|
301
|
-
# as :label_html.
|
|
302
|
-
#
|
|
303
|
-
# == Examples
|
|
304
|
-
#
|
|
305
|
-
# f.label :name # Do I18n lookup
|
|
306
|
-
# f.label :name, "Name" # Same behavior as Rails, do not add required tag
|
|
307
|
-
# f.label :name, :label => "Name" # Same as above, but adds required tag
|
|
308
|
-
#
|
|
309
|
-
# f.label :name, :required => false
|
|
310
|
-
# f.label :name, :id => "cool_label"
|
|
311
|
-
#
|
|
312
|
-
def label(attribute_name, *args)
|
|
313
|
-
return super if args.first.is_a?(String) || block_given?
|
|
314
|
-
|
|
315
|
-
options = args.extract_options!.dup
|
|
316
|
-
options[:label_html] = options.except(:label, :required, :as)
|
|
317
|
-
|
|
318
|
-
column = find_attribute_column(attribute_name)
|
|
319
|
-
input_type = default_input_type(attribute_name, column, options)
|
|
320
|
-
SimpleForm::Inputs::Base.new(self, attribute_name, column, input_type, options).label
|
|
321
|
-
end
|
|
322
|
-
|
|
323
|
-
# Creates an error notification message that only appears when the form object
|
|
324
|
-
# has some error. You can give a specific message with the :message option,
|
|
325
|
-
# otherwise it will look for a message using I18n. All other options given are
|
|
326
|
-
# passed straight as html options to the html tag.
|
|
327
|
-
#
|
|
328
|
-
# == Examples
|
|
329
|
-
#
|
|
330
|
-
# f.error_notification
|
|
331
|
-
# f.error_notification :message => 'Something went wrong'
|
|
332
|
-
# f.error_notification :id => 'user_error_message', :class => 'form_error'
|
|
333
|
-
#
|
|
334
|
-
def error_notification(options={})
|
|
335
|
-
SimpleForm::ErrorNotification.new(self, options).render
|
|
336
|
-
end
|
|
337
|
-
|
|
338
|
-
# Extract the model names from the object_name mess, ignoring numeric and
|
|
339
|
-
# explicit child indexes.
|
|
340
|
-
#
|
|
341
|
-
# Example:
|
|
342
|
-
#
|
|
343
|
-
# route[blocks_attributes][0][blocks_learning_object_attributes][1][foo_attributes]
|
|
344
|
-
# ["route", "blocks", "blocks_learning_object", "foo"]
|
|
345
|
-
#
|
|
346
|
-
def lookup_model_names
|
|
347
|
-
@lookup_model_names ||= begin
|
|
348
|
-
child_index = options[:child_index]
|
|
349
|
-
names = object_name.to_s.scan(/([a-zA-Z_]+)/).flatten
|
|
350
|
-
names.delete(child_index) if child_index
|
|
351
|
-
names.each { |name| name.gsub!('_attributes', '') }
|
|
352
|
-
names.freeze
|
|
353
|
-
end
|
|
354
|
-
end
|
|
355
|
-
|
|
356
|
-
# The action to be used in lookup.
|
|
357
|
-
def lookup_action
|
|
358
|
-
@lookup_action ||= begin
|
|
359
|
-
action = template.controller.action_name
|
|
360
|
-
return unless action
|
|
361
|
-
action = action.to_sym
|
|
362
|
-
ACTIONS[action] || action
|
|
363
|
-
end
|
|
364
|
-
end
|
|
365
|
-
|
|
366
|
-
private
|
|
367
|
-
|
|
368
|
-
# Find an input based on the attribute name.
|
|
369
|
-
def find_input(attribute_name, options={}, &block) #:nodoc:
|
|
370
|
-
column = find_attribute_column(attribute_name)
|
|
371
|
-
input_type = default_input_type(attribute_name, column, options)
|
|
372
|
-
|
|
373
|
-
if input_type == :radio
|
|
374
|
-
SimpleForm.deprecation_warn "Using `:as => :radio` as input type is " \
|
|
375
|
-
"deprecated, please change it to `:as => :radio_buttons`."
|
|
376
|
-
input_type = :radio_buttons
|
|
377
|
-
end
|
|
378
|
-
|
|
379
|
-
if block_given?
|
|
380
|
-
SimpleForm::Inputs::BlockInput.new(self, attribute_name, column, input_type, options, &block)
|
|
381
|
-
else
|
|
382
|
-
find_mapping(input_type).new(self, attribute_name, column, input_type, options)
|
|
383
|
-
end
|
|
384
|
-
end
|
|
385
|
-
|
|
386
|
-
# Attempt to guess the better input type given the defined options. By
|
|
387
|
-
# default alwayls fallback to the user :as option, or to a :select when a
|
|
388
|
-
# collection is given.
|
|
389
|
-
def default_input_type(attribute_name, column, options) #:nodoc:
|
|
390
|
-
return options[:as].to_sym if options[:as]
|
|
391
|
-
return :select if options[:collection]
|
|
392
|
-
custom_type = find_custom_type(attribute_name.to_s) and return custom_type
|
|
393
|
-
|
|
394
|
-
input_type = column.try(:type)
|
|
395
|
-
case input_type
|
|
396
|
-
when :timestamp
|
|
397
|
-
:datetime
|
|
398
|
-
when :string, nil
|
|
399
|
-
case attribute_name.to_s
|
|
400
|
-
when /password/ then :password
|
|
401
|
-
when /time_zone/ then :time_zone
|
|
402
|
-
when /country/ then :country
|
|
403
|
-
when /email/ then :email
|
|
404
|
-
when /phone/ then :tel
|
|
405
|
-
when /url/ then :url
|
|
406
|
-
else
|
|
407
|
-
file_method?(attribute_name) ? :file : (input_type || :string)
|
|
408
|
-
end
|
|
409
|
-
else
|
|
410
|
-
input_type
|
|
411
|
-
end
|
|
412
|
-
end
|
|
413
|
-
|
|
414
|
-
def find_custom_type(attribute_name) #:nodoc:
|
|
415
|
-
SimpleForm.input_mappings.find { |match, type|
|
|
416
|
-
attribute_name =~ match
|
|
417
|
-
}.try(:last) if SimpleForm.input_mappings
|
|
418
|
-
end
|
|
419
|
-
|
|
420
|
-
def file_method?(attribute_name) #:nodoc:
|
|
421
|
-
file = @object.send(attribute_name) if @object.respond_to?(attribute_name)
|
|
422
|
-
file && SimpleForm.file_methods.any? { |m| file.respond_to?(m) }
|
|
423
|
-
end
|
|
424
|
-
|
|
425
|
-
def find_attribute_column(attribute_name) #:nodoc:
|
|
426
|
-
if @object.respond_to?(:column_for_attribute)
|
|
427
|
-
@object.column_for_attribute(attribute_name)
|
|
428
|
-
end
|
|
429
|
-
end
|
|
430
|
-
|
|
431
|
-
def find_association_reflection(association) #:nodoc:
|
|
432
|
-
if @object.class.respond_to?(:reflect_on_association)
|
|
433
|
-
@object.class.reflect_on_association(association)
|
|
434
|
-
end
|
|
435
|
-
end
|
|
436
|
-
|
|
437
|
-
# Attempts to find a mapping. It follows the following rules:
|
|
438
|
-
#
|
|
439
|
-
# 1) It tries to find a registered mapping, if succeeds:
|
|
440
|
-
# a) Try to find an alternative with the same name in the Object scope
|
|
441
|
-
# b) Or use the found mapping
|
|
442
|
-
# 2) If not, fallbacks to #{input_type}Input
|
|
443
|
-
# 3) If not, fallbacks to SimpleForm::Inputs::#{input_type}Input
|
|
444
|
-
def find_mapping(input_type) #:nodoc:
|
|
445
|
-
discovery_cache[input_type] ||=
|
|
446
|
-
if mapping = self.class.mappings[input_type]
|
|
447
|
-
mapping_override(mapping) || mapping
|
|
448
|
-
else
|
|
449
|
-
camelized = "#{input_type.to_s.camelize}Input"
|
|
450
|
-
attempt_mapping(camelized, Object) || attempt_mapping(camelized, self.class) ||
|
|
451
|
-
raise("No input found for #{input_type}")
|
|
452
|
-
end
|
|
453
|
-
end
|
|
454
|
-
|
|
455
|
-
def find_wrapper_mapping(input_type) #:nodoc:
|
|
456
|
-
SimpleForm.wrapper_mappings && SimpleForm.wrapper_mappings[input_type]
|
|
457
|
-
end
|
|
458
|
-
|
|
459
|
-
# If cache_discovery is enabled, use the class level cache that persists
|
|
460
|
-
# between requests, otherwise use the instance one.
|
|
461
|
-
def discovery_cache #:nodoc:
|
|
462
|
-
if SimpleForm.cache_discovery
|
|
463
|
-
self.class.discovery_cache
|
|
464
|
-
else
|
|
465
|
-
@discovery_cache ||= {}
|
|
466
|
-
end
|
|
467
|
-
end
|
|
468
|
-
|
|
469
|
-
def mapping_override(klass) #:nodoc:
|
|
470
|
-
name = klass.name
|
|
471
|
-
if name =~ /^SimpleForm::Inputs/
|
|
472
|
-
attempt_mapping name.split("::").last, Object
|
|
473
|
-
end
|
|
474
|
-
end
|
|
475
|
-
|
|
476
|
-
def attempt_mapping(mapping, at) #:nodoc:
|
|
477
|
-
return if SimpleForm.inputs_discovery == false && at == Object
|
|
478
|
-
|
|
479
|
-
begin
|
|
480
|
-
at.const_get(mapping)
|
|
481
|
-
rescue NameError => e
|
|
482
|
-
raise if e.message !~ /#{mapping}$/
|
|
483
|
-
end
|
|
484
|
-
end
|
|
485
|
-
end
|
|
486
|
-
end
|