simple_form 2.1.0 → 2.1.1

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.

Potentially problematic release.


This version of simple_form might be problematic. Click here for more details.

checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 27282450639f5fb5ad27d5c59f274aab91a4be7a
4
+ data.tar.gz: 1ded8f66c7a18f3ea2679ab4da49f9851e8293c1
5
+ SHA512:
6
+ metadata.gz: 4354640ba60801f7150c215638d03fe65f446c94ec30f93aafafe23a991a64b3789cc39a5f61b86708acd3cbd4fdedd078e29bf35c62e58ac930f436c5320ba6
7
+ data.tar.gz: 3b8dd8220e0b56ec8a6893ac0c20a60dd4950f98b4297fc197de2c28c76f2cbd9adae3dc7f0c854b7dfe9542a514b6f333257869bbf99e5fcd53fa5f7c35cdc1
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 2.1.1
2
+
3
+ ### bug fix
4
+ * Fix XSS vulnerability on label, hint and error components.
5
+
1
6
  ## 2.1.0
2
7
 
3
8
  ### enhancements
@@ -12,7 +12,7 @@ module SimpleForm
12
12
  protected
13
13
 
14
14
  def error_text
15
- "#{options[:error_prefix]} #{errors.send(error_method)}".lstrip.html_safe
15
+ "#{html_escape(options[:error_prefix])} #{errors.send(error_method)}".lstrip.html_safe
16
16
  end
17
17
 
18
18
  def error_method
@@ -5,8 +5,13 @@ module SimpleForm
5
5
  def hint
6
6
  @hint ||= begin
7
7
  hint = options[:hint]
8
- hint_content = hint.is_a?(String) ? hint : translate(:hints)
9
- hint_content.html_safe if hint_content
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
 
@@ -30,7 +30,7 @@ module SimpleForm
30
30
  end
31
31
 
32
32
  def label_text
33
- SimpleForm.label_text.call(raw_label_text, required_label_text).strip.html_safe
33
+ SimpleForm.label_text.call(html_escape(raw_label_text), required_label_text).strip.html_safe
34
34
  end
35
35
 
36
36
  def label_target
@@ -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
@@ -1,3 +1,3 @@
1
1
  module SimpleForm
2
- VERSION = "2.1.0".freeze
2
+ VERSION = "2.1.1".freeze
3
3
  end
@@ -80,8 +80,13 @@ class ErrorTest < ActionView::TestCase
80
80
  assert_no_select 'p.error[error_method]'
81
81
  end
82
82
 
83
- test 'error should generate an error message with raw HTML tags' do
83
+ test 'error should escape error prefix text' do
84
84
  with_error_for @user, :name, :error_prefix => '<b>Name</b>'
85
+ assert_select 'span.error', "&lt;b&gt;Name&lt;/b&gt; can't be blank"
86
+ end
87
+
88
+ test 'error should generate an error message with raw HTML tags' do
89
+ with_error_for @user, :name, :error_prefix => '<b>Name</b>'.html_safe
85
90
  assert_select 'span.error', "Name can't be blank"
86
91
  assert_select 'span.error b', "Name"
87
92
  end
@@ -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', "&lt;script&gt;alert(1337)&lt;/script&gt;"
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't be blank"
141
+ assert_select 'div.omg_hint', "can&#x27;t be blank"
136
142
  end
137
143
  end
138
144
  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', "&lt;script&gt;alert(1337)&lt;/script&gt;"
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/
metadata CHANGED
@@ -1,8 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simple_form
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.1.0
5
- prerelease:
4
+ version: 2.1.1
6
5
  platform: ruby
7
6
  authors:
8
7
  - José Valim
@@ -11,12 +10,11 @@ authors:
11
10
  autorequire:
12
11
  bindir: bin
13
12
  cert_chain: []
14
- date: 2013-02-26 00:00:00.000000000 Z
13
+ date: 2013-11-29 00:00:00.000000000 Z
15
14
  dependencies:
16
15
  - !ruby/object:Gem::Dependency
17
16
  name: activemodel
18
17
  requirement: !ruby/object:Gem::Requirement
19
- none: false
20
18
  requirements:
21
19
  - - ~>
22
20
  - !ruby/object:Gem::Version
@@ -24,7 +22,6 @@ dependencies:
24
22
  type: :runtime
25
23
  prerelease: false
26
24
  version_requirements: !ruby/object:Gem::Requirement
27
- none: false
28
25
  requirements:
29
26
  - - ~>
30
27
  - !ruby/object:Gem::Version
@@ -32,7 +29,6 @@ dependencies:
32
29
  - !ruby/object:Gem::Dependency
33
30
  name: actionpack
34
31
  requirement: !ruby/object:Gem::Requirement
35
- none: false
36
32
  requirements:
37
33
  - - ~>
38
34
  - !ruby/object:Gem::Version
@@ -40,7 +36,6 @@ dependencies:
40
36
  type: :runtime
41
37
  prerelease: false
42
38
  version_requirements: !ruby/object:Gem::Requirement
43
- none: false
44
39
  requirements:
45
40
  - - ~>
46
41
  - !ruby/object:Gem::Version
@@ -65,7 +60,6 @@ files:
65
60
  - lib/generators/simple_form/templates/README
66
61
  - lib/generators/simple_form/USAGE
67
62
  - lib/simple_form/action_view_extensions/builder.rb
68
- - lib/simple_form/action_view_extensions/builder.rb.orig
69
63
  - lib/simple_form/action_view_extensions/form_helper.rb
70
64
  - lib/simple_form/components/errors.rb
71
65
  - lib/simple_form/components/hints.rb
@@ -81,7 +75,6 @@ files:
81
75
  - lib/simple_form/core_ext/hash.rb
82
76
  - lib/simple_form/error_notification.rb
83
77
  - lib/simple_form/form_builder.rb
84
- - lib/simple_form/form_builder.rb.orig
85
78
  - lib/simple_form/helpers/autofocus.rb
86
79
  - lib/simple_form/helpers/disabled.rb
87
80
  - lib/simple_form/helpers/readonly.rb
@@ -109,7 +102,6 @@ files:
109
102
  - lib/simple_form/inputs.rb
110
103
  - lib/simple_form/map_type.rb
111
104
  - lib/simple_form/version.rb
112
- - lib/simple_form/version.rb.orig
113
105
  - lib/simple_form/wrappers/builder.rb
114
106
  - lib/simple_form/wrappers/many.rb
115
107
  - lib/simple_form/wrappers/root.rb
@@ -154,33 +146,26 @@ files:
154
146
  - test/test_helper.rb
155
147
  homepage: https://github.com/plataformatec/simple_form
156
148
  licenses: []
149
+ metadata: {}
157
150
  post_install_message:
158
151
  rdoc_options: []
159
152
  require_paths:
160
153
  - lib
161
154
  required_ruby_version: !ruby/object:Gem::Requirement
162
- none: false
163
155
  requirements:
164
- - - ! '>='
156
+ - - '>='
165
157
  - !ruby/object:Gem::Version
166
158
  version: '0'
167
- segments:
168
- - 0
169
- hash: -389428450847468473
170
159
  required_rubygems_version: !ruby/object:Gem::Requirement
171
- none: false
172
160
  requirements:
173
- - - ! '>='
161
+ - - '>='
174
162
  - !ruby/object:Gem::Version
175
163
  version: '0'
176
- segments:
177
- - 0
178
- hash: -389428450847468473
179
164
  requirements: []
180
165
  rubyforge_project: simple_form
181
- rubygems_version: 1.8.23
166
+ rubygems_version: 2.1.11
182
167
  signing_key:
183
- specification_version: 3
168
+ specification_version: 4
184
169
  summary: Forms made easy!
185
170
  test_files:
186
171
  - test/action_view_extensions/builder_test.rb
@@ -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
@@ -1,7 +0,0 @@
1
- module SimpleForm
2
- <<<<<<< HEAD
3
- VERSION = "2.0.4".freeze
4
- =======
5
- VERSION = "2.1.0.dev".freeze
6
- >>>>>>> issue-720
7
- end