simple_form 3.0.0 → 3.0.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1bda3ec212b0e4f1da3e765b704b417893098679
4
- data.tar.gz: 81b0e670aae4612d40793b872f6fc553fd6d05ab
3
+ metadata.gz: 16a083a7be892436f572b9e055699a94d889d8ca
4
+ data.tar.gz: 5240794b73edefddc037c5a528f3a5eb362fb7bc
5
5
  SHA512:
6
- metadata.gz: e242bc383e74b99f508350376d007c18bd7aedb325632d2d3a8a28043275efa03a9c5b90e6d4a00cec68b53de6636bdb43b3a0af5a83160b8a8bad88919d6248
7
- data.tar.gz: 70b1845d8a357e8e4ff85def7540d849a8fcd4d070a78e185f2c369da7a453b7def368309dc3339a5a5c2b2f8f47613db2be328ac0dbbb90c3fa74aef996f80a
6
+ metadata.gz: 54cfce6263962c3918d36467a0681dc31e48262a777110c2e05ebf4f8daa8bf2ce8c0dec773c9cf37d60af34f424936d09766e34ccb98572780c0f877d9051b8
7
+ data.tar.gz: 70d460b6f6f5467aa84b599ffc5e1cdbb59b6b581d5cc51ac064c2f2100b1ed70f1550073db7ca3670a4955a2a33a49657b81a3c70b6e3bdccef1a0ab88227e4
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ ## 3.0.1
2
+
3
+ ### bug fix
4
+ * Fix XSS vulnerability on label, hint and error components.
5
+
1
6
  ## 3.0.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 = "3.0.0".freeze
2
+ VERSION = "3.0.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&#39;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,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.0
4
+ version: 3.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - José Valim
@@ -10,7 +10,7 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2013-09-21 00:00:00.000000000 Z
13
+ date: 2013-11-29 00:00:00.000000000 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: activemodel
@@ -72,7 +72,6 @@ files:
72
72
  - lib/generators/simple_form/templates/README
73
73
  - lib/generators/simple_form/USAGE
74
74
  - lib/simple_form/action_view_extensions/builder.rb
75
- - lib/simple_form/action_view_extensions/builder.rb.orig
76
75
  - lib/simple_form/action_view_extensions/form_helper.rb
77
76
  - lib/simple_form/components/errors.rb
78
77
  - lib/simple_form/components/hints.rb
@@ -87,7 +86,6 @@ files:
87
86
  - lib/simple_form/components.rb
88
87
  - lib/simple_form/error_notification.rb
89
88
  - lib/simple_form/form_builder.rb
90
- - lib/simple_form/form_builder.rb.orig
91
89
  - lib/simple_form/helpers/autofocus.rb
92
90
  - lib/simple_form/helpers/disabled.rb
93
91
  - lib/simple_form/helpers/readonly.rb
@@ -117,7 +115,6 @@ files:
117
115
  - lib/simple_form/railtie.rb
118
116
  - lib/simple_form/tags.rb
119
117
  - lib/simple_form/version.rb
120
- - lib/simple_form/version.rb.orig
121
118
  - lib/simple_form/wrappers/builder.rb
122
119
  - lib/simple_form/wrappers/many.rb
123
120
  - lib/simple_form/wrappers/root.rb
@@ -180,7 +177,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
180
177
  version: '0'
181
178
  requirements: []
182
179
  rubyforge_project: simple_form
183
- rubygems_version: 2.0.6
180
+ rubygems_version: 2.1.11
184
181
  signing_key:
185
182
  specification_version: 4
186
183
  summary: Forms made easy!
@@ -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