forme 1.3.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +16 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +9 -1
- data/Rakefile +10 -39
- data/lib/forme.rb +8 -4
- data/lib/forme/bs3.rb +387 -0
- data/lib/forme/erb.rb +4 -2
- data/lib/forme/form.rb +2 -0
- data/lib/forme/input.rb +2 -0
- data/lib/forme/rails.rb +2 -0
- data/lib/forme/raw.rb +2 -0
- data/lib/forme/sinatra.rb +2 -0
- data/lib/forme/tag.rb +2 -0
- data/lib/forme/transformers/error_handler.rb +2 -0
- data/lib/forme/transformers/formatter.rb +37 -13
- data/lib/forme/transformers/helper.rb +3 -1
- data/lib/forme/transformers/inputs_wrapper.rb +2 -0
- data/lib/forme/transformers/labeler.rb +2 -0
- data/lib/forme/transformers/serializer.rb +2 -0
- data/lib/forme/transformers/wrapper.rb +3 -1
- data/lib/forme/version.rb +3 -1
- data/lib/roda/plugins/forme.rb +2 -0
- data/lib/sequel/plugins/forme.rb +6 -2
- data/spec/bs3_reference_spec.rb +358 -0
- data/spec/bs3_sequel_plugin_spec.rb +523 -0
- data/spec/bs3_spec.rb +690 -0
- data/spec/erb_helper.rb +27 -25
- data/spec/forme_spec.rb +542 -526
- data/spec/rails_integration_spec.rb +26 -26
- data/spec/roda_integration_spec.rb +14 -5
- data/spec/sequel_plugin_spec.rb +247 -233
- data/spec/sinatra_integration_spec.rb +12 -3
- data/spec/spec_helper.rb +1 -14
- metadata +120 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b392c21e8ea5b18c3b1c125b84c36379b40595bf
|
4
|
+
data.tar.gz: de4ef22b48745082a9661989dd745824ef75ea07
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bfc76ba5baa373ce677fe2fc7f44703d2e8783b9752e667a356a454293b11efecabe4b03fab498b040ac7afd3e0bc10a09f463681f5b839fa7620a984ba7bc1b
|
7
|
+
data.tar.gz: 93b41b112be039b14e2368618e9bc00d8599d7935828fddc27870bdd1426199c58fbc6c63b4085c48368e87c717ed5b0f952d76d684143ef455e1d19854c078f
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
=== 1.4.0 (2016-02-01)
|
2
|
+
|
3
|
+
* Ignore submit buttons when using the :readonly formatter (jeremyevans)
|
4
|
+
|
5
|
+
* Respect :formatter option for radioset and checkboxset inputs (jeremyevans)
|
6
|
+
|
7
|
+
* Add support for running with --enable-frozen-string-literal on ruby 2.3 (jeremyevans)
|
8
|
+
|
9
|
+
* Integrate with the Sequel association_pks plugin in the Sequel plugin (jeremyevans)
|
10
|
+
|
11
|
+
* Do not add required * to label if :label=>nil in the Sequel plugin (jeremyevans)
|
12
|
+
|
13
|
+
* Add forme/bs3 library for Bootstrap 3 support (kematzy, jeremyevans) (#12, #14)
|
14
|
+
|
15
|
+
* Support an :html option in the default formatter to override the HTML created (jeremyevans)
|
16
|
+
|
1
17
|
=== 1.3.0 (2015-04-17)
|
2
18
|
|
3
19
|
* Support option groups in select, checkboxset, and radioset inputs via :optgroups option (jeremyevans)
|
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -73,7 +73,6 @@ passing the appopriate option to +input+ or +inputs+:
|
|
73
73
|
|
74
74
|
f.input(:name, :wrapper=>:p)
|
75
75
|
|
76
|
-
|
77
76
|
= Installation
|
78
77
|
|
79
78
|
gem install forme
|
@@ -842,6 +841,15 @@ You can mark a configuration as the default using:
|
|
842
841
|
|
843
842
|
Forme.default_config = :mine
|
844
843
|
|
844
|
+
=== Bootstrap 3 Support
|
845
|
+
|
846
|
+
Forme ships with support for Bootstrap 3 HTML formatting. This support is shipped in
|
847
|
+
it's own file, so if you don't use it, you don't pay the memory penalty for loading
|
848
|
+
it.
|
849
|
+
|
850
|
+
require 'forme/bs3'
|
851
|
+
Forme.default_config = :bs3
|
852
|
+
|
845
853
|
= Other Similar Projects
|
846
854
|
|
847
855
|
All of these have external dependencies:
|
data/Rakefile
CHANGED
@@ -48,45 +48,16 @@ end
|
|
48
48
|
|
49
49
|
### Specs
|
50
50
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
spec_class = Spec::Rake::SpecTask
|
62
|
-
spec_files_meth = :spec_files=
|
63
|
-
end
|
64
|
-
|
65
|
-
spec = lambda do |name, files, d|
|
66
|
-
lib_dir = File.join(File.dirname(File.expand_path(__FILE__)), 'lib')
|
67
|
-
ENV['RUBYLIB'] ? (ENV['RUBYLIB'] += ":#{lib_dir}") : (ENV['RUBYLIB'] = lib_dir)
|
68
|
-
desc d
|
69
|
-
spec_class.new(name) do |t|
|
70
|
-
t.send spec_files_meth, files
|
71
|
-
t.spec_opts = ENV["#{NAME.upcase}_SPEC_OPTS"].split if ENV["#{NAME.upcase}_SPEC_OPTS"]
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
spec_with_cov = lambda do |name, files, d|
|
76
|
-
spec.call(name, files, d)
|
77
|
-
desc "#{d} with coverage"
|
78
|
-
task "#{name}_cov" do
|
79
|
-
ENV['COVERAGE'] = '1'
|
80
|
-
Rake::Task[name].invoke
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
task :default => [:spec]
|
85
|
-
spec_with_cov.call("spec", Dir["spec/*_spec.rb"], "Run specs")
|
86
|
-
rescue LoadError
|
87
|
-
task :default do
|
88
|
-
puts "Must install rspec to run the default task (which runs specs)"
|
89
|
-
end
|
51
|
+
desc "Run specs"
|
52
|
+
task :spec do
|
53
|
+
sh "#{FileUtils::RUBY} -rubygems -I lib -e 'ARGV.each{|f| require f}' ./spec/*_spec.rb"
|
54
|
+
end
|
55
|
+
task :default => :spec
|
56
|
+
|
57
|
+
desc "Run specs with coverage"
|
58
|
+
task :spec do
|
59
|
+
ENV['COVERAGE'] = '1'
|
60
|
+
Rake::Task['spec'].invoke
|
90
61
|
end
|
91
62
|
|
92
63
|
### Other
|
data/lib/forme.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
1
3
|
require 'date'
|
2
4
|
require 'bigdecimal'
|
3
5
|
|
@@ -18,7 +20,7 @@ module Forme
|
|
18
20
|
end
|
19
21
|
|
20
22
|
# Array of all supported transformer types.
|
21
|
-
TRANSFORMER_TYPES = [:formatter, :serializer, :wrapper, :error_handler, :helper, :labeler, :inputs_wrapper]
|
23
|
+
TRANSFORMER_TYPES = [:formatter, :serializer, :wrapper, :error_handler, :helper, :labeler, :inputs_wrapper, :tag_wrapper, :set_wrapper]
|
22
24
|
|
23
25
|
# Transformer symbols shared by wrapper and inputs_wrapper
|
24
26
|
SHARED_WRAPPERS = [:tr, :table, :ol, :fieldset_ol]
|
@@ -37,6 +39,7 @@ module Forme
|
|
37
39
|
CONFIGURATIONS[:default][t] = :default
|
38
40
|
TRANSFORMERS[t] = {}
|
39
41
|
end
|
42
|
+
CONFIGURATIONS[:default].delete(:set_wrapper)
|
40
43
|
|
41
44
|
# Register a new transformer with this library. Arguments:
|
42
45
|
# +type+ :: Transformer type symbol
|
@@ -91,7 +94,7 @@ module Forme
|
|
91
94
|
case type
|
92
95
|
when :inputs_wrapper
|
93
96
|
yield
|
94
|
-
when :labeler, :error_handler, :wrapper, :helper
|
97
|
+
when :labeler, :error_handler, :wrapper, :helper, :set_wrapper, :tag_wrapper
|
95
98
|
args.first
|
96
99
|
else
|
97
100
|
raise Error, "No matching #{type}: #{trans_name.inspect}"
|
@@ -107,9 +110,10 @@ module Forme
|
|
107
110
|
# default transformer for the receiver.
|
108
111
|
# +nil+ :: Assume the default transformer for this receiver.
|
109
112
|
# otherwise :: return +trans+ directly if it responds to +call+, and raise an +Error+ if not.
|
110
|
-
def self.transformer(type, trans, default_opts)
|
113
|
+
def self.transformer(type, trans, default_opts=nil)
|
111
114
|
case trans
|
112
115
|
when Symbol
|
116
|
+
type = :wrapper if type == :set_wrapper || type == :tag_wrapper
|
113
117
|
TRANSFORMERS[type][trans] || raise(Error, "invalid #{type}: #{trans.inspect} (valid #{type}s: #{TRANSFORMERS[type].keys.map(&:inspect).join(', ')})")
|
114
118
|
when Hash
|
115
119
|
if trans.has_key?(type)
|
@@ -120,7 +124,7 @@ module Forme
|
|
120
124
|
transformer(type, nil, default_opts)
|
121
125
|
end
|
122
126
|
when nil
|
123
|
-
transformer(type, default_opts[type]
|
127
|
+
transformer(type, default_opts[type]) if default_opts
|
124
128
|
else
|
125
129
|
if trans.respond_to?(:call)
|
126
130
|
trans
|
data/lib/forme/bs3.rb
ADDED
@@ -0,0 +1,387 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
module Forme
|
4
|
+
register_config(:bs3, :formatter=>:bs3, :inputs_wrapper=>:bs3, :wrapper=>:bs3, :error_handler=>:bs3, :serializer=>:bs3, :labeler=>:bs3, :tag_wrapper=>:bs3, :set_wrapper=>:div)
|
5
|
+
|
6
|
+
# BS3 Boostrap formatted error handler which adds a span tag
|
7
|
+
# with "help-block with-errors" classes for the error message.
|
8
|
+
#
|
9
|
+
# Uses [github.com/1000hz/bootstrap-validator] formatting.
|
10
|
+
#
|
11
|
+
# Note! The default "error" class on the input is being removed.
|
12
|
+
#
|
13
|
+
# Registered as :bs3.
|
14
|
+
class ErrorHandler::Bootstrap3 < ErrorHandler
|
15
|
+
Forme.register_transformer(:error_handler, :bs3, new)
|
16
|
+
|
17
|
+
# Return tag with error message span tag after it.
|
18
|
+
def call(tag, input)
|
19
|
+
if tag.is_a?(Tag)
|
20
|
+
tag.attr[:class] = tag.attr[:class].to_s.gsub(/\s*error\s*/,'')
|
21
|
+
tag.attr.delete(:class) if tag.attr[:class].to_s == ''
|
22
|
+
end
|
23
|
+
attr = input.opts[:error_attr]
|
24
|
+
attr = attr ? attr.dup : {}
|
25
|
+
Forme.attr_classes(attr, 'help-block with-errors')
|
26
|
+
return [tag] if input.opts[:skip_error_message]
|
27
|
+
|
28
|
+
case input.type
|
29
|
+
when :submit, :reset
|
30
|
+
[tag]
|
31
|
+
when :textarea
|
32
|
+
input.opts[:wrapper] = :bs3
|
33
|
+
if input.opts[:wrapper_attr]
|
34
|
+
Forme.attr_classes(input.opts[:wrapper_attr], 'has-error')
|
35
|
+
else
|
36
|
+
input.opts[:wrapper_attr] = { :class => 'has-error' }
|
37
|
+
end
|
38
|
+
[ tag, input.tag(:span, attr, input.opts[:error]) ]
|
39
|
+
|
40
|
+
when :select
|
41
|
+
input.opts[:wrapper] = :bs3
|
42
|
+
if input.opts[:wrapper_attr]
|
43
|
+
Forme.attr_classes(input.opts[:wrapper_attr], 'has-error')
|
44
|
+
else
|
45
|
+
input.opts[:wrapper_attr] = { :class => 'has-error' }
|
46
|
+
end
|
47
|
+
[ tag, input.tag(:span, attr, input.opts[:error]) ]
|
48
|
+
|
49
|
+
when :checkbox, :radio
|
50
|
+
|
51
|
+
input.opts[:wrapper] = :div
|
52
|
+
if input.opts[:wrapper_attr]
|
53
|
+
Forme.attr_classes(input.opts[:wrapper_attr], 'has-error')
|
54
|
+
else
|
55
|
+
input.opts[:wrapper_attr] = { :class => 'has-error' }
|
56
|
+
end
|
57
|
+
|
58
|
+
[
|
59
|
+
input.tag(:div, { :class=> input.type.to_s }, [tag] ),
|
60
|
+
input.tag(:span, attr, input.opts[:error])
|
61
|
+
]
|
62
|
+
else
|
63
|
+
if input.opts[:wrapper_attr]
|
64
|
+
Forme.attr_classes(input.opts[:wrapper_attr], 'has-error')
|
65
|
+
else
|
66
|
+
input.opts[:wrapper_attr] = { :class => 'has-error' }
|
67
|
+
end
|
68
|
+
[tag, input.tag(:span, attr, input.opts[:error])]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
class Formatter::Bs3 < Formatter
|
74
|
+
Forme.register_transformer(:formatter, :bs3, self)
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
# Copied to remove .error from class attrs
|
79
|
+
def normalize_options
|
80
|
+
copy_options_to_attributes(ATTRIBUTE_OPTIONS)
|
81
|
+
copy_boolean_options_to_attributes(ATTRIBUTE_BOOLEAN_OPTIONS)
|
82
|
+
handle_key_option
|
83
|
+
|
84
|
+
Forme.attr_classes(@attr, @opts[:class]) if @opts.has_key?(:class)
|
85
|
+
# Forme.attr_classes(@attr, 'error') if @opts[:error]
|
86
|
+
|
87
|
+
if data = opts[:data]
|
88
|
+
data.each do |k, v|
|
89
|
+
sym = :"data-#{k}"
|
90
|
+
@attr[sym] = v unless @attr.has_key?(sym)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def set_label
|
96
|
+
form._tag(:span, {:class=>'set-label'}, @opts[:set_label])
|
97
|
+
end
|
98
|
+
|
99
|
+
def _add_set_error(tags)
|
100
|
+
tags << input.tag(:span, {:class=>'help-block with-errors'}, @opts[:set_error])
|
101
|
+
end
|
102
|
+
|
103
|
+
def format_radioset
|
104
|
+
@opts[:wrapper_attr] ||= {}
|
105
|
+
klasses = 'radioset'
|
106
|
+
klasses = @opts[:error] || @opts[:set_error] ? "#{klasses} has-error" : klasses
|
107
|
+
Forme.attr_classes(@opts[:wrapper_attr], klasses)
|
108
|
+
super
|
109
|
+
end
|
110
|
+
|
111
|
+
def format_checkboxset
|
112
|
+
@opts[:wrapper_attr] ||= {}
|
113
|
+
klasses = 'checkboxset'
|
114
|
+
klasses = @opts[:error] || @opts[:set_error] ? "#{klasses} has-error" : klasses
|
115
|
+
Forme.attr_classes(@opts[:wrapper_attr], klasses)
|
116
|
+
super
|
117
|
+
end
|
118
|
+
|
119
|
+
def _format_set(type, tag_attrs={})
|
120
|
+
raise Error, "can't have radioset with no options" unless @opts[:optgroups] || @opts[:options]
|
121
|
+
key = @opts[:key]
|
122
|
+
name = @opts[:name]
|
123
|
+
id = @opts[:id]
|
124
|
+
if @opts[:error]
|
125
|
+
@opts[:set_error] = @opts.delete(:error)
|
126
|
+
end
|
127
|
+
if @opts[:label]
|
128
|
+
@opts[:set_label] = @opts.delete(:label)
|
129
|
+
end
|
130
|
+
|
131
|
+
tag_wrapper = Forme.transformer(:tag_wrapper, @opts.delete(:tag_wrapper), @input.form_opts) || :default
|
132
|
+
wrapper = @opts.fetch(:wrapper){@opts[:wrapper] = @input.form_opts[:set_wrapper] || @input.form_opts[:wrapper]}
|
133
|
+
wrapper = Forme.transformer(:wrapper, wrapper)
|
134
|
+
|
135
|
+
tags = process_select_optgroups(:_format_set_optgroup) do |label, value, sel, attrs|
|
136
|
+
value ||= label
|
137
|
+
r_opts = attrs.merge(tag_attrs).merge(:label=>label||value, :label_attr=>{:class=>:option}, :wrapper=>tag_wrapper)
|
138
|
+
r_opts[:value] ||= value if value
|
139
|
+
r_opts[:checked] ||= :checked if sel
|
140
|
+
|
141
|
+
if name
|
142
|
+
r_opts[:name] ||= name
|
143
|
+
end
|
144
|
+
if id
|
145
|
+
r_opts[:id] ||= "#{id}_#{value}"
|
146
|
+
end
|
147
|
+
if key
|
148
|
+
r_opts[:key] ||= key
|
149
|
+
r_opts[:key_id] ||= value
|
150
|
+
end
|
151
|
+
|
152
|
+
form._input(type, r_opts)
|
153
|
+
end
|
154
|
+
|
155
|
+
if @opts[:set_error]
|
156
|
+
_add_set_error(tags)
|
157
|
+
end
|
158
|
+
|
159
|
+
tags.unshift(form._tag(:label, {}, @opts[:set_label])) if @opts[:set_label]
|
160
|
+
|
161
|
+
tags
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
# Formatter that adds "readonly" for most input types,
|
167
|
+
# and disables select/radio/checkbox inputs.
|
168
|
+
#
|
169
|
+
# Registered as :bs3_readonly.
|
170
|
+
class Formatter::Bs3ReadOnly < Formatter
|
171
|
+
Forme.register_transformer(:formatter, :bs3_readonly, self)
|
172
|
+
|
173
|
+
private
|
174
|
+
|
175
|
+
# Disabled checkbox inputs.
|
176
|
+
def format_checkbox
|
177
|
+
@attr[:disabled] = :disabled
|
178
|
+
super
|
179
|
+
end
|
180
|
+
|
181
|
+
# Use a span with text instead of an input field.
|
182
|
+
def _format_input(type)
|
183
|
+
@attr[:readonly] = :readonly
|
184
|
+
super
|
185
|
+
end
|
186
|
+
|
187
|
+
# Disabled radio button inputs.
|
188
|
+
def format_radio
|
189
|
+
@attr[:disabled] = :disabled
|
190
|
+
super
|
191
|
+
end
|
192
|
+
|
193
|
+
# Use a span with text of the selected values instead of a select box.
|
194
|
+
def format_select
|
195
|
+
@attr[:disabled] = :disabled
|
196
|
+
super
|
197
|
+
end
|
198
|
+
|
199
|
+
# Use a span with text instead of a text area.
|
200
|
+
def format_textarea
|
201
|
+
@attr[:readonly] = :readonly
|
202
|
+
super
|
203
|
+
end
|
204
|
+
end
|
205
|
+
|
206
|
+
# Use a <fieldset> tag to wrap the inputs.
|
207
|
+
#
|
208
|
+
# Registered as :bs3.
|
209
|
+
class InputsWrapper::Bootstrap3
|
210
|
+
Forme.register_transformer(:inputs_wrapper, :bs3, new)
|
211
|
+
|
212
|
+
def call(form, opts, &block)
|
213
|
+
attr = opts[:attr] ? opts[:attr].dup : {}
|
214
|
+
Forme.attr_classes(attr, 'inputs')
|
215
|
+
if legend = opts[:legend]
|
216
|
+
form.tag(:fieldset, attr) do
|
217
|
+
form.emit(form.tag(:legend, opts[:legend_attr], legend))
|
218
|
+
yield
|
219
|
+
end
|
220
|
+
else
|
221
|
+
form.tag(:fieldset, attr, &Proc.new)
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
226
|
+
# Use a <table class="table"> tag to wrap the inputs.
|
227
|
+
#
|
228
|
+
# Registered as :bs3_table.
|
229
|
+
class InputsWrapper::Bs3Table
|
230
|
+
Forme.register_transformer(:inputs_wrapper, :bs3_table, new)
|
231
|
+
|
232
|
+
# Wrap the inputs in a <table> tag.
|
233
|
+
def call(form, opts, &block)
|
234
|
+
attr = opts[:attr] ? opts[:attr].dup : { :class=>'table table-bordered'}
|
235
|
+
form.tag(:table, attr) do
|
236
|
+
if legend = opts[:legend]
|
237
|
+
form.emit(form.tag(:caption, opts[:legend_attr], legend))
|
238
|
+
end
|
239
|
+
|
240
|
+
if (labels = opts[:labels]) && !labels.empty?
|
241
|
+
form.emit(form.tag(:tr, {}, labels.map{|l| form._tag(:th, {}, l)}))
|
242
|
+
end
|
243
|
+
|
244
|
+
yield
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
# Labeler that creates BS3 label tags referencing the the given tag's id
|
250
|
+
# using a +for+ attribute. Requires that all tags with labels have +id+ fields.
|
251
|
+
#
|
252
|
+
# Registered as :bs3.
|
253
|
+
class Labeler::Bootstrap3
|
254
|
+
Forme.register_transformer(:labeler, :bs3, new)
|
255
|
+
|
256
|
+
# Return an array with a label tag as the first entry and +tag+ as
|
257
|
+
# a second entry. If the +input+ has a :label_for option, use that,
|
258
|
+
# otherwise use the input's :id option. If neither the :id or
|
259
|
+
# :label_for option is used, the label created will not be
|
260
|
+
# associated with an input.
|
261
|
+
def call(tag, input)
|
262
|
+
unless id = input.opts[:id]
|
263
|
+
if key = input.opts[:key]
|
264
|
+
namespaces = input.form_opts[:namespace]
|
265
|
+
id = "#{namespaces.join('_')}#{'_' unless namespaces.empty?}#{key}"
|
266
|
+
if key_id = input.opts[:key_id]
|
267
|
+
id += "_#{key_id.to_s}"
|
268
|
+
end
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
label_attr = input.opts[:label_attr]
|
273
|
+
label_attr = label_attr ? label_attr.dup : {}
|
274
|
+
|
275
|
+
label_attr[:for] = label_attr[:for] === false ? nil : input.opts.fetch(:label_for, id)
|
276
|
+
label = input.opts[:label]
|
277
|
+
lpos = input.opts[:label_position] || ([:radio, :checkbox].include?(input.type) ? :after : :before)
|
278
|
+
|
279
|
+
case input.type
|
280
|
+
when :checkbox, :radio
|
281
|
+
label = if lpos == :before
|
282
|
+
[label, ' ', tag]
|
283
|
+
else
|
284
|
+
[tag, ' ', label]
|
285
|
+
end
|
286
|
+
input.tag(:label, label_attr, label)
|
287
|
+
when :submit
|
288
|
+
[tag]
|
289
|
+
else
|
290
|
+
label = input.tag(:label, label_attr, [input.opts[:label]])
|
291
|
+
if lpos == :after
|
292
|
+
[tag, ' ', label]
|
293
|
+
else
|
294
|
+
[label, ' ', tag]
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
# Wraps inputs with <div class="form-group">
|
301
|
+
class Wrapper::Bootstrap3 < Wrapper
|
302
|
+
# Wrap the input in the tag of the given type.
|
303
|
+
|
304
|
+
def call(tag, input)
|
305
|
+
attr = input.opts[:wrapper_attr] ? input.opts[:wrapper_attr].dup : { }
|
306
|
+
klass = attr[:class] ? attr[:class].split(' ').unshift('form-group').uniq : ['form-group']
|
307
|
+
|
308
|
+
case input.type
|
309
|
+
when :submit, :reset
|
310
|
+
klass.delete('form-group')
|
311
|
+
attr[:class] = klass.sort.uniq.join(' ').strip
|
312
|
+
attr.delete(:class) if attr[:class].empty?
|
313
|
+
[tag]
|
314
|
+
when :radio, :checkbox
|
315
|
+
klass.delete('form-group')
|
316
|
+
klass.unshift( input.type.to_s )
|
317
|
+
attr[:class] = klass.sort.uniq.join(' ').strip
|
318
|
+
[input.tag(:div, attr, tag)]
|
319
|
+
when :hidden
|
320
|
+
super
|
321
|
+
else
|
322
|
+
attr[:class] = klass.sort.uniq.join(' ').strip
|
323
|
+
[input.tag(:div, attr, [tag])]
|
324
|
+
end
|
325
|
+
|
326
|
+
end
|
327
|
+
|
328
|
+
Forme.register_transformer(:wrapper, :bs3, new)
|
329
|
+
end
|
330
|
+
|
331
|
+
# Serializer class that converts tags to BS3 bootstrap tags.
|
332
|
+
#
|
333
|
+
# Registered at :bs3.
|
334
|
+
class Serializer::Bootstrap3 < Serializer
|
335
|
+
Forme.register_transformer(:serializer, :bs3, new)
|
336
|
+
|
337
|
+
def call(tag)
|
338
|
+
# All textual <input>, <textarea>, and <select> elements with .form-control
|
339
|
+
case tag
|
340
|
+
when Tag
|
341
|
+
case tag.type
|
342
|
+
when :input
|
343
|
+
# default to <input type="text"...> if not set
|
344
|
+
tag.attr[:type] = :text if tag.attr[:type].nil?
|
345
|
+
|
346
|
+
case tag.attr[:type].to_sym
|
347
|
+
when :checkbox, :radio, :hidden
|
348
|
+
# .form-control class causes rendering problems, so remove if found
|
349
|
+
tag.attr[:class].gsub!(/\s*form-control\s*/,'') if tag.attr[:class]
|
350
|
+
tag.attr[:class] = nil if tag.attr[:class] && tag.attr[:class].empty?
|
351
|
+
|
352
|
+
when :file
|
353
|
+
tag.attr[:class] = nil unless tag.attr[:class] && tag.attr[:class].strip != ''
|
354
|
+
|
355
|
+
when :submit, :reset
|
356
|
+
klass = ['btn', 'btn-default']
|
357
|
+
if tag.attr[:class] && tag.attr[:class].strip != ''
|
358
|
+
tag.attr[:class].split(' ').each { |c| klass.push c }
|
359
|
+
end
|
360
|
+
tag.attr[:class] = klass.uniq
|
361
|
+
['btn-primary','btn-success', 'btn-info', 'btn-warning','btn-danger',
|
362
|
+
'btn-outline','btn-link'
|
363
|
+
].each do |k|
|
364
|
+
tag.attr[:class].delete('btn-default') if tag.attr[:class].include?(k)
|
365
|
+
end
|
366
|
+
tag.attr[:class].join(' ')
|
367
|
+
|
368
|
+
else
|
369
|
+
klass = tag.attr[:class] ? "form-control #{tag.attr[:class].to_s}" : ''
|
370
|
+
tag.attr[:class] = "form-control #{klass.gsub(/\s*form-control\s*/,'')}".strip
|
371
|
+
end
|
372
|
+
|
373
|
+
return "<#{tag.type}#{attr_html(tag.attr)}/>"
|
374
|
+
|
375
|
+
when :textarea, :select
|
376
|
+
klass = tag.attr[:class] ? "form-control #{tag.attr[:class].to_s}" : ''
|
377
|
+
tag.attr[:class] = "form-control #{klass.gsub(/\s*form-control\s*/,'')}".strip
|
378
|
+
return "#{serialize_open(tag)}#{call(tag.children)}#{serialize_close(tag)}"
|
379
|
+
else
|
380
|
+
super
|
381
|
+
end
|
382
|
+
else
|
383
|
+
super
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|