forme 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 81ec9ce1b755276902340c709aa7addbd73b6b80252fcf0cbaac236098c62ac1
4
- data.tar.gz: 35e17dd67304585e027dca62d6181eff1d30a0fcc7aa128879c9d8d54fd78f55
3
+ metadata.gz: 13efa41ba160d80619b779cda0ad94581b37695e9fb8caedc696d26c91c92d87
4
+ data.tar.gz: 6558cf408b4573b3822c566c8e48b856de47009c6293831e0cdcc3fed7f4ebc2
5
5
  SHA512:
6
- metadata.gz: 16f13f8ba1ba9fdae6b26b80f0b2e1fa2ce9ef520a3a29936597771f9f5424d2b49afc6debdb173e2aee6d672a471bb1aa8cee8e73d82ce7f5d30a1fc8ced578
7
- data.tar.gz: 89d6f1ef00b4a9b28b55d9a6f42fb5250d10eaec78a7921d6220451f2fe7ea5c4ecea7907bcef5567e5e1dbcb60ccf2ec52195a6a4bfaacf2c4c353c785e9049
6
+ metadata.gz: 42d8dd057d905a8cb44610c9b3c20c9d055d0f0495b8ec94b020a637d0b10f5c1919ebc697e0b4f8970a29fcb511661cc56d622dc1d91eb8f347ab62fe36d7c4
7
+ data.tar.gz: 10beb8c2109850be4c1fb5d0c46d13218269c8b65f604cda930024eab8ff3838f32ab70256696d93f5c17f3e011d5dad0450511051a6abda40e75e37c1bbf2eb
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ === 2.3.0 (2022-11-15)
2
+
3
+ * Add forme/bs5 for Bootstrap 5 support (janko) (#106)
4
+
5
+ * Raise exception if attempting to load Roda forme_set plugin without setting :secret option (janko) (#104)
6
+
1
7
  === 2.2.0 (2022-06-28)
2
8
 
3
9
  * Use inputmode and pattern attributes instead of type=number for integer fields in the Sequel forme plugin (jeremyevans)
data/README.rdoc CHANGED
@@ -1264,12 +1264,17 @@ You can mark a configuration as the default using:
1264
1264
 
1265
1265
  Forme.default_config = :mine
1266
1266
 
1267
- === Bootstrap 3 Support
1267
+ === Bootstrap Support
1268
1268
 
1269
- Forme ships with support for Bootstrap 3-4 HTML formatting. This support is shipped in
1269
+ Forme ships with support for Bootstrap 5 HTML formatting. This support is shipped in
1270
1270
  it's own file, so if you don't use it, you don't pay the memory penalty for loading
1271
1271
  it.
1272
1272
 
1273
+ require 'forme/bs5'
1274
+ Forme.default_config = :bs5
1275
+
1276
+ There is also support for Bootstrap versions 3-4:
1277
+
1273
1278
  require 'forme/bs3'
1274
1279
  Forme.default_config = :bs3
1275
1280
 
data/lib/forme/bs5.rb ADDED
@@ -0,0 +1,213 @@
1
+ # frozen-string-literal: true
2
+
3
+ module Forme
4
+ register_config(:bs5, formatter: :bs5, wrapper: :bs5, error_handler: :bs5, serializer: :bs5, labeler: :bs5, helper: :bs5, tag_wrapper: :bs5, set_wrapper: :div)
5
+
6
+ # Update the <tt>:class</tt> entry in the +attr+ hash with the given +classes+,
7
+ # adding the classes before any existing classes.
8
+ def self.attr_classes_after(attr, *classes)
9
+ attr[:class] = merge_classes(*classes, attr[:class])
10
+ end
11
+
12
+ class ErrorHandler::Bootstrap5
13
+ Forme.register_transformer(:error_handler, :bs5, new)
14
+
15
+ def call(tags, input)
16
+ attr = input.opts[:error_attr]
17
+ attr = attr ? attr.dup : {}
18
+
19
+ unless attr[:class] && attr[:class].include?("invalid-tooltip")
20
+ Forme.attr_classes(attr, "invalid-feedback")
21
+ end
22
+
23
+ attr[:id] ||= input.opts[:error_id]
24
+
25
+ [tags, input.tag(:div, attr, input.opts[:error])]
26
+ end
27
+ end
28
+
29
+ class Formatter::Bootstrap5 < Formatter
30
+ Forme.register_transformer(:formatter, :bs5, self)
31
+
32
+ private
33
+
34
+ def normalize_options
35
+ super
36
+
37
+ if @opts[:error]
38
+ # remove "error" class
39
+ @attr[:class] = @attr[:class].to_s.sub(/\s*error$/,'')
40
+ @attr.delete(:class) if @attr[:class].to_s == ''
41
+
42
+ Forme.attr_classes(@attr, "is-invalid")
43
+ end
44
+
45
+ if @opts[:help]
46
+ if @opts[:helper_attr] && @opts[:helper_attr][:id]
47
+ @attr["aria-describedby"] ||= @opts[:helper_attr][:id]
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ # Formatter that adds "readonly" for most input types,
54
+ # and disables select/radio/checkbox inputs.
55
+ #
56
+ # Registered as :bs5_readonly.
57
+ class Formatter::Bs5ReadOnly < Formatter
58
+ Forme.register_transformer(:formatter, :bs5_readonly, self)
59
+
60
+ private
61
+
62
+ # Disabled checkbox inputs.
63
+ def format_checkbox
64
+ @attr[:disabled] = :disabled
65
+ super
66
+ end
67
+
68
+ # Use a span with text instead of an input field.
69
+ def _format_input(type)
70
+ @attr[:readonly] = :readonly
71
+ super
72
+ end
73
+
74
+ # Disabled radio button inputs.
75
+ def format_radio
76
+ @attr[:disabled] = :disabled
77
+ super
78
+ end
79
+
80
+ # Use a span with text of the selected values instead of a select box.
81
+ def format_select
82
+ @attr[:disabled] = :disabled
83
+ super
84
+ end
85
+
86
+ # Use a span with text instead of a text area.
87
+ def format_textarea
88
+ @attr[:readonly] = :readonly
89
+ super
90
+ end
91
+ end
92
+
93
+ # Use a <table class="table"> tag to wrap the inputs.
94
+ #
95
+ # Registered as :bs5_table.
96
+ class InputsWrapper::Bs5Table
97
+ Forme.register_transformer(:inputs_wrapper, :bs5_table, new)
98
+
99
+ # Wrap the inputs in a <table> tag.
100
+ def call(form, opts, &block)
101
+ attr = opts[:attr] ? opts[:attr].dup : { :class=>'table table-bordered'}
102
+ form.tag(:table, attr) do
103
+ if legend = opts[:legend]
104
+ form.tag(:caption, opts[:legend_attr], legend)
105
+ end
106
+
107
+ if (labels = opts[:labels]) && !labels.empty?
108
+ form.tag(:tr, {}, labels.map{|l| form._tag(:th, {}, l)})
109
+ end
110
+
111
+ yield
112
+ end
113
+ end
114
+ end
115
+
116
+ class Labeler::Bootstrap5 < Labeler::Explicit
117
+ Forme.register_transformer(:labeler, :bs5, new)
118
+
119
+ def call(tag, input)
120
+ floating_label = (input.opts[:wrapper_attr] || {})[:class].to_s.include?("form-floating")
121
+ input.opts[:label_position] ||= :after if floating_label
122
+
123
+ tags = super
124
+ return tags if floating_label
125
+
126
+ attr = tags.find { |tag| tag.is_a?(Tag) && tag.type == :label }.attr
127
+
128
+ label_class = case input.type
129
+ when :radio, :checkbox
130
+ "form-check-label"
131
+ else
132
+ "form-label"
133
+ end
134
+ Forme.attr_classes_after(attr, label_class)
135
+
136
+ tags
137
+ end
138
+ end
139
+
140
+ class Helper::Bootstrap5
141
+ Forme.register_transformer(:helper, :bs5, new)
142
+
143
+ def call(tag, input)
144
+ attr = input.opts[:helper_attr]
145
+ attr = attr ? attr.dup : {}
146
+ Forme.attr_classes(attr, 'form-text')
147
+ [tag, input.tag(:div, attr, input.opts[:help])]
148
+ end
149
+ end
150
+
151
+ class Wrapper::Bootstrap5 < Wrapper
152
+ Forme.register_transformer(:wrapper, :bs5, new)
153
+
154
+ def call(tag, input)
155
+ attr = input.opts[:wrapper_attr] ? input.opts[:wrapper_attr].dup : { }
156
+
157
+ case input.type
158
+ when :submit, :reset, :hidden
159
+ super
160
+ when :radio, :checkbox
161
+ Forme.attr_classes_after(attr, "form-check")
162
+ input.tag(:div, attr, super)
163
+ else
164
+ input.tag(:div, attr, super)
165
+ end
166
+ end
167
+ end
168
+
169
+ class Serializer::Bootstrap5 < Serializer
170
+ Forme.register_transformer(:serializer, :bs5, new)
171
+
172
+ BUTTON_STYLES = %w[
173
+ btn-primary btn-secondary btn-success btn-danger btn-warning btn-info btn-light btn-dark btn-link
174
+ btn-outline-primary btn-outline-secondary btn-outline-success btn-outline-danger btn-outline-warning btn-outline-info btn-outline-light btn-outline-dark
175
+ ].freeze
176
+
177
+ def call(tag)
178
+ return super unless tag.is_a?(Tag)
179
+
180
+ attr_class = case tag.type
181
+ when :input
182
+ # default to <input type="text"...> if not set
183
+ tag.attr[:type] = :text if tag.attr[:type].nil?
184
+
185
+ case tag.attr[:type].to_sym
186
+ when :checkbox, :radio
187
+ "form-check-input"
188
+ when :range
189
+ "form-range"
190
+ when :color
191
+ %w"form-control form-control-color"
192
+ when :submit, :reset
193
+ classes = ["btn"]
194
+ classes << "btn-primary" if (tag.attr[:class].to_s.split(" ") & BUTTON_STYLES).empty?
195
+ classes
196
+ when :hidden
197
+ # nothing
198
+ else
199
+ unless tag.attr[:class] && tag.attr[:class].include?("form-control-plaintext")
200
+ "form-control"
201
+ end
202
+ end
203
+ when :textarea
204
+ "form-control"
205
+ when :select
206
+ "form-select"
207
+ end
208
+ Forme.attr_classes_after(tag.attr, *attr_class) if attr_class
209
+
210
+ super
211
+ end
212
+ end
213
+ end
data/lib/forme/version.rb CHANGED
@@ -6,7 +6,7 @@ module Forme
6
6
  MAJOR = 2
7
7
 
8
8
  # The minor version of Forme, updated for new feature releases of Forme.
9
- MINOR = 2
9
+ MINOR = 3
10
10
 
11
11
  # The patch version of Forme, updated only for bug fixes from the last
12
12
  # feature release.
data/lib/forme.rb CHANGED
@@ -96,7 +96,8 @@ module Forme
96
96
  Form.form(*a, &block)
97
97
  end
98
98
 
99
- # Update the <tt>:class</tt> entry in the +attr+ hash with the given +classes+.
99
+ # Update the <tt>:class</tt> entry in the +attr+ hash with the given +classes+,
100
+ # adding the classes after any existing classes.
100
101
  def self.attr_classes(attr, *classes)
101
102
  attr[:class] = merge_classes(attr[:class], *classes)
102
103
  end
@@ -13,7 +13,9 @@ class Roda
13
13
 
14
14
  # Set the HMAC secret.
15
15
  def self.configure(app, opts = OPTS, &block)
16
- app.opts[:forme_set_hmac_secret] = opts[:secret] || app.opts[:forme_set_hmac_secret]
16
+ unless app.opts[:forme_set_hmac_secret] = opts[:secret] || app.opts[:forme_set_hmac_secret]
17
+ raise RodaError, "must provide :secret option to forme_set plugin"
18
+ end
17
19
 
18
20
  if block
19
21
  app.send(:define_method, :_forme_set_handle_error, &block)
@@ -27,8 +29,8 @@ class Roda
27
29
 
28
30
  # Map of error types to error messages
29
31
  ERROR_MESSAGES = {
30
- :missing_data=>"_forme_set_data parameter not submitted",
31
- :missing_hmac=>"_forme_set_data_hmac parameter not submitted",
32
+ :missing_data=>"_forme_set_data parameter not submitted, make sure the forme_set Sequel plugin is loaded",
33
+ :missing_hmac=>"_forme_set_data_hmac parameter not submitted, make sure the forme_set Sequel plugin is loaded",
32
34
  :hmac_mismatch=>"_forme_set_data_hmac does not match _forme_set_data",
33
35
  :csrf_mismatch=>"_forme_set_data CSRF token does not match submitted CSRF token",
34
36
  :missing_namespace=>"no content in expected namespace"
@@ -1,5 +1,7 @@
1
1
  # frozen-string-literal: true
2
2
 
3
+ require_relative '../../forme'
4
+
3
5
  module Sequel # :nodoc:
4
6
  module Plugins # :nodoc:
5
7
  # The forme_set plugin makes the model instance keep track of which form
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forme
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.0
4
+ version: 2.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jeremy Evans
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-06-28 00:00:00.000000000 Z
11
+ date: 2022-11-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: minitest
@@ -157,6 +157,7 @@ description: |
157
157
  2) Have a simple API
158
158
  3) Support forms both with and without related objects
159
159
  4) Allow compiling down to different types of output
160
+ 5) Integrate easily into web frameworks
160
161
  email: code@jeremyevans.net
161
162
  executables: []
162
163
  extensions: []
@@ -168,9 +169,9 @@ files:
168
169
  - CHANGELOG
169
170
  - MIT-LICENSE
170
171
  - README.rdoc
171
- - Rakefile
172
172
  - lib/forme.rb
173
173
  - lib/forme/bs3.rb
174
+ - lib/forme/bs5.rb
174
175
  - lib/forme/erb.rb
175
176
  - lib/forme/form.rb
176
177
  - lib/forme/input.rb
@@ -193,24 +194,6 @@ files:
193
194
  - lib/sequel/plugins/forme.rb
194
195
  - lib/sequel/plugins/forme_i18n.rb
195
196
  - lib/sequel/plugins/forme_set.rb
196
- - spec/all.rb
197
- - spec/bs3_reference_spec.rb
198
- - spec/bs3_sequel_plugin_spec.rb
199
- - spec/bs3_spec.rb
200
- - spec/erb_helper.rb
201
- - spec/erubi_capture_helper.rb
202
- - spec/forme_coverage.rb
203
- - spec/forme_spec.rb
204
- - spec/rails_integration_spec.rb
205
- - spec/roda_integration_spec.rb
206
- - spec/sequel_helper.rb
207
- - spec/sequel_i18n_helper.rb
208
- - spec/sequel_i18n_plugin_spec.rb
209
- - spec/sequel_plugin_spec.rb
210
- - spec/sequel_set_plugin_spec.rb
211
- - spec/shared_erb_specs.rb
212
- - spec/sinatra_integration_spec.rb
213
- - spec/spec_helper.rb
214
197
  homepage: http://github.com/jeremyevans/forme
215
198
  licenses:
216
199
  - MIT
@@ -220,7 +203,7 @@ metadata:
220
203
  documentation_uri: http://forme.jeremyevans.net
221
204
  mailing_list_uri: https://github.com/jeremyevans/forme/discussions
222
205
  source_code_uri: https://github.com/jeremyevans/forme
223
- post_install_message:
206
+ post_install_message:
224
207
  rdoc_options:
225
208
  - "--quiet"
226
209
  - "--line-numbers"
@@ -243,7 +226,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
243
226
  version: '0'
244
227
  requirements: []
245
228
  rubygems_version: 3.3.7
246
- signing_key:
229
+ signing_key:
247
230
  specification_version: 4
248
231
  summary: HTML forms library
249
232
  test_files: []
data/Rakefile DELETED
@@ -1,82 +0,0 @@
1
- require "rake"
2
- require "rake/clean"
3
-
4
- NAME = 'forme'
5
- VERS = lambda do
6
- require File.expand_path("../lib/forme/version", __FILE__)
7
- Forme.version
8
- end
9
- CLEAN.include ["#{NAME}-*.gem", "rdoc", "coverage", '**/*.rbc']
10
-
11
- # Gem Packaging and Release
12
-
13
- desc "Packages #{NAME}"
14
- task :package=>[:clean] do |p|
15
- sh %{gem build #{NAME}.gemspec}
16
- end
17
-
18
- desc "Upload #{NAME} gem to rubygems"
19
- task :release=>[:package] do
20
- sh %{gem push ./#{NAME}-#{VERS.call}.gem}
21
- end
22
-
23
- ### RDoc
24
-
25
- RDOC_DEFAULT_OPTS = ["--line-numbers", "--inline-source", '--title', 'Forme']
26
-
27
- begin
28
- gem 'hanna-nouveau'
29
- RDOC_DEFAULT_OPTS.concat(['-f', 'hanna'])
30
- rescue Gem::LoadError
31
- end
32
-
33
- rdoc_task_class = begin
34
- require "rdoc/task"
35
- RDoc::Task
36
- rescue LoadError
37
- require "rake/rdoctask"
38
- Rake::RDocTask
39
- end
40
-
41
- RDOC_OPTS = RDOC_DEFAULT_OPTS + ['--main', 'README.rdoc']
42
-
43
- rdoc_task_class.new do |rdoc|
44
- rdoc.rdoc_dir = "rdoc"
45
- rdoc.options += RDOC_OPTS
46
- rdoc.rdoc_files.add %w"README.rdoc CHANGELOG MIT-LICENSE lib/**/*.rb"
47
- end
48
-
49
- ### Specs
50
-
51
- desc "Run specs"
52
- task :spec do
53
- sh "#{FileUtils::RUBY} #{'-w' if RUBY_VERSION >= '3'} spec/all.rb"
54
- end
55
- task :default => :spec
56
-
57
- desc "Run specs with coverage"
58
- task :spec_cov do
59
- ENV['COVERAGE'] = '1'
60
- sh "#{FileUtils::RUBY} spec/all.rb"
61
- end
62
-
63
- ### Other
64
-
65
- desc "Print #{NAME} version"
66
- task :version do
67
- puts VERS.call
68
- end
69
-
70
- desc "Check syntax of all .rb files"
71
- task :check_syntax do
72
- Dir['**/*.rb'].each{|file| print `#{ENV['RUBY'] || :ruby} -c #{file} | fgrep -v "Syntax OK"`}
73
- end
74
-
75
- desc "Start an IRB shell using the extension"
76
- task :irb do
77
- require 'rbconfig'
78
- ruby = ENV['RUBY'] || File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name'])
79
- irb = ENV['IRB'] || File.join(RbConfig::CONFIG['bindir'], File.basename(ruby).sub('ruby', 'irb'))
80
- sh %{#{irb} -I lib -r forme}
81
- end
82
-
data/spec/all.rb DELETED
@@ -1,2 +0,0 @@
1
- $: << 'lib'
2
- Dir.new(File.dirname(__FILE__)).each{|f| require_relative f if f.end_with?('_spec.rb')}