twitter_bootstrap_formalwear 0.9.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.
Files changed (40) hide show
  1. data/LICENSE.markdown +22 -0
  2. data/README.markdown +142 -0
  3. data/Rakefile +38 -0
  4. data/lib/tasks/twitter_bootstrap_formalwear_tasks.rake +4 -0
  5. data/lib/twitter_bootstrap_formalwear.rb +10 -0
  6. data/lib/twitter_bootstrap_formalwear/form_builder.rb +256 -0
  7. data/lib/twitter_bootstrap_formalwear/form_helpers.rb +30 -0
  8. data/lib/twitter_bootstrap_formalwear/railtie.rb +9 -0
  9. data/lib/twitter_bootstrap_formalwear/version.rb +3 -0
  10. data/test/dummy/README.rdoc +261 -0
  11. data/test/dummy/Rakefile +7 -0
  12. data/test/dummy/app/assets/javascripts/application.js +15 -0
  13. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  14. data/test/dummy/app/controllers/application_controller.rb +3 -0
  15. data/test/dummy/app/helpers/application_helper.rb +2 -0
  16. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  17. data/test/dummy/config.ru +4 -0
  18. data/test/dummy/config/application.rb +56 -0
  19. data/test/dummy/config/boot.rb +10 -0
  20. data/test/dummy/config/database.yml +25 -0
  21. data/test/dummy/config/environment.rb +5 -0
  22. data/test/dummy/config/environments/development.rb +37 -0
  23. data/test/dummy/config/environments/production.rb +67 -0
  24. data/test/dummy/config/environments/test.rb +37 -0
  25. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  26. data/test/dummy/config/initializers/inflections.rb +15 -0
  27. data/test/dummy/config/initializers/mime_types.rb +5 -0
  28. data/test/dummy/config/initializers/secret_token.rb +7 -0
  29. data/test/dummy/config/initializers/session_store.rb +8 -0
  30. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  31. data/test/dummy/config/locales/en.yml +5 -0
  32. data/test/dummy/config/routes.rb +58 -0
  33. data/test/dummy/public/404.html +26 -0
  34. data/test/dummy/public/422.html +26 -0
  35. data/test/dummy/public/500.html +25 -0
  36. data/test/dummy/public/favicon.ico +0 -0
  37. data/test/dummy/script/rails +6 -0
  38. data/test/test_helper.rb +15 -0
  39. data/test/twitter_bootstrap_formalwear_test.rb +7 -0
  40. metadata +162 -0
@@ -0,0 +1,22 @@
1
+ The MIT License
2
+ ===============
3
+
4
+ Copyright (C) 2011 by Stephen Touset
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
@@ -0,0 +1,142 @@
1
+ Twitter Bootstrap Formalwear
2
+ ============================
3
+
4
+ Twitter Bootstrap Formalwear dresses up your Ruby on Rails forms with
5
+ [Twitter Bootstrap] markup. It is a lightly-modified fork of
6
+ [`twitter_bootstrap_form_for`](https://github.com/stouset/twitter_bootstrap_form_for).
7
+
8
+ Unlike `twitter_bootstrap_form_for`, which overwrites existing [Rails FormHelper] methods,
9
+ `twitter_bootstrap_formalwear` extends FormHelper with additional methods to generate
10
+ labels, controls, control groups, and action blocks.
11
+
12
+ Because existing methods remain intact, you maintain the option to generate
13
+ non-Bootstrap markup in your form if necessary.
14
+
15
+
16
+ ## Dependencies ##
17
+
18
+ Rails and Twitter Bootstrap (v2.0+), of course. A familiarity with Rails FormHelper
19
+ usage and syntax will be helpful.
20
+
21
+
22
+ ## Installation ##
23
+
24
+ Add the dependency to your `Gemfile`:
25
+
26
+ gem 'twitter_bootstrap_formalwear'
27
+
28
+ Then run `bundle` from the project directory.
29
+
30
+
31
+ ## Usage/Syntax ##
32
+
33
+
34
+ ### Initializing a Form: ###
35
+
36
+ To use `twitter_bootstrap_formalwear`, first create a form using the same syntax as `form_for` or `fields_for`:
37
+
38
+ <%= formalwear_form_for (@person, :html => { :class => 'form-horizontal' }) do |f| %>
39
+
40
+ <%= formalwear_fields_for (:person, @client do |f| %>
41
+
42
+
43
+ ### Generating Form Elements: ###
44
+
45
+ `twitter_bootstrap_formalwear` extends `form_for` and `fields_for` with additional methods:
46
+
47
+ - group
48
+ - label
49
+ - controls
50
+ - actions
51
+
52
+ These methods can be used generically to create container elements:
53
+
54
+ <%= f.group do %>
55
+ <!-- HTML goes here! -->
56
+ <% end %>
57
+
58
+ # =>
59
+ <div class="control-group">
60
+ <!-- HTML goes here! -->
61
+ </div>
62
+
63
+ Or, more usefully, they can be prepended with a form element (of any type supported by the [Rails FormHelper]):
64
+
65
+ <%= f.text_field_group :first_name, :class => 'input-large' %>
66
+
67
+ # =>
68
+ <div class="control-group">
69
+ <label class="control-label" for="first_name">First name</label>
70
+ <div class="controls">
71
+ <input type="text" name="first_name" value="" class="input-large" />
72
+ </div>
73
+ </div>
74
+
75
+ If you need fine-grained control, you can generate only a Bootstrap label:
76
+
77
+ <%= f.text_field_label :first_name %>
78
+
79
+ # =>
80
+ <label class="control-label" for="first_name">First name</label>
81
+
82
+ ...or a Bootstrap control:
83
+
84
+ <%= f.text_field_control :first_name, :class => 'input-large' %>
85
+
86
+ # =>
87
+ <div class="controls">
88
+ <input type="text" name="first_name" value="" class="input-large" />
89
+ </div>
90
+
91
+ For you control freaks, the standard Rails FormHelper methods still work if needed:
92
+
93
+ <%= f.text_field :first_name, :class => 'input-large' %>
94
+
95
+ # =>
96
+ <input type="text" name="first_name" value="" class="input-large" />
97
+
98
+
99
+ ### More Usage Examples ###
100
+
101
+ Form actions:
102
+
103
+ <%= f.actions do %>
104
+ <%= f.submit 'Sign up', :class => 'btn' %>
105
+ <% end %>
106
+
107
+ # =>
108
+ <div class="form-actions">
109
+ <input class="btn" type="submit" value="Sign up">
110
+ </div>
111
+
112
+ Checkboxes (note that omitting the first argument will omit the label element):
113
+
114
+ <%= f.group do %>
115
+ <%= f.check_box :remember_me %> Remember Me?
116
+ <% end %>
117
+
118
+ # =>
119
+ <div class="control-group">
120
+ <div class="controls">
121
+ <input id="remember_me" name="remember_me" type="checkbox" value="1" /> Remember Me?
122
+ </div>
123
+ </div>
124
+
125
+ Add-ons and form help text: may or may not work yet. Documentation coming soon.
126
+
127
+ ## Bugs / Known Issues ##
128
+
129
+ You may have already noticed that the `label` method exists in `form_for`, even
130
+ though I claim above that `twitter_bootstrap_formalwear` doesn't overwrite existing
131
+ code. But since the new method dances so lightly (just adding `class="control-label"`),
132
+ I think it's safe to simply overwrite the old method. Thoughts?
133
+
134
+ I've been using this fork successfully on my own projects, but some less common
135
+ use cases may be buggy.
136
+
137
+ ## Todo ##
138
+
139
+ Tests!
140
+
141
+ [Twitter Bootstrap]: http://twitter.github.com/bootstrap/
142
+ [Rails FormHelper]: http://api.rubyonrails.org/classes/ActionView/Helpers/FormHelper.html
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'TwitterBootstrapFormalwear'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+
24
+
25
+
26
+ Bundler::GemHelper.install_tasks
27
+
28
+ require 'rake/testtask'
29
+
30
+ Rake::TestTask.new(:test) do |t|
31
+ t.libs << 'lib'
32
+ t.libs << 'test'
33
+ t.pattern = 'test/**/*_test.rb'
34
+ t.verbose = false
35
+ end
36
+
37
+
38
+ task :default => :test
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :twitter_bootstrap_formalwear do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,10 @@
1
+ require 'action_view'
2
+
3
+ module TwitterBootstrapFormalwear
4
+ autoload :FormBuilder, 'twitter_bootstrap_formalwear/form_builder'
5
+ autoload :FormHelpers, 'twitter_bootstrap_formalwear/form_helpers'
6
+ autoload :Railtie, 'twitter_bootstrap_formalwear/railtie'
7
+ autoload :VERSION, 'twitter_bootstrap_formalwear/version'
8
+ end
9
+
10
+ TwitterBootstrapFormalwear::Railtie # trigger loading the Railtie
@@ -0,0 +1,256 @@
1
+ require 'twitter_bootstrap_formalwear'
2
+ require 'action_view/helpers'
3
+
4
+ class TwitterBootstrapFormalwear::FormBuilder < ActionView::Helpers::FormBuilder
5
+ include TwitterBootstrapFormalwear::FormHelpers
6
+
7
+ attr_reader :template
8
+ attr_reader :object
9
+ attr_reader :object_name
10
+
11
+ INPUTS = [
12
+ :check_box,
13
+ :radio_button,
14
+ :select,
15
+ *ActionView::Helpers::FormBuilder.instance_methods.grep(%r{
16
+ _(area|field|select)$ # all area, field, and select methods
17
+ }mx).map(&:to_sym)
18
+ ]
19
+
20
+ INPUTS.delete(:hidden_field)
21
+
22
+ #TOGGLES = [
23
+ # :check_box,
24
+ # :radio_button,
25
+ #]
26
+
27
+ #
28
+ # Wraps the contents of the block passed in a fieldset with optional
29
+ # +legend+ text.
30
+ #
31
+ def fieldset(legend = nil, options = {})
32
+ template.content_tag(:fieldset, options) do
33
+ template.concat template.content_tag(:legend, legend) unless legend.nil?
34
+ yield
35
+ end
36
+ end
37
+
38
+ #
39
+ # Wraps action buttons into their own styled container.
40
+ #
41
+ def actions(&block)
42
+ template.content_tag(:div, :class => 'form-actions', &block)
43
+ end
44
+
45
+ #
46
+ # Generates a control-group div element for the given +attribute+,
47
+ # containing label and controls elements.
48
+ #
49
+ def group(attribute = '', text = '', options = {}, &block)
50
+ text, attribute = attribute, nil if attribute.kind_of? String
51
+
52
+ id = _wrapper_id attribute, 'control_group'
53
+ classes = _wrapper_classes attribute, 'control-group'
54
+
55
+ template.content_tag(:div, :id => id, :class => classes) do
56
+ if !attribute.blank?
57
+ template.concat self.label(attribute, text, options, &block)
58
+ end
59
+ template.concat self.controls(attribute, text, options, &block)
60
+ end
61
+ end
62
+
63
+ #
64
+ # Generates a label element for the given +attribute+. If +text+ is passed, uses
65
+ # that as the text for the label; otherwise humanizes the +attribute+ name.
66
+ #
67
+ def label(attribute, text = '', options = {}, &block)
68
+ text, attribute = attribute, nil if attribute.kind_of? String
69
+
70
+ options = options.merge(:class => 'control-label')
71
+
72
+ case
73
+ when attribute && text then super(attribute, text, options, &nil)
74
+ when attribute then super(attribute, nil, options, &nil)
75
+ when text then template.label_tag(nil, text, options, &nil)
76
+ end
77
+ end
78
+
79
+ #
80
+ # Generates a controls div element for the given +attribute+.
81
+ #
82
+ def controls(attribute, text = '', options = {}, &block)
83
+ text, attribute = attribute, nil if attribute.kind_of? String
84
+
85
+ template.content_tag(:div, :class => 'controls') {
86
+ template.fields_for(
87
+ self.object_name,
88
+ self.object,
89
+ self.options.merge(:builder => TwitterBootstrapFormalwear::FormControls),
90
+ &block
91
+ )
92
+ }
93
+ end
94
+
95
+ #
96
+ # Renders a button with default classes to style it as a form button.
97
+ #
98
+ def button(value = nil, options = {})
99
+ super value, {
100
+ :type => 'button',
101
+ :class => 'btn',
102
+ }.merge(options)
103
+ end
104
+
105
+ #
106
+ # Renders a submit tag with default classes to style it as a primary form
107
+ # button.
108
+ #
109
+ def submit(value = nil, options = {})
110
+ self.button value, {
111
+ :type => 'submit',
112
+ :class => 'btn btn-primary',
113
+ }.merge(options)
114
+ end
115
+
116
+ INPUTS.each do |input|
117
+ define_method input.to_s + '_group' do |attribute, *args, &block|
118
+ options = args.extract_options!
119
+ text = args.any? ? args.shift : ''
120
+
121
+ self.group(attribute, text, options) do |builder|
122
+ builder.send(input, attribute, *(args << options), &block)
123
+ end
124
+ end
125
+
126
+ define_method input.to_s + '_label' do |attribute, *args, &block|
127
+ options = args.extract_options!
128
+ text = args.any? ? args.shift : ''
129
+
130
+ self.label(attribute, text, options) do |builder|
131
+ builder.send(input, attribute, *(args << options), &block)
132
+ end
133
+ end
134
+
135
+ define_method input.to_s + '_controls' do |attribute, *args, &block|
136
+ options = args.extract_options!
137
+ text = args.any? ? args.shift : ''
138
+
139
+ self.controls(attribute, text, options) do |builder|
140
+ builder.send(input, attribute, *(args << options), &block)
141
+ end
142
+ end
143
+ end
144
+
145
+ protected
146
+
147
+ def errors_on?(attribute)
148
+ self.object.errors[attribute].present? if self.object.respond_to?(:errors)
149
+ end
150
+
151
+ private
152
+
153
+ #
154
+ # Returns an HTML id to uniquely identify the markup around an input field.
155
+ #
156
+ def _wrapper_id(attribute, suffix = nil)
157
+ [
158
+ _object_name + _object_index,
159
+ _attribute_name(attribute),
160
+ suffix,
161
+ ].compact.join('_') if attribute
162
+ end
163
+
164
+ #
165
+ # Returns any classes necessary for the wrapper div around fields for
166
+ # +attribute+, such as 'errors' if any errors are present on the attribute.
167
+ # Merges any +classes+ passed in.
168
+ #
169
+ def _wrapper_classes(attribute, *classes)
170
+ classes.push 'error' if attribute and self.errors_on?(attribute)
171
+ classes.compact.join(' ')
172
+ end
173
+
174
+ def _attribute_name(attribute)
175
+ attribute.to_s.gsub(/[\?\/\-]$/, '')
176
+ end
177
+
178
+ def _object_name
179
+ self.object_name.to_s.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
180
+ end
181
+
182
+ def _object_index
183
+ case
184
+ when options.has_key?(:index) then options[:index]
185
+ when defined?(@auto_index) then @auto_index
186
+ else nil
187
+ end.to_s
188
+ end
189
+ end
190
+
191
+ class TwitterBootstrapFormalwear::FormControls < ActionView::Helpers::FormBuilder
192
+ attr_reader :template
193
+ attr_reader :object
194
+ attr_reader :object_name
195
+
196
+ TwitterBootstrapFormalwear::FormBuilder::INPUTS.each do |input|
197
+ define_method input do |attribute, *args, &block|
198
+ options = args.extract_options!
199
+ add_on = options.delete(:add_on)
200
+ tag = add_on.present? ? :div : :span
201
+ classes = [ "input", add_on ].compact.join('-')
202
+
203
+ template.content_tag(tag, :class => classes) do
204
+ template.concat super attribute, *(args << options)
205
+ template.concat self.error_span(attribute) if self.errors_on?(attribute)
206
+ block.call if block.present?
207
+ end
208
+ end
209
+ end
210
+
211
+ =begin
212
+ def check_box(attribute, text, options = {}, checked_value = 1, unchecked_value = 0)
213
+ klasses = _merge_classes 'checkbox', options.delete(:inline) && 'inline'
214
+
215
+ self.label(attribute, :class => klasses) do
216
+ template.concat super(attribute, options, checked_value, unchecked_value)
217
+ template.concat text
218
+ yield if block_given?
219
+ end
220
+ end
221
+
222
+ def radio_button(attribute, value, text = nil, options = {})
223
+ klasses = _merge_classes 'radio', options.delete(:inline) && 'inline'
224
+
225
+ self.label(attribute, :class => klasses) do
226
+ template.concat super(attribute, value, options)
227
+ template.concat text || value.to_s.humanize.titleize
228
+ yield if block_given?
229
+ end
230
+ end
231
+ =end
232
+
233
+ protected
234
+
235
+ def error_span(attribute, options = {})
236
+ options[:class] = _merge_classes options[:class], 'help-inline'
237
+
238
+ template.content_tag :span,
239
+ self.errors_for(attribute),
240
+ :class => options[:class]
241
+ end
242
+
243
+ def errors_for(attribute)
244
+ self.object.errors[attribute].try(:join, ', ')
245
+ end
246
+
247
+ def errors_on?(attribute)
248
+ self.object.errors[attribute].present? if self.object.respond_to?(:errors)
249
+ end
250
+
251
+ private
252
+
253
+ def _merge_classes(string, *classes)
254
+ string.to_s.split(' ').push(*classes.compact).join(' ')
255
+ end
256
+ end