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.
- data/LICENSE.markdown +22 -0
- data/README.markdown +142 -0
- data/Rakefile +38 -0
- data/lib/tasks/twitter_bootstrap_formalwear_tasks.rake +4 -0
- data/lib/twitter_bootstrap_formalwear.rb +10 -0
- data/lib/twitter_bootstrap_formalwear/form_builder.rb +256 -0
- data/lib/twitter_bootstrap_formalwear/form_helpers.rb +30 -0
- data/lib/twitter_bootstrap_formalwear/railtie.rb +9 -0
- data/lib/twitter_bootstrap_formalwear/version.rb +3 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +56 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +58 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/test_helper.rb +15 -0
- data/test/twitter_bootstrap_formalwear_test.rb +7 -0
- metadata +162 -0
data/LICENSE.markdown
ADDED
@@ -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.
|
data/README.markdown
ADDED
@@ -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
|
data/Rakefile
ADDED
@@ -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,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
|