omg-actionview 8.0.0.alpha1
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 +7 -0
- data/CHANGELOG.md +25 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +40 -0
- data/app/assets/javascripts/rails-ujs.esm.js +686 -0
- data/app/assets/javascripts/rails-ujs.js +630 -0
- data/lib/action_view/base.rb +316 -0
- data/lib/action_view/buffers.rb +165 -0
- data/lib/action_view/cache_expiry.rb +69 -0
- data/lib/action_view/context.rb +32 -0
- data/lib/action_view/dependency_tracker/erb_tracker.rb +159 -0
- data/lib/action_view/dependency_tracker/ruby_tracker.rb +43 -0
- data/lib/action_view/dependency_tracker/wildcard_resolver.rb +32 -0
- data/lib/action_view/dependency_tracker.rb +41 -0
- data/lib/action_view/deprecator.rb +7 -0
- data/lib/action_view/digestor.rb +130 -0
- data/lib/action_view/flows.rb +75 -0
- data/lib/action_view/gem_version.rb +17 -0
- data/lib/action_view/helpers/active_model_helper.rb +54 -0
- data/lib/action_view/helpers/asset_tag_helper.rb +680 -0
- data/lib/action_view/helpers/asset_url_helper.rb +473 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +205 -0
- data/lib/action_view/helpers/cache_helper.rb +315 -0
- data/lib/action_view/helpers/capture_helper.rb +236 -0
- data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
- data/lib/action_view/helpers/controller_helper.rb +42 -0
- data/lib/action_view/helpers/csp_helper.rb +26 -0
- data/lib/action_view/helpers/csrf_helper.rb +35 -0
- data/lib/action_view/helpers/date_helper.rb +1266 -0
- data/lib/action_view/helpers/debug_helper.rb +38 -0
- data/lib/action_view/helpers/form_helper.rb +2765 -0
- data/lib/action_view/helpers/form_options_helper.rb +927 -0
- data/lib/action_view/helpers/form_tag_helper.rb +1088 -0
- data/lib/action_view/helpers/javascript_helper.rb +96 -0
- data/lib/action_view/helpers/number_helper.rb +165 -0
- data/lib/action_view/helpers/output_safety_helper.rb +70 -0
- data/lib/action_view/helpers/rendering_helper.rb +218 -0
- data/lib/action_view/helpers/sanitize_helper.rb +201 -0
- data/lib/action_view/helpers/tag_helper.rb +621 -0
- data/lib/action_view/helpers/tags/base.rb +138 -0
- data/lib/action_view/helpers/tags/check_box.rb +65 -0
- data/lib/action_view/helpers/tags/checkable.rb +18 -0
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +37 -0
- data/lib/action_view/helpers/tags/collection_helpers.rb +118 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +31 -0
- data/lib/action_view/helpers/tags/collection_select.rb +33 -0
- data/lib/action_view/helpers/tags/color_field.rb +26 -0
- data/lib/action_view/helpers/tags/date_field.rb +14 -0
- data/lib/action_view/helpers/tags/date_select.rb +75 -0
- data/lib/action_view/helpers/tags/datetime_field.rb +39 -0
- data/lib/action_view/helpers/tags/datetime_local_field.rb +29 -0
- data/lib/action_view/helpers/tags/datetime_select.rb +10 -0
- data/lib/action_view/helpers/tags/email_field.rb +10 -0
- data/lib/action_view/helpers/tags/file_field.rb +26 -0
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +34 -0
- data/lib/action_view/helpers/tags/hidden_field.rb +14 -0
- data/lib/action_view/helpers/tags/label.rb +84 -0
- data/lib/action_view/helpers/tags/month_field.rb +14 -0
- data/lib/action_view/helpers/tags/number_field.rb +20 -0
- data/lib/action_view/helpers/tags/password_field.rb +14 -0
- data/lib/action_view/helpers/tags/placeholderable.rb +24 -0
- data/lib/action_view/helpers/tags/radio_button.rb +32 -0
- data/lib/action_view/helpers/tags/range_field.rb +10 -0
- data/lib/action_view/helpers/tags/search_field.rb +27 -0
- data/lib/action_view/helpers/tags/select.rb +45 -0
- data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
- data/lib/action_view/helpers/tags/tel_field.rb +10 -0
- data/lib/action_view/helpers/tags/text_area.rb +24 -0
- data/lib/action_view/helpers/tags/text_field.rb +33 -0
- data/lib/action_view/helpers/tags/time_field.rb +23 -0
- data/lib/action_view/helpers/tags/time_select.rb +10 -0
- data/lib/action_view/helpers/tags/time_zone_select.rb +25 -0
- data/lib/action_view/helpers/tags/translator.rb +39 -0
- data/lib/action_view/helpers/tags/url_field.rb +10 -0
- data/lib/action_view/helpers/tags/week_field.rb +14 -0
- data/lib/action_view/helpers/tags/weekday_select.rb +31 -0
- data/lib/action_view/helpers/tags.rb +47 -0
- data/lib/action_view/helpers/text_helper.rb +568 -0
- data/lib/action_view/helpers/translation_helper.rb +161 -0
- data/lib/action_view/helpers/url_helper.rb +812 -0
- data/lib/action_view/helpers.rb +68 -0
- data/lib/action_view/layouts.rb +434 -0
- data/lib/action_view/locale/en.yml +56 -0
- data/lib/action_view/log_subscriber.rb +132 -0
- data/lib/action_view/lookup_context.rb +299 -0
- data/lib/action_view/model_naming.rb +14 -0
- data/lib/action_view/path_registry.rb +57 -0
- data/lib/action_view/path_set.rb +84 -0
- data/lib/action_view/railtie.rb +132 -0
- data/lib/action_view/record_identifier.rb +118 -0
- data/lib/action_view/render_parser/prism_render_parser.rb +139 -0
- data/lib/action_view/render_parser/ripper_render_parser.rb +350 -0
- data/lib/action_view/render_parser.rb +40 -0
- data/lib/action_view/renderer/abstract_renderer.rb +186 -0
- data/lib/action_view/renderer/collection_renderer.rb +204 -0
- data/lib/action_view/renderer/object_renderer.rb +34 -0
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +120 -0
- data/lib/action_view/renderer/partial_renderer.rb +267 -0
- data/lib/action_view/renderer/renderer.rb +107 -0
- data/lib/action_view/renderer/streaming_template_renderer.rb +107 -0
- data/lib/action_view/renderer/template_renderer.rb +115 -0
- data/lib/action_view/rendering.rb +190 -0
- data/lib/action_view/routing_url_for.rb +149 -0
- data/lib/action_view/tasks/cache_digests.rake +25 -0
- data/lib/action_view/template/error.rb +264 -0
- data/lib/action_view/template/handlers/builder.rb +25 -0
- data/lib/action_view/template/handlers/erb/erubi.rb +85 -0
- data/lib/action_view/template/handlers/erb.rb +157 -0
- data/lib/action_view/template/handlers/html.rb +11 -0
- data/lib/action_view/template/handlers/raw.rb +11 -0
- data/lib/action_view/template/handlers.rb +66 -0
- data/lib/action_view/template/html.rb +33 -0
- data/lib/action_view/template/inline.rb +22 -0
- data/lib/action_view/template/raw_file.rb +25 -0
- data/lib/action_view/template/renderable.rb +30 -0
- data/lib/action_view/template/resolver.rb +212 -0
- data/lib/action_view/template/sources/file.rb +17 -0
- data/lib/action_view/template/sources.rb +13 -0
- data/lib/action_view/template/text.rb +32 -0
- data/lib/action_view/template/types.rb +50 -0
- data/lib/action_view/template.rb +580 -0
- data/lib/action_view/template_details.rb +66 -0
- data/lib/action_view/template_path.rb +66 -0
- data/lib/action_view/test_case.rb +449 -0
- data/lib/action_view/testing/resolvers.rb +44 -0
- data/lib/action_view/unbound_template.rb +67 -0
- data/lib/action_view/version.rb +10 -0
- data/lib/action_view/view_paths.rb +117 -0
- data/lib/action_view.rb +104 -0
- metadata +275 -0
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionView
|
|
4
|
+
module Helpers
|
|
5
|
+
module Tags # :nodoc:
|
|
6
|
+
class Base # :nodoc:
|
|
7
|
+
include Helpers::ActiveModelInstanceTag, Helpers::TagHelper, Helpers::FormTagHelper
|
|
8
|
+
|
|
9
|
+
attr_reader :object
|
|
10
|
+
|
|
11
|
+
def initialize(object_name, method_name, template_object, options = {})
|
|
12
|
+
@object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup
|
|
13
|
+
@template_object = template_object
|
|
14
|
+
|
|
15
|
+
@object_name.sub!(/\[\]$/, "") || @object_name.sub!(/\[\]\]$/, "]")
|
|
16
|
+
@object = retrieve_object(options.delete(:object))
|
|
17
|
+
@skip_default_ids = options.delete(:skip_default_ids)
|
|
18
|
+
@allow_method_names_outside_object = options.delete(:allow_method_names_outside_object)
|
|
19
|
+
@options = options
|
|
20
|
+
|
|
21
|
+
if Regexp.last_match
|
|
22
|
+
@generate_indexed_names = true
|
|
23
|
+
@auto_index = retrieve_autoindex(Regexp.last_match.pre_match)
|
|
24
|
+
else
|
|
25
|
+
@generate_indexed_names = false
|
|
26
|
+
@auto_index = nil
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# This is what child classes implement.
|
|
31
|
+
def render
|
|
32
|
+
raise NotImplementedError, "Subclasses must implement a render method"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
private
|
|
36
|
+
def value
|
|
37
|
+
return unless object
|
|
38
|
+
|
|
39
|
+
if @allow_method_names_outside_object
|
|
40
|
+
object.public_send @method_name if object.respond_to?(@method_name)
|
|
41
|
+
else
|
|
42
|
+
object.public_send @method_name
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def value_before_type_cast
|
|
47
|
+
return unless object
|
|
48
|
+
|
|
49
|
+
method_before_type_cast = @method_name + "_before_type_cast"
|
|
50
|
+
|
|
51
|
+
if value_came_from_user? && object.respond_to?(method_before_type_cast)
|
|
52
|
+
object.public_send(method_before_type_cast)
|
|
53
|
+
else
|
|
54
|
+
value
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def value_came_from_user?
|
|
59
|
+
method_name = "#{@method_name}_came_from_user?"
|
|
60
|
+
!object.respond_to?(method_name) || object.public_send(method_name)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def retrieve_object(object)
|
|
64
|
+
if object
|
|
65
|
+
object
|
|
66
|
+
elsif @template_object.instance_variable_defined?("@#{@object_name}")
|
|
67
|
+
@template_object.instance_variable_get("@#{@object_name}")
|
|
68
|
+
end
|
|
69
|
+
rescue NameError
|
|
70
|
+
# As @object_name may contain the nested syntax (item[subobject]) we need to fallback to nil.
|
|
71
|
+
nil
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def retrieve_autoindex(pre_match)
|
|
75
|
+
object = self.object || @template_object.instance_variable_get("@#{pre_match}")
|
|
76
|
+
if object && object.respond_to?(:to_param)
|
|
77
|
+
object.to_param
|
|
78
|
+
else
|
|
79
|
+
raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def add_default_name_and_id_for_value(tag_value, options)
|
|
84
|
+
if tag_value.nil?
|
|
85
|
+
add_default_name_and_id(options)
|
|
86
|
+
else
|
|
87
|
+
specified_id = options["id"]
|
|
88
|
+
add_default_name_and_id(options)
|
|
89
|
+
|
|
90
|
+
if specified_id.blank? && options["id"].present?
|
|
91
|
+
options["id"] += "_#{sanitized_value(tag_value)}"
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
def add_default_name_and_id(options)
|
|
97
|
+
index = name_and_id_index(options)
|
|
98
|
+
options["name"] = options.fetch("name") { tag_name(options["multiple"], index) }
|
|
99
|
+
|
|
100
|
+
if generate_ids?
|
|
101
|
+
options["id"] = options.fetch("id") { tag_id(index, options.delete("namespace")) }
|
|
102
|
+
if namespace = options.delete("namespace")
|
|
103
|
+
options["id"] = options["id"] ? "#{namespace}_#{options['id']}" : namespace
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def tag_name(multiple = false, index = nil)
|
|
109
|
+
@template_object.field_name(@object_name, sanitized_method_name, multiple: multiple, index: index)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def tag_id(index = nil, namespace = nil)
|
|
113
|
+
@template_object.field_id(@object_name, @method_name, index: index, namespace: namespace)
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
def sanitized_method_name
|
|
117
|
+
@sanitized_method_name ||= @method_name.delete_suffix("?")
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def sanitized_value(value)
|
|
121
|
+
value.to_s.gsub(/[\s.]/, "_").gsub(/[^-[[:word:]]]/, "").downcase
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def name_and_id_index(options)
|
|
125
|
+
if options.key?("index")
|
|
126
|
+
options.delete("index") || ""
|
|
127
|
+
elsif @generate_indexed_names
|
|
128
|
+
@auto_index || ""
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def generate_ids?
|
|
133
|
+
!@skip_default_ids
|
|
134
|
+
end
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "action_view/helpers/tags/checkable"
|
|
4
|
+
|
|
5
|
+
module ActionView
|
|
6
|
+
module Helpers
|
|
7
|
+
module Tags # :nodoc:
|
|
8
|
+
class CheckBox < Base # :nodoc:
|
|
9
|
+
include Checkable
|
|
10
|
+
|
|
11
|
+
def initialize(object_name, method_name, template_object, checked_value, unchecked_value, options)
|
|
12
|
+
@checked_value = checked_value
|
|
13
|
+
@unchecked_value = unchecked_value
|
|
14
|
+
super(object_name, method_name, template_object, options)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def render
|
|
18
|
+
options = @options.stringify_keys
|
|
19
|
+
options["type"] = "checkbox"
|
|
20
|
+
options["value"] = @checked_value
|
|
21
|
+
options["checked"] = "checked" if input_checked?(options)
|
|
22
|
+
|
|
23
|
+
if options["multiple"]
|
|
24
|
+
add_default_name_and_id_for_value(@checked_value, options)
|
|
25
|
+
options.delete("multiple")
|
|
26
|
+
else
|
|
27
|
+
add_default_name_and_id(options)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
include_hidden = options.delete("include_hidden") { true }
|
|
31
|
+
checkbox = tag("input", options)
|
|
32
|
+
|
|
33
|
+
if include_hidden
|
|
34
|
+
hidden = hidden_field_for_checkbox(options)
|
|
35
|
+
hidden + checkbox
|
|
36
|
+
else
|
|
37
|
+
checkbox
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
def checked?(value)
|
|
43
|
+
case value
|
|
44
|
+
when TrueClass, FalseClass
|
|
45
|
+
value == !!@checked_value
|
|
46
|
+
when NilClass
|
|
47
|
+
false
|
|
48
|
+
when String
|
|
49
|
+
value == @checked_value
|
|
50
|
+
else
|
|
51
|
+
if value.respond_to?(:include?)
|
|
52
|
+
value.include?(@checked_value)
|
|
53
|
+
else
|
|
54
|
+
value.to_i == @checked_value.to_i
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def hidden_field_for_checkbox(options)
|
|
60
|
+
@unchecked_value ? tag("input", options.slice("name", "disabled", "form").merge!("type" => "hidden", "value" => @unchecked_value, "autocomplete" => "off")) : "".html_safe
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionView
|
|
4
|
+
module Helpers
|
|
5
|
+
module Tags # :nodoc:
|
|
6
|
+
module Checkable # :nodoc:
|
|
7
|
+
def input_checked?(options)
|
|
8
|
+
if options.has_key?("checked")
|
|
9
|
+
checked = options.delete "checked"
|
|
10
|
+
checked == true || checked == "checked"
|
|
11
|
+
else
|
|
12
|
+
checked?(value)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "action_view/helpers/tags/collection_helpers"
|
|
4
|
+
|
|
5
|
+
module ActionView
|
|
6
|
+
module Helpers
|
|
7
|
+
module Tags # :nodoc:
|
|
8
|
+
class CollectionCheckBoxes < Base # :nodoc:
|
|
9
|
+
include CollectionHelpers
|
|
10
|
+
include FormOptionsHelper
|
|
11
|
+
|
|
12
|
+
class CheckBoxBuilder < Builder # :nodoc:
|
|
13
|
+
def checkbox(extra_html_options = {})
|
|
14
|
+
html_options = extra_html_options.merge(@input_html_options)
|
|
15
|
+
html_options[:multiple] = true
|
|
16
|
+
html_options[:skip_default_ids] = false
|
|
17
|
+
@template_object.checkbox(@object_name, @method_name, html_options, @value, nil)
|
|
18
|
+
end
|
|
19
|
+
alias_method :check_box, :checkbox
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def render(&block)
|
|
23
|
+
render_collection_for(CheckBoxBuilder, &block)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
def render_component(builder)
|
|
28
|
+
builder.checkbox + builder.label
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def hidden_field_name
|
|
32
|
+
"#{super}[]"
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionView
|
|
4
|
+
module Helpers
|
|
5
|
+
module Tags # :nodoc:
|
|
6
|
+
module CollectionHelpers # :nodoc:
|
|
7
|
+
class Builder # :nodoc:
|
|
8
|
+
attr_reader :object, :text, :value
|
|
9
|
+
|
|
10
|
+
def initialize(template_object, object_name, method_name, object,
|
|
11
|
+
sanitized_attribute_name, text, value, input_html_options)
|
|
12
|
+
@template_object = template_object
|
|
13
|
+
@object_name = object_name
|
|
14
|
+
@method_name = method_name
|
|
15
|
+
@object = object
|
|
16
|
+
@sanitized_attribute_name = sanitized_attribute_name
|
|
17
|
+
@text = text
|
|
18
|
+
@value = value
|
|
19
|
+
@input_html_options = input_html_options
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def label(label_html_options = {}, &block)
|
|
23
|
+
html_options = @input_html_options.slice(:index, :namespace).merge(label_html_options)
|
|
24
|
+
html_options[:for] ||= @input_html_options[:id] if @input_html_options[:id]
|
|
25
|
+
|
|
26
|
+
@template_object.label(@object_name, @sanitized_attribute_name, @text, html_options, &block)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def initialize(object_name, method_name, template_object, collection, value_method, text_method, options, html_options)
|
|
31
|
+
@collection = collection
|
|
32
|
+
@value_method = value_method
|
|
33
|
+
@text_method = text_method
|
|
34
|
+
@html_options = html_options
|
|
35
|
+
|
|
36
|
+
super(object_name, method_name, template_object, options)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
def instantiate_builder(builder_class, item, value, text, html_options)
|
|
41
|
+
builder_class.new(@template_object, @object_name, @method_name, item,
|
|
42
|
+
sanitize_attribute_name(value), text, value, html_options)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Generate default options for collection helpers, such as :checked and
|
|
46
|
+
# :disabled.
|
|
47
|
+
def default_html_options_for_collection(item, value)
|
|
48
|
+
html_options = @html_options.dup
|
|
49
|
+
|
|
50
|
+
[:checked, :selected, :disabled, :readonly].each do |option|
|
|
51
|
+
current_value = @options[option]
|
|
52
|
+
next if current_value.nil?
|
|
53
|
+
|
|
54
|
+
accept = if current_value.respond_to?(:call)
|
|
55
|
+
current_value.call(item)
|
|
56
|
+
else
|
|
57
|
+
Array(current_value).map(&:to_s).include?(value.to_s)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
if accept
|
|
61
|
+
html_options[option] = true
|
|
62
|
+
elsif option == :checked
|
|
63
|
+
html_options[option] = false
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
html_options[:object] = @object
|
|
68
|
+
html_options
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def sanitize_attribute_name(value)
|
|
72
|
+
"#{sanitized_method_name}_#{sanitized_value(value)}"
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def render_collection
|
|
76
|
+
@collection.map do |item|
|
|
77
|
+
value = value_for_collection(item, @value_method)
|
|
78
|
+
text = value_for_collection(item, @text_method)
|
|
79
|
+
default_html_options = default_html_options_for_collection(item, value)
|
|
80
|
+
additional_html_options = option_html_attributes(item)
|
|
81
|
+
|
|
82
|
+
yield item, value, text, default_html_options.merge(additional_html_options)
|
|
83
|
+
end.join.html_safe
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def render_collection_for(builder_class, &block)
|
|
87
|
+
options = @options.stringify_keys
|
|
88
|
+
rendered_collection = render_collection do |item, value, text, default_html_options|
|
|
89
|
+
builder = instantiate_builder(builder_class, item, value, text, default_html_options)
|
|
90
|
+
|
|
91
|
+
if block_given?
|
|
92
|
+
@template_object.capture(builder, &block)
|
|
93
|
+
else
|
|
94
|
+
render_component(builder)
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Prepend a hidden field to make sure something will be sent back to the
|
|
99
|
+
# server if all radio buttons are unchecked.
|
|
100
|
+
if options.fetch("include_hidden", true)
|
|
101
|
+
hidden_field + rendered_collection
|
|
102
|
+
else
|
|
103
|
+
rendered_collection
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def hidden_field
|
|
108
|
+
hidden_name = @html_options[:name] || hidden_field_name
|
|
109
|
+
@template_object.hidden_field_tag(hidden_name, "", id: nil)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def hidden_field_name
|
|
113
|
+
"#{tag_name(false, @options[:index])}"
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "action_view/helpers/tags/collection_helpers"
|
|
4
|
+
|
|
5
|
+
module ActionView
|
|
6
|
+
module Helpers
|
|
7
|
+
module Tags # :nodoc:
|
|
8
|
+
class CollectionRadioButtons < Base # :nodoc:
|
|
9
|
+
include CollectionHelpers
|
|
10
|
+
include FormOptionsHelper
|
|
11
|
+
|
|
12
|
+
class RadioButtonBuilder < Builder # :nodoc:
|
|
13
|
+
def radio_button(extra_html_options = {})
|
|
14
|
+
html_options = extra_html_options.merge(@input_html_options)
|
|
15
|
+
html_options[:skip_default_ids] = false
|
|
16
|
+
@template_object.radio_button(@object_name, @method_name, @value, html_options)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def render(&block)
|
|
21
|
+
render_collection_for(RadioButtonBuilder, &block)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
def render_component(builder)
|
|
26
|
+
builder.radio_button + builder.label
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionView
|
|
4
|
+
module Helpers
|
|
5
|
+
module Tags # :nodoc:
|
|
6
|
+
class CollectionSelect < Base # :nodoc:
|
|
7
|
+
include SelectRenderer
|
|
8
|
+
include FormOptionsHelper
|
|
9
|
+
|
|
10
|
+
def initialize(object_name, method_name, template_object, collection, value_method, text_method, options, html_options)
|
|
11
|
+
@collection = collection
|
|
12
|
+
@value_method = value_method
|
|
13
|
+
@text_method = text_method
|
|
14
|
+
@html_options = html_options
|
|
15
|
+
|
|
16
|
+
super(object_name, method_name, template_object, options)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def render
|
|
20
|
+
option_tags_options = {
|
|
21
|
+
selected: @options.fetch(:selected) { value },
|
|
22
|
+
disabled: @options[:disabled]
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
select_content_tag(
|
|
26
|
+
options_from_collection_for_select(@collection, @value_method, @text_method, option_tags_options),
|
|
27
|
+
@options, @html_options
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionView
|
|
4
|
+
module Helpers
|
|
5
|
+
module Tags # :nodoc:
|
|
6
|
+
class ColorField < TextField # :nodoc:
|
|
7
|
+
def render
|
|
8
|
+
options = @options.stringify_keys
|
|
9
|
+
options["value"] ||= validate_color_string(value)
|
|
10
|
+
@options = options
|
|
11
|
+
super
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
private
|
|
15
|
+
def validate_color_string(string)
|
|
16
|
+
regex = /#[0-9a-fA-F]{6}/
|
|
17
|
+
if regex.match?(string)
|
|
18
|
+
string.downcase
|
|
19
|
+
else
|
|
20
|
+
"#000000"
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "active_support/core_ext/time/calculations"
|
|
4
|
+
|
|
5
|
+
module ActionView
|
|
6
|
+
module Helpers
|
|
7
|
+
module Tags # :nodoc:
|
|
8
|
+
class DateSelect < Base # :nodoc:
|
|
9
|
+
include SelectRenderer
|
|
10
|
+
|
|
11
|
+
def initialize(object_name, method_name, template_object, options, html_options)
|
|
12
|
+
@html_options = html_options
|
|
13
|
+
|
|
14
|
+
super(object_name, method_name, template_object, options)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def render
|
|
18
|
+
error_wrapping(datetime_selector(@options, @html_options).public_send("select_#{select_type}").html_safe)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class << self
|
|
22
|
+
def select_type
|
|
23
|
+
@select_type ||= name.split("::").last.sub("Select", "").downcase
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
def select_type
|
|
29
|
+
self.class.select_type
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def datetime_selector(options, html_options)
|
|
33
|
+
datetime = options.fetch(:selected) { value || default_datetime(options) }
|
|
34
|
+
@auto_index ||= nil
|
|
35
|
+
|
|
36
|
+
options = options.dup
|
|
37
|
+
options[:field_name] = @method_name
|
|
38
|
+
options[:include_position] = true
|
|
39
|
+
options[:prefix] ||= @object_name
|
|
40
|
+
options[:index] = @auto_index if @auto_index && !options.has_key?(:index)
|
|
41
|
+
|
|
42
|
+
DateTimeSelector.new(datetime, options, html_options)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def default_datetime(options)
|
|
46
|
+
return if options[:include_blank] || options[:prompt]
|
|
47
|
+
|
|
48
|
+
case options[:default]
|
|
49
|
+
when nil
|
|
50
|
+
Time.current
|
|
51
|
+
when Date, Time
|
|
52
|
+
options[:default]
|
|
53
|
+
else
|
|
54
|
+
default = options[:default].dup
|
|
55
|
+
|
|
56
|
+
# Rename :minute and :second to :min and :sec
|
|
57
|
+
default[:min] ||= default[:minute]
|
|
58
|
+
default[:sec] ||= default[:second]
|
|
59
|
+
|
|
60
|
+
time = Time.current
|
|
61
|
+
|
|
62
|
+
[:year, :month, :day, :hour, :min, :sec].each do |key|
|
|
63
|
+
default[key] ||= time.public_send(key)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
Time.utc(
|
|
67
|
+
default[:year], default[:month], default[:day],
|
|
68
|
+
default[:hour], default[:min], default[:sec]
|
|
69
|
+
)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionView
|
|
4
|
+
module Helpers
|
|
5
|
+
module Tags # :nodoc:
|
|
6
|
+
class DatetimeField < TextField # :nodoc:
|
|
7
|
+
def render
|
|
8
|
+
options = @options.stringify_keys
|
|
9
|
+
options["value"] = datetime_value(options["value"] || value)
|
|
10
|
+
options["min"] = format_datetime(parse_datetime(options["min"]))
|
|
11
|
+
options["max"] = format_datetime(parse_datetime(options["max"]))
|
|
12
|
+
@options = options
|
|
13
|
+
super
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
private
|
|
17
|
+
def datetime_value(value)
|
|
18
|
+
if value.is_a?(String)
|
|
19
|
+
value
|
|
20
|
+
else
|
|
21
|
+
format_datetime(value)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def format_datetime(value)
|
|
26
|
+
raise NotImplementedError
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def parse_datetime(value)
|
|
30
|
+
if value.is_a?(String)
|
|
31
|
+
DateTime.parse(value) rescue nil
|
|
32
|
+
else
|
|
33
|
+
value
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionView
|
|
4
|
+
module Helpers
|
|
5
|
+
module Tags # :nodoc:
|
|
6
|
+
class DatetimeLocalField < DatetimeField # :nodoc:
|
|
7
|
+
def initialize(object_name, method_name, template_object, options = {})
|
|
8
|
+
@include_seconds = options.delete(:include_seconds) { true }
|
|
9
|
+
super
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
class << self
|
|
13
|
+
def field_type
|
|
14
|
+
@field_type ||= "datetime-local"
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
def format_datetime(value)
|
|
20
|
+
if @include_seconds
|
|
21
|
+
value&.strftime("%Y-%m-%dT%T")
|
|
22
|
+
else
|
|
23
|
+
value&.strftime("%Y-%m-%dT%H:%M")
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionView
|
|
4
|
+
module Helpers
|
|
5
|
+
module Tags # :nodoc:
|
|
6
|
+
class FileField < TextField # :nodoc:
|
|
7
|
+
def render
|
|
8
|
+
include_hidden = @options.delete(:include_hidden)
|
|
9
|
+
options = @options.stringify_keys
|
|
10
|
+
add_default_name_and_id(options)
|
|
11
|
+
|
|
12
|
+
if options["multiple"] && include_hidden
|
|
13
|
+
hidden_field_for_multiple_file(options) + super
|
|
14
|
+
else
|
|
15
|
+
super
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
def hidden_field_for_multiple_file(options)
|
|
21
|
+
tag("input", "name" => options["name"], "type" => "hidden", "value" => "", "autocomplete" => "off")
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|