twitter_bootstrap_form_for 1.0.5 → 2.0.1.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,10 @@
1
1
  Twitter Bootstrap Form For Changes
2
2
  ==================================
3
3
 
4
+ ## 1.0.6 (unreleased)
5
+ - use base FormBuilder rather than user-specified default
6
+ for inline inputs (re-re-closes [\#2])
7
+
4
8
  ## 1.0.5 (2011-11-28) ##
5
9
  - properly name inline input fields (re-closes [\#2])
6
10
 
@@ -18,10 +18,11 @@ Just Rails. But you were going to use that anyway, weren't you?
18
18
  ## Syntax ##
19
19
 
20
20
  ```haml
21
- = twitter_bootstrap_form_for @user do |user|
21
+ / supports both vertical and horizontal forms
22
+ = twitter_bootstrap_form_for @user, :html => { :class => 'form-horizontal'} do |user|
22
23
 
23
24
  / wraps a section in a fieldset with the provided legend text
24
- = user.inputs 'Sign up', :class => 'sign_up' do
25
+ = user.fieldset 'Sign up', :class => 'sign_up' do
25
26
 
26
27
  / generates a standard email field
27
28
  = user.email_field :email, :placeholder => 'me@example.com'
@@ -38,31 +39,31 @@ Just Rails. But you were going to use that anyway, weren't you?
38
39
  / input fields with custom add-ons
39
40
  = user.text_field :twitter_id, 'Twitter', :class => 'medium', :add_on => :prepend do
40
41
  %span.add-on @
41
-
42
+
42
43
  / select fields now have the second parameter as a label
43
- = user.date_select :born_on, 'Born on', {}, :class => 'small'
44
+ = user.date_select :born_on, 'Born on', {}, :class => 'span2'
44
45
 
45
- / inline inputs are not automatically labeled
46
- = user.inline 'Interests' do |inline|
47
- #{inline.text_field :interest_1, :class => 'small'},
48
- #{inline.text_field :interest_2, :class => 'small'}, and
49
- #{inline.text_field :interest_3, :class => 'small'}
46
+ / inline inputs
47
+ = user.label 'Interests' do |controls|
48
+ #{controls.text_field :interest_1, :class => 'span2 inline'},
49
+ #{controls.text_field :interest_2, :class => 'span2 inline'}, and
50
+ #{controls.text_field :interest_3, :class => 'span2 inline'}
50
51
 
51
52
  / group of radio buttons
52
- = user.toggles 'Email Preferences' do
53
- = user.radio_button :email, 'HTML Email', :html, :checked => true
54
- = user.radio_button :email, 'Plain Text', :plain
53
+ = user.label 'Email Preferences' do |controls|
54
+ = controls.radio_button :email, :html, 'HTML Email', :checked => true
55
+ = controls.radio_button :email, :plain, 'Plain Text'
55
56
 
56
57
  / group of checkboxes
57
- = user.toggles 'Agreements' do
58
- = user.check_box :agree, 'I agree to the abusive Terms and Conditions'
59
- = user.check_box :spam, 'I agree to receive all sorts of spam'
60
- = user.check_box :spammer, 'I agree to let the site spam others through my Twitter account'
61
-
62
- / wraps buttons in a distinctive style
63
- = user.actions do
64
- = user.submit 'Sign up'
65
- = button_tag 'Cancel', :type => 'reset', :class => 'btn'
58
+ = user.label 'Agreements' do |controls|
59
+ = controls.check_box :agree, 'I agree to the abusive Terms and Conditions'
60
+ = controls.check_box :spam, 'I agree to receive all sorts of spam'
61
+ = controls.check_box :spammer, 'I agree to let the site spam others through my Twitter account'
62
+
63
+ / wraps buttons in a distinctive style
64
+ = user.actions do
65
+ = user.submit 'Sign up'
66
+ = user.button 'Cancel'
66
67
  ```
67
68
 
68
69
  That code produces the following output, with no custom stylesheets.
@@ -73,9 +74,13 @@ That's it. All of the Rails field helpers you know and love work just like
73
74
  their normal FormBuilder counterparts, but with minor extensions to expose
74
75
  the functionality anticipated by Twitter Bootstrap.
75
76
 
76
- ## Known Bugs ##
77
+ ## Form Helper Changes ##
78
+
79
+ The changes this `FormBuilder` effects to the existing Rails form helpers is
80
+ simple:
77
81
 
78
- - inline fields don't receive error markup ([issue #28])
82
+ * the second parameter becomes the label (pass false to disable, nil for default)
83
+ * the last options hash accepts an `:add_on` key
84
+ * if a block is passed, the HTML it outputs is placed immediately after the input
79
85
 
80
86
  [Twitter Bootstrap]: http://twitter.github.com/bootstrap/
81
- [issue #28]: https://github.com/stouset/twitter_bootstrap_form_for/issues/28
Binary file
@@ -26,32 +26,59 @@ class TwitterBootstrapFormFor::FormBuilder < ActionView::Helpers::FormBuilder
26
26
  # Wraps the contents of the block passed in a fieldset with optional
27
27
  # +legend+ text.
28
28
  #
29
- def inputs(legend = nil, options = {}, &block)
29
+ def fieldset(legend = nil, options = {})
30
30
  template.content_tag(:fieldset, options) do
31
31
  template.concat template.content_tag(:legend, legend) unless legend.nil?
32
- block.call
32
+ yield
33
33
  end
34
34
  end
35
35
 
36
36
  #
37
- # Wraps groups of toggles (radio buttons, checkboxes) with a single label
38
- # and the appropriate markup. All toggle buttons should be rendered
39
- # inside of here, and will not look correct unless they are.
37
+ # Wraps action buttons into their own styled container.
38
+ #
39
+ def actions(&block)
40
+ template.content_tag(:div, :class => 'form-actions', &block)
41
+ end
42
+
40
43
  #
41
- def toggles(label = nil, &block)
42
- template.content_tag(:div, :class => 'clearfix') do
43
- template.concat template.content_tag(:label, label)
44
- template.concat template.content_tag(:div, :class => "input") {
45
- template.content_tag(:ul, :class => "inputs-list") { block.call }
44
+ # Attaches a label to the inputs rendered inside of the block passed to it.
45
+ # Associates the label with the input for the +attribute+ given. If +text+
46
+ #is passed, uses that as the text for the label; otherwise humanizes the
47
+ # +attribute+ name.
48
+ #
49
+ def label(attribute, text = '', options = {}, &block)
50
+ text, attribute = attribute, nil if attribute.kind_of? String
51
+
52
+ options = { :class => 'control-label' }.merge(options)
53
+ id = _wrapper_id attribute, 'control_group'
54
+ classes = _wrapper_classes attribute, 'control-group'
55
+
56
+ template.content_tag(:div, :id => id, :class => classes) do
57
+ template.concat case
58
+ when attribute && text then super(attribute, text, options, &nil)
59
+ when attribute then super(attribute, nil, options, &nil)
60
+ when text then template.label_tag(nil, text, options, &nil)
61
+ end
62
+
63
+ template.concat template.content_tag(:div, :class => 'controls') {
64
+ template.fields_for(
65
+ self.object_name,
66
+ self.object,
67
+ self.options.merge(:builder => TwitterBootstrapFormFor::FormControls),
68
+ &block
69
+ )
46
70
  }
47
71
  end
48
72
  end
49
73
 
50
74
  #
51
- # Wraps action buttons into their own styled container.
75
+ # Renders a button with default classes to style it as a form button.
52
76
  #
53
- def actions(&block)
54
- template.content_tag(:div, :class => 'actions', &block)
77
+ def button(value = nil, options = {})
78
+ super value, {
79
+ :type => 'button',
80
+ :class => 'btn',
81
+ }.merge(options)
55
82
  end
56
83
 
57
84
  #
@@ -59,119 +86,50 @@ class TwitterBootstrapFormFor::FormBuilder < ActionView::Helpers::FormBuilder
59
86
  # button.
60
87
  #
61
88
  def submit(value = nil, options = {})
62
- options[:class] ||= 'btn primary'
63
-
64
- super value, options
65
- end
66
-
67
- #
68
- # Creates bootstrap wrapping before yielding a plain old rails builder
69
- # to the supplied block.
70
- #
71
- def inline(label = nil, &block)
72
- template.content_tag(:div, :class => 'clearfix') do
73
- template.concat template.content_tag(:label, label) if label.present?
74
- template.concat template.content_tag(:div, :class => 'input') {
75
- template.content_tag(:div, :class => 'inline-inputs') do
76
- template.fields_for(
77
- self.object_name,
78
- self.object,
79
- self.options.merge(:builder => ActionView::Base.default_form_builder),
80
- &block
81
- )
82
- end
83
- }
84
- end
89
+ self.button value, {
90
+ :type => 'submit',
91
+ :class => 'btn btn-primary',
92
+ }.merge(options)
85
93
  end
86
94
 
87
95
  INPUTS.each do |input|
88
96
  define_method input do |attribute, *args, &block|
89
- options = args.extract_options!
90
- label = args.first.nil? ? '' : args.shift
91
- classes = [ 'input' ]
92
- classes << ('input-' + options.delete(:add_on).to_s) if options[:add_on]
93
-
94
- self.div_wrapper(attribute) do
95
- template.concat self.label(attribute, label) if label
96
- template.concat template.content_tag(:div, :class => classes.join(' ')) {
97
- template.concat super(attribute, *(args << options))
98
- template.concat error_span(attribute)
99
- block.call if block.present?
100
- }
101
- end
102
- end
103
- end
97
+ options = args.extract_options!
98
+ text = args.any? ? args.shift : ''
104
99
 
105
- TOGGLES.each do |toggle|
106
- define_method toggle do |attribute, *args, &block|
107
- label = args.first.nil? ? '' : args.shift
108
- target = self.object_name.to_s + '_' + attribute.to_s
109
- label_attrs = toggle == :check_box ? { :for => target } : {}
110
-
111
- template.content_tag(:li) do
112
- template.concat template.content_tag(:label, label_attrs) {
113
- template.concat super(attribute, *args)
114
- template.concat ' ' # give the input and span some room
115
- template.concat template.content_tag(:span, label)
116
- }
100
+ self.label(attribute, text) do |builder|
101
+ builder.send(input, attribute, *(args << options), &block)
117
102
  end
118
103
  end
119
104
  end
120
105
 
121
106
  protected
122
107
 
123
- #
124
- # Wraps the contents of +block+ inside a +tag+ with an appropriate class and
125
- # id for the object's +attribute+. HTML options can be overridden by passing
126
- # an +options+ hash.
127
- #
128
- def div_wrapper(attribute, options = {}, &block)
129
- options[:id] = _wrapper_id attribute, options[:id]
130
- options[:class] = _wrapper_classes attribute, options[:class], 'clearfix'
131
-
132
- template.content_tag :div, options, &block
133
- end
134
-
135
- def error_span(attribute, options = {})
136
- options[:class] ||= 'help-inline'
137
-
138
- template.content_tag(
139
- :span, self.errors_for(attribute),
140
- :class => options[:class]
141
- ) if self.errors_on?(attribute)
142
- end
143
-
144
108
  def errors_on?(attribute)
145
109
  self.object.errors[attribute].present? if self.object.respond_to?(:errors)
146
110
  end
147
111
 
148
- def errors_for(attribute)
149
- self.object.errors[attribute].try(:join, ', ')
150
- end
151
-
152
112
  private
153
113
 
154
114
  #
155
115
  # Returns an HTML id to uniquely identify the markup around an input field.
156
- # If a +default+ is provided, it uses that one instead.
157
116
  #
158
- def _wrapper_id(attribute, default = nil)
159
- default || [
117
+ def _wrapper_id(attribute, suffix = nil)
118
+ [
160
119
  _object_name + _object_index,
161
120
  _attribute_name(attribute),
162
- 'input'
163
- ].join('_')
121
+ suffix,
122
+ ].compact.join('_') if attribute
164
123
  end
165
124
 
166
125
  #
167
126
  # Returns any classes necessary for the wrapper div around fields for
168
127
  # +attribute+, such as 'errors' if any errors are present on the attribute.
169
- # This merges any +classes+ passed in.
128
+ # Merges any +classes+ passed in.
170
129
  #
171
130
  def _wrapper_classes(attribute, *classes)
172
- classes.compact.tap do |klasses|
173
- klasses.push 'error' if self.errors_on?(attribute)
174
- end.join(' ')
131
+ classes.push 'error' if attribute and self.errors_on?(attribute)
132
+ classes.compact.join(' ')
175
133
  end
176
134
 
177
135
  def _attribute_name(attribute)
@@ -190,3 +148,68 @@ class TwitterBootstrapFormFor::FormBuilder < ActionView::Helpers::FormBuilder
190
148
  end.to_s
191
149
  end
192
150
  end
151
+
152
+ class TwitterBootstrapFormFor::FormControls < ActionView::Helpers::FormBuilder
153
+ attr_reader :template
154
+ attr_reader :object
155
+ attr_reader :object_name
156
+
157
+ TwitterBootstrapFormFor::FormBuilder::INPUTS.each do |input|
158
+ define_method input do |attribute, *args, &block|
159
+ options = args.extract_options!
160
+ add_on = options.delete(:add_on)
161
+ tag = add_on.present? ? :div : :span
162
+ classes = [ "input", add_on ].compact.join('-')
163
+
164
+ template.content_tag(tag, :class => classes) do
165
+ template.concat super attribute, *(args << options)
166
+ template.concat self.error_span(attribute) if self.errors_on?(attribute)
167
+ block.call if block.present?
168
+ end
169
+ end
170
+ end
171
+
172
+ def check_box(attribute, text, options = {}, checked_value = 1, unchecked_value = 0)
173
+ klasses = _merge_classes 'checkbox', options.delete(:inline) && 'inline'
174
+
175
+ self.label(attribute, :class => klasses) do
176
+ template.concat super(attribute, options, checked_value, unchecked_value)
177
+ template.concat text
178
+ yield if block_given?
179
+ end
180
+ end
181
+
182
+ def radio_button(attribute, value, text = nil, options = {})
183
+ klasses = _merge_classes 'radio', options.delete(:inline) && 'inline'
184
+
185
+ self.label(attribute, :class => klasses) do
186
+ template.concat super(attribute, value, options)
187
+ template.concat text || value.to_s.humanize.titleize
188
+ yield if block_given?
189
+ end
190
+ end
191
+
192
+ protected
193
+
194
+ def error_span(attribute, options = {})
195
+ options[:class] = _merge_classes options[:class], 'help-inline'
196
+
197
+ template.content_tag :span,
198
+ self.errors_for(attribute),
199
+ :class => options[:class]
200
+ end
201
+
202
+ def errors_for(attribute)
203
+ self.object.errors[attribute].try(:join, ', ')
204
+ end
205
+
206
+ def errors_on?(attribute)
207
+ self.object.errors[attribute].present? if self.object.respond_to?(:errors)
208
+ end
209
+
210
+ private
211
+
212
+ def _merge_classes(string, *classes)
213
+ string.to_s.split(' ').push(*classes.compact).join(' ')
214
+ end
215
+ end
@@ -18,13 +18,13 @@ module TwitterBootstrapFormFor::FormHelpers
18
18
 
19
19
  private
20
20
 
21
- BLANK_FIELD_ERROR_PROC = lambda {|input, _| input }
21
+ BLANK_FIELD_ERROR_PROC = lambda {|input, *_| input }
22
22
 
23
23
  def _override_field_error_proc
24
- original_field_error_proc = self.field_error_proc
25
- self.field_error_proc = BLANK_FIELD_ERROR_PROC
24
+ original_field_error_proc = ::ActionView::Base.field_error_proc
25
+ ::ActionView::Base.field_error_proc = BLANK_FIELD_ERROR_PROC
26
26
  yield
27
27
  ensure
28
- self.field_error_proc = original_field_error_proc
28
+ ::ActionView::Base.field_error_proc = original_field_error_proc
29
29
  end
30
30
  end
@@ -1,3 +1,3 @@
1
1
  module TwitterBootstrapFormFor
2
- VERSION = '1.0.5'
2
+ VERSION = '2.0.1.0.rc1'
3
3
  end
metadata CHANGED
@@ -1,19 +1,19 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: twitter_bootstrap_form_for
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.5
5
- prerelease:
4
+ version: 2.0.1.0.rc1
5
+ prerelease: 8
6
6
  platform: ruby
7
7
  authors:
8
8
  - Stephen Touset
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-11-28 00:00:00.000000000 Z
12
+ date: 2012-02-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: railties
16
- requirement: &70263346386380 !ruby/object:Gem::Requirement
16
+ requirement: &70350113394920 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '3'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70263346386380
24
+ version_requirements: *70350113394920
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: actionpack
27
- requirement: &70263346384740 !ruby/object:Gem::Requirement
27
+ requirement: &70350113394060 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,7 +32,7 @@ dependencies:
32
32
  version: '3'
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70263346384740
35
+ version_requirements: *70350113394060
36
36
  description: A custom Rails FormBuilder that assumes the use of Twitter Bootstrap
37
37
  email:
38
38
  - stephen@touset.org
@@ -65,9 +65,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
65
65
  required_rubygems_version: !ruby/object:Gem::Requirement
66
66
  none: false
67
67
  requirements:
68
- - - ! '>='
68
+ - - ! '>'
69
69
  - !ruby/object:Gem::Version
70
- version: '0'
70
+ version: 1.3.1
71
71
  requirements: []
72
72
  rubyforge_project:
73
73
  rubygems_version: 1.8.11
@@ -75,3 +75,4 @@ signing_key:
75
75
  specification_version: 3
76
76
  summary: Rails form builder optimized for Twitter Bootstrap
77
77
  test_files: []
78
+ has_rdoc: