omg-actionview 8.0.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|