forme 1.3.0 → 1.4.0
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.
- 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
|