twitter_bootstrap_formalwear 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
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