forme 1.8.0 → 1.12.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 +50 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +181 -9
- data/lib/forme/bs3.rb +1 -1
- data/lib/forme/form.rb +1 -1
- data/lib/forme/transformers/error_handler.rb +46 -1
- data/lib/forme/transformers/formatter.rb +57 -43
- data/lib/forme/transformers/inputs_wrapper.rb +2 -2
- data/lib/forme/transformers/labeler.rb +19 -0
- data/lib/forme/transformers/serializer.rb +1 -12
- data/lib/forme/transformers/wrapper.rb +1 -1
- data/lib/forme/version.rb +1 -1
- data/lib/forme.rb +27 -0
- data/lib/roda/plugins/forme_route_csrf.rb +15 -1
- data/lib/roda/plugins/forme_set.rb +214 -0
- data/lib/sequel/plugins/forme.rb +10 -5
- data/lib/sequel/plugins/forme_set.rb +50 -28
- data/spec/bs3_reference_spec.rb +4 -4
- data/spec/bs3_sequel_plugin_spec.rb +45 -45
- data/spec/bs3_spec.rb +1 -1
- data/spec/erb_helper.rb +2 -2
- data/spec/forme_spec.rb +75 -18
- data/spec/rails_integration_spec.rb +43 -26
- data/spec/roda_integration_spec.rb +357 -1
- data/spec/sequel_i18n_plugin_spec.rb +5 -4
- data/spec/sequel_plugin_spec.rb +61 -54
- data/spec/sequel_set_plugin_spec.rb +63 -14
- data/spec/spec_helper.rb +2 -2
- metadata +25 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f9ee8a7a7efaf9ff59c55404e6c11aba0125cdf59972366b94aa3359b06c6c1f
|
4
|
+
data.tar.gz: dd9f90ad552c827b66a3d8146f8933e5258b34ece1db5a8a9515ac9e2aeada44
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5867cc14f49ea0a2419e4f7d30a518a8886753f11e4e6af46f7565106ef54869c9bf9d5ba691f7f08ee7fab5d729bb16e8b1463939dc043d7e91aeb5e8bf1f2e
|
7
|
+
data.tar.gz: 25bf44d57e37ef994cab9a6c2bf532b4dddd209fff6b1542b21596e2cdbfa1e20a1ae04af9d2b4c0ad113f322c06fe1e8a662a4d2facd36439f2c1a45d2e4250
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,53 @@
|
|
1
|
+
=== 1.12.0 (2021-08-25)
|
2
|
+
|
3
|
+
* Make forme_set Sequel plugin handle frozen Sequel::Model instances (jeremyevans)
|
4
|
+
|
5
|
+
* Do not override an error on a field when using the Sequel plugin if :error option is already given (jeremyevans)
|
6
|
+
|
7
|
+
* Avoid error when creating label text when using Sequel input on non-Sequel form without an explicit :label option (jeremyevans)
|
8
|
+
|
9
|
+
* Make :select_options option for date/datetime selects support providing both option texts and option values using a 2 element array (jeremyevans)
|
10
|
+
|
11
|
+
=== 1.11.0 (2020-01-03)
|
12
|
+
|
13
|
+
* Add Roda forme_set plugin, using HMACed form metadata to automatically handle submitted form parameters (jeremyevans)
|
14
|
+
|
15
|
+
=== 1.10.0 (2019-05-13)
|
16
|
+
|
17
|
+
* Make readonly formatter ignore hidden inputs (jeremyevans)
|
18
|
+
|
19
|
+
* Add :select_labels for date inputs using :as=>:select to use labels for the inputs for better accessibility (jeremyevans)
|
20
|
+
|
21
|
+
* Add :after_legend error_handler for adding error message after legend when using :legend labeler (jeremyevans)
|
22
|
+
|
23
|
+
* Add aria-describedby to all inputs with errors where possible for better accessibility (jeremyevans)
|
24
|
+
|
25
|
+
* Add aria-invalid to all inputs with errors for better accessibility (jeremyevans)
|
26
|
+
|
27
|
+
* Support :fieldset wrapper and :legend labeler, can be used for accessible radioset/checkboxset (jeremyevans)
|
28
|
+
|
29
|
+
* Support :tag_label_attr option for radioset and checkbox set for label attributes for each radio/checkbox label (jeremyevans)
|
30
|
+
|
31
|
+
* Support custom :error_handler in radioset and checkboxset inputs (jeremyevans)
|
32
|
+
|
33
|
+
* Support custom :labeler in radioset and checkboxset inputs (jeremyevans)
|
34
|
+
|
35
|
+
* Avoid calling Proc.new with an implicit block, which is deprecated starting in ruby 2.7 (jeremyevans)
|
36
|
+
|
37
|
+
=== 1.9.0 (2018-11-16)
|
38
|
+
|
39
|
+
* Automatically add maxlength attributes to text and textarea inputs in the Sequel plugin based on maximum database column length (jeremyevans)
|
40
|
+
|
41
|
+
* Make forme_set Sequel plugin recognize default formatter changes set via with_opts (jeremyevans)
|
42
|
+
|
43
|
+
* Use div with nested p tags instead of spans for readonly textarea inputs (jeremyevans)
|
44
|
+
|
45
|
+
* Make readonly text input spans use the readonly-text class for easier styling (jeremyevans)
|
46
|
+
|
47
|
+
* Add Forme.h for HTML escaping, using cgi/escape if available for faster escaping (jeremyevans)
|
48
|
+
|
49
|
+
* Correctly handle :value=>false option and false option values in select, radioset, and checkboxset inputs (jeremyevans)
|
50
|
+
|
1
51
|
=== 1.8.0 (2018-06-11)
|
2
52
|
|
3
53
|
* Add support for :errors form option for setting error information for multiple inputs, similar to :values form option (adam12) (#32)
|
data/MIT-LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -101,8 +101,7 @@ by creating your own transformer and then calling the existing transformer.
|
|
101
101
|
Demo Site :: http://forme-demo.jeremyevans.net
|
102
102
|
RDoc :: http://forme.jeremyevans.net
|
103
103
|
Source :: https://github.com/jeremyevans/forme
|
104
|
-
|
105
|
-
Google Group :: https://groups.google.com/forum/#!forum/ruby-forme
|
104
|
+
Discussion Forum :: https://github.com/jeremyevans/forme/discussions
|
106
105
|
Bug Tracker :: https://github.com/jeremyevans/forme/issues
|
107
106
|
|
108
107
|
= Basic Usage
|
@@ -589,7 +588,7 @@ As you can see, you basically need to recreate the conditionals used when creati
|
|
589
588
|
the form, so that that the processing of the form submission handles only the
|
590
589
|
inputs that were displayed on the form.
|
591
590
|
|
592
|
-
=== forme_set plugin
|
591
|
+
=== forme_set Sequel plugin
|
593
592
|
|
594
593
|
The forme_set plugin is designed to make handling form submissions easier. What it does
|
595
594
|
is record the form fields that are used on the object, and then it uses those fields
|
@@ -686,6 +685,166 @@ internally). forme_parse returns a hash with the following keys:
|
|
686
685
|
It is possible to use forme_set for the values it can handle, and set other fields manually
|
687
686
|
using set_fields.
|
688
687
|
|
688
|
+
=== forme_set Roda plugin
|
689
|
+
|
690
|
+
The forme_set Roda plugin builds on the forme_set Sequel plugin and is designed to make
|
691
|
+
handling form submissions even easier. This plugin uses a hidden form input to store which
|
692
|
+
fields were used to build the form, as well as some other metadata. It uses another hidden
|
693
|
+
form input with an HMAC, so that on submission, if the HMAC matches, you can be sure that an
|
694
|
+
attacker didn't add extra fields.
|
695
|
+
|
696
|
+
There are a couple advantages to this plugin over using just the Sequel forme_set plugin.
|
697
|
+
One is that you do not need to record the form fields when processing the submission of a
|
698
|
+
form, since the information you need is included in the form submission. Another is that
|
699
|
+
calling the forme_set method is simpler, since it can determine the necessary parameters.
|
700
|
+
|
701
|
+
While you need code like this when using just the Sequel forme_set plugin:
|
702
|
+
|
703
|
+
album = Album[1]
|
704
|
+
Forme.form(album, :action=>'/foo') do |f|
|
705
|
+
f.input :name
|
706
|
+
f.input :copies_sold if album.released?
|
707
|
+
end
|
708
|
+
album.forme_set(params['album'])
|
709
|
+
|
710
|
+
when you also use the Roda forme_set plugin, you can simplify it to:
|
711
|
+
|
712
|
+
album = Album[1]
|
713
|
+
forme_set(album)
|
714
|
+
|
715
|
+
==== Validations
|
716
|
+
|
717
|
+
The Roda forme_set plugin supports and uses the same validations as the Sequel forme_set
|
718
|
+
plugin. However, the Roda plugin is more accurate because it uses the options that were
|
719
|
+
present on the form when it was originally built, instead of the options that would be
|
720
|
+
present on the form when the form was submitted. However, note that that can be a
|
721
|
+
negative if you are dynamically adding values to both the database and the form between
|
722
|
+
when the form was built and when it was submitted.
|
723
|
+
|
724
|
+
==== Usage
|
725
|
+
|
726
|
+
Because the Roda forme_set plugin includes the metadata needed to process the form in form
|
727
|
+
submissions, you don't need to rearrange code to use it, or rerender templates.
|
728
|
+
You can do:
|
729
|
+
|
730
|
+
album = Album[1]
|
731
|
+
forme_set(album)
|
732
|
+
|
733
|
+
And the method will update the +album+ object using the appropriate form values.
|
734
|
+
|
735
|
+
Note that using the Roda forme_set plugin requires you set a secret for the HMAC. It
|
736
|
+
is important that you keep this value secret, because if an attacker has access to this,
|
737
|
+
they would be able to set arbitrary attributes for model objects. In your Roda class,
|
738
|
+
you can load the plugin via:
|
739
|
+
|
740
|
+
plugin :forme_set, :secret => ENV["APP_FORME_HMAC_SECRET"]
|
741
|
+
|
742
|
+
By default, invalid form submissions will raise an exception. If you want to change
|
743
|
+
that behavior (i.e. to display a nice error page), pass a block when loading the plugin:
|
744
|
+
|
745
|
+
plugin :forme_set do |error_type, obj|
|
746
|
+
# ...
|
747
|
+
end
|
748
|
+
|
749
|
+
The block arguments will be a symbol for the type of error (:missing_data, :missing_hmac,
|
750
|
+
:hmac_mismatch, :csrf_mismatch, or :missing_namespace) and the object passed to +forme_set+.
|
751
|
+
This block should raise or halt. If it does not, the default behavior of raising an
|
752
|
+
exception will be taken.
|
753
|
+
|
754
|
+
=== Form Versions
|
755
|
+
|
756
|
+
The Roda forme_set plugin supports form versions. This allows you to gracefully handle
|
757
|
+
changes to forms, processing submissions of the form generated before the change (if
|
758
|
+
possible) as well as the processing submissions of the form generated after the change.
|
759
|
+
|
760
|
+
For example, maybe you have an existing form with just an input for the name:
|
761
|
+
|
762
|
+
form(album) do |f|
|
763
|
+
f.input(:name)
|
764
|
+
end
|
765
|
+
|
766
|
+
Then later, you want to add an input for the number of copies sold:
|
767
|
+
|
768
|
+
form(album) do |f|
|
769
|
+
f.input(:name)
|
770
|
+
f.input(:copies_sold)
|
771
|
+
end
|
772
|
+
|
773
|
+
Using the Roda forme_set plugin, submissions of the old form would only set the
|
774
|
+
name field, it wouldn't set the copies_sold field, since when the form was created,
|
775
|
+
only the name field was used.
|
776
|
+
|
777
|
+
You can handle this case be versioning the form when making changes to it:
|
778
|
+
|
779
|
+
form(album, {}, :form_version=>1) do |f|
|
780
|
+
f.input(:name)
|
781
|
+
f.input(:copies_sold)
|
782
|
+
end
|
783
|
+
|
784
|
+
When you are processing the form submission with forme_set, you pass a block, which
|
785
|
+
will be yielded the version for the form (nil if no version was set):
|
786
|
+
|
787
|
+
forme_set(album) do |version|
|
788
|
+
if version == nil
|
789
|
+
album.copies_sold = 0
|
790
|
+
end
|
791
|
+
end
|
792
|
+
|
793
|
+
The block is also yielded the object passed for forme_set, useful if you don't keep
|
794
|
+
a reference to it:
|
795
|
+
|
796
|
+
album = forme_set(Album.new) do |version, obj|
|
797
|
+
if version == nil
|
798
|
+
obj.copies_sold = 0
|
799
|
+
end
|
800
|
+
end
|
801
|
+
|
802
|
+
You only need to support old versions of the form for as long as their could be
|
803
|
+
active sessions that could use the old versions of the form. As long you as
|
804
|
+
are expiring sessions to prevent session fixation, you can remove the version
|
805
|
+
handling after the expiration period has passed since the change to the form
|
806
|
+
was made.
|
807
|
+
|
808
|
+
Note that this issue with handling changes to forms is not specific to the Roda
|
809
|
+
forme_set plugin, it affects pretty much all form submissions. The Roda forme_set
|
810
|
+
plugin just makes this issue easier to handle.
|
811
|
+
|
812
|
+
==== Caveats
|
813
|
+
|
814
|
+
The Roda forme_set plugin has basically the same caveats as Sequel forme_set plugin.
|
815
|
+
Additionally, it has a couple other restrictions that the Sequel forme_set plugin
|
816
|
+
does not have.
|
817
|
+
|
818
|
+
First, the Roda forme_set plugin only handles a single object in forms,
|
819
|
+
which must be provided when creating the form. It does not handle multiple
|
820
|
+
objects in the same form, and ignores any fields set for an object different
|
821
|
+
from the one passed when creating the form. You can use the Sequel forme_set
|
822
|
+
plugin to handle form submissions involving multiple objects, or for the
|
823
|
+
objects that were not passed when creating the form.
|
824
|
+
|
825
|
+
Second, the Roda forme_set plugin does not handle cases where the field values
|
826
|
+
are placed outside the forms default namespace. The Sequel forme_set plugin
|
827
|
+
can handle those issues, as long as all values are in the same namespace, since
|
828
|
+
the Sequel forme_set plugin requires you pass in the specific hash to use (the
|
829
|
+
Roda forme_set plugin use the form's namespace information and the submitted
|
830
|
+
parameters to determine the hash to use).
|
831
|
+
|
832
|
+
In cases where the Roda forme_set does not handle things correctly, you can use
|
833
|
+
forme_parse, which will return metadata in the same format as the Sequel plugin
|
834
|
+
forme_parse method, with the addition of a :form_version key in the hash for the
|
835
|
+
form version.
|
836
|
+
|
837
|
+
It is possible to use the Roda forme_set plugin for the submissions it can handle, the
|
838
|
+
Sequel forme_set plugin for the submissions it can handle, and set other fields manually
|
839
|
+
using the Sequel set_fields methods.
|
840
|
+
|
841
|
+
Note that when using the Roda forme_set plugin with an existing form, you should first
|
842
|
+
enable the Roda plugin without actually using the Roda forme_set method. Do not
|
843
|
+
start using the Roda forme_set method until all currently valid sessions were
|
844
|
+
established after the Roda forme_set plugin was enabled. Otherwise, sessions that
|
845
|
+
access the form before the Roda forme_set plugin was enabled will not work if they
|
846
|
+
submit the form after the Roda forme_set plugin is enabled.
|
847
|
+
|
689
848
|
== Other Sequel Plugins
|
690
849
|
|
691
850
|
In addition to the Sequel plugins mentioned above, Forme also ships with additional Sequel
|
@@ -695,9 +854,9 @@ forme_i18n :: Handles translations for labels using i18n.
|
|
695
854
|
|
696
855
|
= Roda Support
|
697
856
|
|
698
|
-
Forme ships with
|
699
|
-
recommended to use forme_route_csrf, as that uses Roda's route_csrf
|
700
|
-
supports more secure request-specific CSRF tokens. In both cases, usage in ERB
|
857
|
+
Forme ships with three Roda plugins: forme_set (discussed above), forme, and forme_route_csrf.
|
858
|
+
For new code, it is recommended to use forme_route_csrf, as that uses Roda's route_csrf
|
859
|
+
plugin, which supports more secure request-specific CSRF tokens. In both cases, usage in ERB
|
701
860
|
templates is the same:
|
702
861
|
|
703
862
|
<% form(@obj, :action=>'/foo') do |f| %>
|
@@ -780,6 +939,7 @@ These options are supported by all of the input types:
|
|
780
939
|
:disabled :: Set the disabled attribute if true
|
781
940
|
:error :: Set an error message, invoking the error_handler
|
782
941
|
:error_handler :: Set a custom error_handler, overriding the form's default
|
942
|
+
:help :: Set help text to use, invoking the helper
|
783
943
|
:helper :: Set a custom helper, overriding the form's default
|
784
944
|
:id :: The id attribute to use
|
785
945
|
:key :: The base to use for the name and id attributes, based on the current
|
@@ -822,8 +982,13 @@ creates multiple select options. Options:
|
|
822
982
|
for the select field and string to use a string
|
823
983
|
(:date default: <tt>[:year, '-', :month, '-', :day]</tt>)
|
824
984
|
(:datetime default: <tt>[:year, '-', :month, '-', :day, ' ', :hour, ':', :minute, ':', :second]</tt>)
|
985
|
+
:select_labels :: The labels to use for the select boxes. Should be a hash keyed by the
|
986
|
+
symbol used (e.g. <tt>{:month=>'Month'}</tt>). By default, no labels are used.
|
825
987
|
:select_options :: The options to use for the select boxes. Should be a hash keyed by the
|
826
|
-
symbol used in order (e.g. <tt>{:year=>1970..2020}</tt>)
|
988
|
+
symbol used in order (e.g. <tt>{:year=>1970..2020}</tt>). The values
|
989
|
+
can be a number used as both the value and the text of the option or
|
990
|
+
an array with two elements, the first of which is the value for the option
|
991
|
+
and the second of which is the text for the option.
|
827
992
|
|
828
993
|
=== :select
|
829
994
|
|
@@ -843,7 +1008,8 @@ Creates a select tag, containing option tags specified by the :options option.
|
|
843
1008
|
:options :: An enumerable of options used for creating option tags.
|
844
1009
|
If the :text_method and :value_method are not given and the entry is an
|
845
1010
|
array, uses the first entry of the array as the text of the option, and
|
846
|
-
the
|
1011
|
+
the last entry of the array as the value of the option. If the last entry
|
1012
|
+
of the array is a hash, uses the hash as the attributes for the option.
|
847
1013
|
:selected :: The value that should be selected. Any options that are equal to
|
848
1014
|
this value (or included in this value if a multiple select box),
|
849
1015
|
are set to selected.
|
@@ -862,6 +1028,7 @@ the following options:
|
|
862
1028
|
|
863
1029
|
:tag_wrapper :: The wrapper transformer for individual tags in the set
|
864
1030
|
:tag_labeler :: The labeler transformer for individual tags in the set
|
1031
|
+
:tag_label_attr :: The attributes to use for labels for individual tags in the set
|
865
1032
|
|
866
1033
|
=== :radioset
|
867
1034
|
|
@@ -980,7 +1147,9 @@ Forme ships with a bunch of built-in transformers that you can use:
|
|
980
1147
|
|
981
1148
|
=== +error_handler+
|
982
1149
|
|
1150
|
+
:after_legend :: designed for usage with :legend labeler, putting error message after legend, adding error for first input in the set
|
983
1151
|
:default :: modifies tag to add an error class and adds a span with the error message
|
1152
|
+
:set :: default error_handler for checkboxset and radioset inputs, that adds an error to the last input in the set
|
984
1153
|
|
985
1154
|
This supports the following options:
|
986
1155
|
|
@@ -998,8 +1167,10 @@ This supports the following options:
|
|
998
1167
|
|
999
1168
|
:default :: uses implicit labels, where the tag is a child of the label tag
|
1000
1169
|
:explicit :: uses explicit labels with the for attribute, where tag is a sibling of the label tag
|
1170
|
+
:legend :: adds a legend before the tags, mostly useful for accessible checkboxset and radioset inputs
|
1171
|
+
:span :: default labeler for checkboxset and radioset inputs that adds a span before the tags
|
1001
1172
|
|
1002
|
-
|
1173
|
+
The :default and :explicit labelers respect the following options:
|
1003
1174
|
|
1004
1175
|
:label_position :: Can be set to :before or :after to place the label before or after the the input.
|
1005
1176
|
:label_attr :: A hash of attributes to use for the label tag
|
@@ -1008,6 +1179,7 @@ Both of these respect the following options:
|
|
1008
1179
|
|
1009
1180
|
:default :: returns tag without wrapping
|
1010
1181
|
:div :: wraps tag in div tag
|
1182
|
+
:fieldset :: wraps tags in a fieldset, mostly useful for accessible checkboxset and radioset inputs
|
1011
1183
|
:fieldset_ol :: same as :li, but also sets +inputs_wrapper+ to :fieldset_ol
|
1012
1184
|
:li :: wraps tag in li tag
|
1013
1185
|
:ol :: same as :li, but also sets +inputs_wrapper+ to :ol
|
data/lib/forme/bs3.rb
CHANGED
data/lib/forme/form.rb
CHANGED
@@ -11,10 +11,55 @@ module Forme
|
|
11
11
|
|
12
12
|
# Return tag with error message span tag after it.
|
13
13
|
def call(tag, input)
|
14
|
+
[tag, error_tag(input)]
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def error_tag(input)
|
14
20
|
attr = input.opts[:error_attr]
|
15
21
|
attr = attr ? attr.dup : {}
|
16
22
|
Forme.attr_classes(attr, 'error_message')
|
17
|
-
|
23
|
+
|
24
|
+
if id = input.opts[:error_id]
|
25
|
+
unless attr['id'] || attr[:id]
|
26
|
+
attr['id'] = id
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
input.tag(:span, attr, input.opts[:error])
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class ErrorHandler::Set < ErrorHandler
|
35
|
+
Forme.register_transformer(:error_handler, :set, new)
|
36
|
+
|
37
|
+
def call(tag, input)
|
38
|
+
return super unless last_input = input.opts[:last_input]
|
39
|
+
|
40
|
+
last_input.opts[:error] = input.opts[:error]
|
41
|
+
last_input.opts[:error_attr] = input.opts[:error_attr] if input.opts[:error_attr]
|
42
|
+
last_input.opts[:error_handler] = :default
|
43
|
+
|
44
|
+
tag
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class ErrorHandler::AfterLegend < ErrorHandler
|
49
|
+
Forme.register_transformer(:error_handler, :after_legend, new)
|
50
|
+
|
51
|
+
def call(tag, input)
|
52
|
+
return super unless tag.is_a?(Array)
|
53
|
+
return super unless tag.first.is_a?(Tag)
|
54
|
+
return super unless tag.first.type == :legend
|
55
|
+
|
56
|
+
first_input = input.opts[:first_input]
|
57
|
+
attr = first_input.opts[:attr] ||= {}
|
58
|
+
Forme.attr_classes(attr, 'error')
|
59
|
+
attr['aria-invalid'] = 'true'
|
60
|
+
attr['aria-describedby'] = input.opts[:error_id] = "#{first_input.opts[:id]}_error_message"
|
61
|
+
|
62
|
+
tag.insert(1, error_tag(input))
|
18
63
|
end
|
19
64
|
end
|
20
65
|
end
|
@@ -61,7 +61,6 @@ module Forme
|
|
61
61
|
@attr = attr ? attr.dup : {}
|
62
62
|
@opts = input.opts
|
63
63
|
normalize_options
|
64
|
-
|
65
64
|
tag = if html = input.opts[:html]
|
66
65
|
html = html.call(input) if html.respond_to?(:call)
|
67
66
|
form.raw(html)
|
@@ -158,9 +157,13 @@ module Forme
|
|
158
157
|
ops = ops.merge(@opts[:select_options]) if @opts[:select_options]
|
159
158
|
first_input = true
|
160
159
|
format = DATE_SELECT_FORMAT
|
160
|
+
@opts[:select_labels] ||= {}
|
161
161
|
order.map do |x|
|
162
162
|
next x if x.is_a?(String)
|
163
|
-
|
163
|
+
options = ops[x].map do |value, text|
|
164
|
+
[text || sprintf(format, value), value]
|
165
|
+
end
|
166
|
+
opts = @opts.merge(:label=>@opts[:select_labels][x], :wrapper=>nil, :error=>nil, :name=>"#{name}[#{x}]", :value=>values[x], :options=>options)
|
164
167
|
opts[:id] = if first_input
|
165
168
|
first_input = false
|
166
169
|
id
|
@@ -186,9 +189,9 @@ module Forme
|
|
186
189
|
copy_options_to_attributes([:size])
|
187
190
|
|
188
191
|
os = process_select_optgroups(:_format_select_optgroup) do |label, value, sel, attrs|
|
189
|
-
if value || sel
|
190
|
-
|
191
|
-
attrs[:value] = value
|
192
|
+
if !value.nil? || sel
|
193
|
+
attrs = attrs.dup
|
194
|
+
attrs[:value] = value unless value.nil?
|
192
195
|
attrs[:selected] = :selected if sel
|
193
196
|
end
|
194
197
|
tag(:option, attrs, [label])
|
@@ -221,24 +224,25 @@ module Forme
|
|
221
224
|
key = @opts[:key]
|
222
225
|
name = @opts[:name]
|
223
226
|
id = @opts[:id]
|
224
|
-
|
225
|
-
|
226
|
-
end
|
227
|
-
if @opts[:label]
|
228
|
-
@opts[:set_label] = @opts.delete(:label)
|
229
|
-
end
|
227
|
+
@opts[:labeler] ||= :span
|
228
|
+
@opts[:error_handler] ||= :set
|
230
229
|
|
231
230
|
tag_wrapper = Forme.transformer(:tag_wrapper, @opts.delete(:tag_wrapper), @input.form_opts) || :default
|
232
231
|
tag_labeler = Forme.transformer(:labeler, @opts.delete(:tag_labeler), @input.form_opts) || :default
|
233
232
|
wrapper = @opts.fetch(:wrapper){@opts[:wrapper] = @input.form_opts[:set_wrapper] || @input.form_opts[:wrapper]}
|
234
233
|
wrapper = Forme.transformer(:wrapper, wrapper)
|
234
|
+
tag_label_attr = @opts[:tag_label_attr] || @opts[:label_attr]
|
235
235
|
|
236
|
-
|
237
|
-
|
236
|
+
first_input = nil
|
237
|
+
last_input = nil
|
238
|
+
ret = process_select_optgroups(:_format_set_optgroup) do |label, value, sel, attrs|
|
239
|
+
value = label if value.nil?
|
238
240
|
label_attr = {:class=>:option}
|
239
|
-
label_attr.merge!(
|
240
|
-
r_opts = attrs.merge(tag_attrs).merge(:label=>label||value, :label_attr=>label_attr, :wrapper=>tag_wrapper, :labeler=>tag_labeler)
|
241
|
-
r_opts[:value]
|
241
|
+
label_attr.merge!(tag_label_attr) if tag_label_attr
|
242
|
+
r_opts = attrs.merge(tag_attrs).merge(:label=>label||value, :label_attr=>label_attr, :wrapper=>tag_wrapper, :labeler=>tag_labeler, :error=>nil, :error_attr=>nil)
|
243
|
+
if r_opts[:value].nil?
|
244
|
+
r_opts[:value] = value unless value.nil?
|
245
|
+
end
|
242
246
|
r_opts[:checked] ||= :checked if sel
|
243
247
|
r_opts[:formatter] = @opts[:formatter] if @opts[:formatter]
|
244
248
|
|
@@ -253,31 +257,15 @@ module Forme
|
|
253
257
|
r_opts[:key_id] ||= value
|
254
258
|
end
|
255
259
|
|
256
|
-
form._input(type, r_opts)
|
260
|
+
input = form._input(type, r_opts)
|
261
|
+
first_input ||= input
|
262
|
+
last_input = input
|
257
263
|
end
|
258
264
|
|
259
|
-
|
260
|
-
|
261
|
-
end
|
262
|
-
|
263
|
-
tags.unshift(set_label) if @opts[:set_label]
|
264
|
-
|
265
|
-
tags
|
266
|
-
end
|
265
|
+
@opts[:first_input] = first_input
|
266
|
+
@opts[:last_input] = last_input
|
267
267
|
|
268
|
-
|
269
|
-
form._tag(:span, {:class=>:label}, @opts[:set_label])
|
270
|
-
end
|
271
|
-
|
272
|
-
def _add_set_error(tags)
|
273
|
-
if (last_input = tags.last) && last_input.is_a?(Input)
|
274
|
-
last_input.opts[:error] = @opts[:set_error]
|
275
|
-
last_input.opts[:error_attr] = @opts[:error_attr] if @opts[:error_attr]
|
276
|
-
else
|
277
|
-
attr = @opts[:error_attr] || {}
|
278
|
-
Forme.attr_classes(attr, 'error_message')
|
279
|
-
tags << form._tag(:span, attr, [@opts[:set_error]])
|
280
|
-
end
|
268
|
+
ret
|
281
269
|
end
|
282
270
|
|
283
271
|
# Formats a textarea. Respects the following options:
|
@@ -319,7 +307,19 @@ module Forme
|
|
319
307
|
handle_errors_option
|
320
308
|
|
321
309
|
Forme.attr_classes(@attr, @opts[:class]) if @opts.has_key?(:class)
|
322
|
-
|
310
|
+
|
311
|
+
if @opts[:error]
|
312
|
+
Forme.attr_classes(@attr, 'error')
|
313
|
+
@attr["aria-invalid"] = "true"
|
314
|
+
if @opts.fetch(:error_handler, true)
|
315
|
+
unless @opts[:error_id]
|
316
|
+
if id = @attr[:id] || @attr['id']
|
317
|
+
error_id = @attr['aria-describedby'] ||= "#{id}_error_message"
|
318
|
+
@opts[:error_id] = error_id
|
319
|
+
end
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
323
|
|
324
324
|
if data = opts[:data]
|
325
325
|
data.each do |k, v|
|
@@ -462,7 +462,7 @@ module Forme
|
|
462
462
|
text = x
|
463
463
|
end
|
464
464
|
|
465
|
-
yield [text, val, val ? cmp.call(val) : cmp.call(text), attr]
|
465
|
+
yield [text, val, !val.nil? ? cmp.call(val) : cmp.call(text), attr]
|
466
466
|
end
|
467
467
|
end
|
468
468
|
end
|
@@ -538,8 +538,11 @@ module Forme
|
|
538
538
|
end
|
539
539
|
|
540
540
|
# Use a span with text instead of an input field.
|
541
|
+
# For hidden inputs, do not show anything
|
541
542
|
def _format_input(type)
|
542
|
-
|
543
|
+
unless type.to_s == 'hidden'
|
544
|
+
tag(:span, {'class'=>'readonly-text'}, @attr[:value])
|
545
|
+
end
|
543
546
|
end
|
544
547
|
|
545
548
|
# Disabled radio button inputs.
|
@@ -560,9 +563,20 @@ module Forme
|
|
560
563
|
''
|
561
564
|
end
|
562
565
|
|
563
|
-
#
|
566
|
+
# Format the text as separate paragraphs.
|
564
567
|
def format_textarea
|
565
|
-
|
568
|
+
text = @attr[:value]
|
569
|
+
case text
|
570
|
+
when nil, Forme::Raw
|
571
|
+
# nothing
|
572
|
+
when String
|
573
|
+
text = text.gsub(/\A[\r\n]+|[\r\n]+\z/, '').split(/(?:\r?\n)(?:\r?\n)+/).map do |t|
|
574
|
+
t = Forme.h(t)
|
575
|
+
t.gsub!(/\r?\n/, "<br />")
|
576
|
+
tag(:p, {}, Forme.raw(t))
|
577
|
+
end
|
578
|
+
end
|
579
|
+
tag(:div, {'class'=>'readonly-textarea'}, text)
|
566
580
|
end
|
567
581
|
end
|
568
582
|
end
|