stimulus_plumbers 0.2.9 → 0.3.1
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.md +38 -0
- data/README.md +5 -4
- data/app/assets/javascripts/stimulus-plumbers/stimulus-plumbers-controllers.es.js +246 -269
- data/app/assets/javascripts/stimulus-plumbers/stimulus-plumbers-controllers.umd.js +1 -1
- data/lib/stimulus_plumbers/components/action_list/section.rb +6 -5
- data/lib/stimulus_plumbers/components/action_list.rb +3 -3
- data/lib/stimulus_plumbers/components/avatar.rb +4 -3
- data/lib/stimulus_plumbers/components/calendar/month/turbo/days_of_month.rb +62 -41
- data/lib/stimulus_plumbers/components/calendar/month/turbo/days_of_week.rb +13 -10
- data/lib/stimulus_plumbers/components/calendar.rb +36 -13
- data/lib/stimulus_plumbers/components/card/section.rb +3 -3
- data/lib/stimulus_plumbers/components/card.rb +3 -3
- data/lib/stimulus_plumbers/components/combobox/autocomplete.rb +8 -14
- data/lib/stimulus_plumbers/components/combobox/date.rb +6 -2
- data/lib/stimulus_plumbers/components/combobox/dropdown.rb +6 -2
- data/lib/stimulus_plumbers/components/combobox/popover.rb +9 -5
- data/lib/stimulus_plumbers/components/combobox/time.rb +1 -1
- data/lib/stimulus_plumbers/components/combobox/trigger.rb +19 -10
- data/lib/stimulus_plumbers/components/combobox.rb +33 -23
- data/lib/stimulus_plumbers/components/date_picker/navigation.rb +14 -22
- data/lib/stimulus_plumbers/components/divider.rb +16 -0
- data/lib/stimulus_plumbers/components/icon.rb +14 -20
- data/lib/stimulus_plumbers/components/popover/builder.rb +2 -2
- data/lib/stimulus_plumbers/components/popover.rb +11 -6
- data/lib/stimulus_plumbers/configuration.rb +3 -18
- data/lib/stimulus_plumbers/engine.rb +2 -2
- data/lib/stimulus_plumbers/form/builder.rb +40 -44
- data/lib/stimulus_plumbers/form/field.rb +96 -45
- data/lib/stimulus_plumbers/form/fields/error.rb +2 -2
- data/lib/stimulus_plumbers/form/fields/fieldset.rb +54 -0
- data/lib/stimulus_plumbers/form/fields/group.rb +2 -2
- data/lib/stimulus_plumbers/form/fields/hint.rb +2 -2
- data/lib/stimulus_plumbers/form/fields/input_group.rb +25 -0
- data/lib/stimulus_plumbers/form/fields/inputs/choice.rb +69 -0
- data/lib/stimulus_plumbers/form/fields/inputs/datetime.rb +81 -0
- data/lib/stimulus_plumbers/form/fields/inputs/file.rb +22 -0
- data/lib/stimulus_plumbers/form/fields/inputs/password.rb +59 -0
- data/lib/stimulus_plumbers/form/fields/inputs/search.rb +102 -0
- data/lib/stimulus_plumbers/form/fields/inputs/select/grouped.rb +56 -0
- data/lib/stimulus_plumbers/form/fields/inputs/select/timezone.rb +59 -0
- data/lib/stimulus_plumbers/form/fields/inputs/select/weekday.rb +45 -0
- data/lib/stimulus_plumbers/form/fields/inputs/select.rb +91 -0
- data/lib/stimulus_plumbers/form/fields/inputs/submit.rb +25 -0
- data/lib/stimulus_plumbers/form/fields/inputs/text.rb +37 -0
- data/lib/stimulus_plumbers/form/fields/inputs/text_area.rb +22 -0
- data/lib/stimulus_plumbers/form/fields/label.rb +13 -9
- data/lib/stimulus_plumbers/helpers/calendar_helper.rb +1 -1
- data/lib/stimulus_plumbers/helpers/combobox_helper.rb +29 -36
- data/lib/stimulus_plumbers/helpers/divider_helper.rb +11 -0
- data/lib/stimulus_plumbers/helpers.rb +2 -0
- data/lib/stimulus_plumbers/plumber/base.rb +1 -1
- data/lib/stimulus_plumbers/plumber/dispatcher/callable_inspector.rb +19 -0
- data/lib/stimulus_plumbers/plumber/dispatcher/instance_exec.rb +35 -0
- data/lib/stimulus_plumbers/plumber/dispatcher/klass_proxy.rb +34 -0
- data/lib/stimulus_plumbers/plumber/dispatcher/method_call.rb +36 -0
- data/lib/stimulus_plumbers/plumber/dispatcher.rb +4 -87
- data/lib/stimulus_plumbers/plumber/html_options.rb +6 -5
- data/lib/stimulus_plumbers/plumber/renderer.rb +2 -2
- data/lib/stimulus_plumbers/themes/base.rb +26 -5
- data/lib/stimulus_plumbers/themes/configuration.rb +38 -0
- data/lib/stimulus_plumbers/themes/schema/form/ranges.rb +14 -0
- data/lib/stimulus_plumbers/themes/schema/icon.rb +32 -0
- data/lib/stimulus_plumbers/themes/schema/ranges.rb +1 -1
- data/lib/stimulus_plumbers/themes/schema.rb +17 -5
- data/lib/stimulus_plumbers/version.rb +1 -1
- data/lib/stimulus_plumbers.rb +4 -2
- metadata +25 -21
- data/lib/stimulus_plumbers/form/fields/choice.rb +0 -25
- data/lib/stimulus_plumbers/form/fields/combobox.rb +0 -41
- data/lib/stimulus_plumbers/form/fields/file.rb +0 -16
- data/lib/stimulus_plumbers/form/fields/password.rb +0 -55
- data/lib/stimulus_plumbers/form/fields/renderer.rb +0 -52
- data/lib/stimulus_plumbers/form/fields/search.rb +0 -54
- data/lib/stimulus_plumbers/form/fields/select.rb +0 -33
- data/lib/stimulus_plumbers/form/fields/submit.rb +0 -23
- data/lib/stimulus_plumbers/form/fields/text.rb +0 -33
- data/lib/stimulus_plumbers/form/fields/text_area.rb +0 -16
- data/lib/stimulus_plumbers/themes/tailwind/action_list.rb +0 -33
- data/lib/stimulus_plumbers/themes/tailwind/avatar.rb +0 -52
- data/lib/stimulus_plumbers/themes/tailwind/button.rb +0 -89
- data/lib/stimulus_plumbers/themes/tailwind/calendar.rb +0 -80
- data/lib/stimulus_plumbers/themes/tailwind/card.rb +0 -24
- data/lib/stimulus_plumbers/themes/tailwind/combobox.rb +0 -75
- data/lib/stimulus_plumbers/themes/tailwind/form.rb +0 -108
- data/lib/stimulus_plumbers/themes/tailwind/layout.rb +0 -25
- data/lib/stimulus_plumbers/themes/tailwind_theme.rb +0 -31
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Form
|
|
5
|
+
module Fields
|
|
6
|
+
module Inputs
|
|
7
|
+
module Select
|
|
8
|
+
def select(attribute, choices = nil, options = {}, html_options = {})
|
|
9
|
+
html_native = options.delete(:html_native) { false }
|
|
10
|
+
Field.new(@template, **options).render(
|
|
11
|
+
object,
|
|
12
|
+
attribute,
|
|
13
|
+
input_id: field_id(attribute)
|
|
14
|
+
) do |html_opts, opts, error|
|
|
15
|
+
merged_html_opts = merge_html_options(html_options, html_opts, field_theme(:form_select, error: error))
|
|
16
|
+
if html_native
|
|
17
|
+
super(attribute, choices, opts, merged_html_opts)
|
|
18
|
+
else
|
|
19
|
+
render_select_dropdown(attribute, opts, merged_html_opts, err: error) { Array(choices) }
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def collection_select(
|
|
25
|
+
attribute,
|
|
26
|
+
collection,
|
|
27
|
+
value_method,
|
|
28
|
+
text_method,
|
|
29
|
+
options = {},
|
|
30
|
+
html_options = {}
|
|
31
|
+
)
|
|
32
|
+
html_native = options.delete(:html_native) { false }
|
|
33
|
+
Field.new(@template, **options).render(
|
|
34
|
+
object,
|
|
35
|
+
attribute,
|
|
36
|
+
input_id: field_id(attribute)
|
|
37
|
+
) do |html_opts, opts, error|
|
|
38
|
+
merged_html_opts = merge_html_options(html_options, html_opts, field_theme(:form_select, error: error))
|
|
39
|
+
if html_native
|
|
40
|
+
super(attribute, collection, value_method, text_method, opts, merged_html_opts)
|
|
41
|
+
else
|
|
42
|
+
render_select_dropdown(attribute, opts, merged_html_opts, err: error) do
|
|
43
|
+
collection.map { |item| [item.public_send(text_method), item.public_send(value_method)] }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def render_select_dropdown(attribute, opts, html_opts, err:)
|
|
52
|
+
include_blank = opts.delete(:include_blank)
|
|
53
|
+
prompt = opts.delete(:prompt)
|
|
54
|
+
current_value = opts.delete(:selected) { object.respond_to?(attribute) ? object.public_send(attribute) : nil }
|
|
55
|
+
choices = build_select_dropdown_choices(yield(current_value), include_blank: include_blank, prompt: prompt)
|
|
56
|
+
|
|
57
|
+
combobox_opts = build_select_dropdown_opts(html_opts, current_value)
|
|
58
|
+
render_combobox(attribute, input_id: html_opts[:id], opts: combobox_opts, err: err) do
|
|
59
|
+
Components::Combobox::Dropdown.new(@template).render(
|
|
60
|
+
options: choices,
|
|
61
|
+
value: current_value,
|
|
62
|
+
labelledby: Field.label_id(html_opts[:id])
|
|
63
|
+
)
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def build_select_dropdown_opts(html_opts, current_value)
|
|
68
|
+
Components::Combobox::Dropdown.default_opts.deep_merge(
|
|
69
|
+
input: { value: current_value },
|
|
70
|
+
trigger: html_opts
|
|
71
|
+
)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def build_select_dropdown_choices(choices, include_blank:, prompt:)
|
|
75
|
+
return choices unless include_blank || prompt
|
|
76
|
+
|
|
77
|
+
choices = choices.dup
|
|
78
|
+
choices.unshift([include_blank.is_a?(String) ? include_blank : "", ""]) if include_blank
|
|
79
|
+
choices.unshift(build_select_dropdown_choice_prompt(prompt)) if prompt
|
|
80
|
+
choices
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def build_select_dropdown_choice_prompt(prompt)
|
|
84
|
+
label = prompt.is_a?(String) ? prompt : I18n.t("helpers.select.prompt", default: "Please select")
|
|
85
|
+
[label, "", { disabled: true }]
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Form
|
|
5
|
+
module Fields
|
|
6
|
+
module Inputs
|
|
7
|
+
module Submit
|
|
8
|
+
def submit(value = nil, options = {})
|
|
9
|
+
if value.is_a?(Hash)
|
|
10
|
+
options = value
|
|
11
|
+
value = nil
|
|
12
|
+
end
|
|
13
|
+
value ||= submit_default_value
|
|
14
|
+
variant = options.delete(:variant) { :default }
|
|
15
|
+
@template.tag.input(
|
|
16
|
+
type: "submit",
|
|
17
|
+
value: value,
|
|
18
|
+
**merge_html_options(field_theme(:form_submit, variant: variant), options)
|
|
19
|
+
)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Form
|
|
5
|
+
module Fields
|
|
6
|
+
module Inputs
|
|
7
|
+
module Text
|
|
8
|
+
FIELD_TYPES = %i[
|
|
9
|
+
color_field
|
|
10
|
+
datetime_local_field
|
|
11
|
+
email_field
|
|
12
|
+
month_field
|
|
13
|
+
number_field
|
|
14
|
+
range_field
|
|
15
|
+
telephone_field
|
|
16
|
+
text_field
|
|
17
|
+
url_field
|
|
18
|
+
week_field
|
|
19
|
+
].freeze
|
|
20
|
+
|
|
21
|
+
FIELD_TYPES.each do |method_name|
|
|
22
|
+
define_method(method_name) do |attribute, options = {}|
|
|
23
|
+
Field.new(@template, **options).render(
|
|
24
|
+
object,
|
|
25
|
+
attribute,
|
|
26
|
+
input_id: field_id(attribute)
|
|
27
|
+
) do |html_opts, opts, error|
|
|
28
|
+
html_options = merge_html_options(opts, html_opts, field_theme(:form_input, error: error))
|
|
29
|
+
super(attribute, html_options)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Form
|
|
5
|
+
module Fields
|
|
6
|
+
module Inputs
|
|
7
|
+
module TextArea
|
|
8
|
+
def text_area(attribute, options = {})
|
|
9
|
+
Field.new(@template, **options).render(
|
|
10
|
+
object,
|
|
11
|
+
attribute,
|
|
12
|
+
input_id: field_id(attribute)
|
|
13
|
+
) do |html_opts, opts, error|
|
|
14
|
+
html_options = merge_html_options(opts, html_opts, field_theme(:form_textarea, error: error))
|
|
15
|
+
super(attribute, html_options)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -4,16 +4,20 @@ module StimulusPlumbers
|
|
|
4
4
|
module Form
|
|
5
5
|
module Fields
|
|
6
6
|
class Label < Plumber::Base
|
|
7
|
-
def render(text:, for_id
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
7
|
+
def render(text:, for_id: nil, id: nil, required: false, hidden: false, tag: :label)
|
|
8
|
+
mark_options = required && merge_html_options(
|
|
9
|
+
{ aria: { hidden: true } },
|
|
10
|
+
theme.resolve(:form_required_mark)
|
|
11
|
+
)
|
|
12
|
+
html_options = merge_html_options(theme.resolve(:form_label, required: required, hidden: hidden))
|
|
13
|
+
template.content_tag(tag, for: for_id, id: id, **html_options) do
|
|
14
|
+
template.safe_join(
|
|
15
|
+
[
|
|
16
|
+
text,
|
|
17
|
+
mark_options ? template.content_tag(:span, "*", **mark_options) : nil
|
|
18
|
+
]
|
|
19
|
+
)
|
|
14
20
|
end
|
|
15
|
-
|
|
16
|
-
template.content_tag(:label, inner, for: for_id, class: klass.presence)
|
|
17
21
|
end
|
|
18
22
|
end
|
|
19
23
|
end
|
|
@@ -5,7 +5,7 @@ module StimulusPlumbers
|
|
|
5
5
|
module CalendarHelper
|
|
6
6
|
def sp_calendar_month(date: nil, **html_options, &block)
|
|
7
7
|
if date
|
|
8
|
-
html_options.deep_merge
|
|
8
|
+
html_options = html_options.deep_merge(
|
|
9
9
|
data: {
|
|
10
10
|
"calendar-month-year-value": date.year,
|
|
11
11
|
"calendar-month-month-value": date.month - 1,
|
|
@@ -4,70 +4,63 @@ module StimulusPlumbers
|
|
|
4
4
|
module Helpers
|
|
5
5
|
module ComboboxHelper
|
|
6
6
|
def sp_combobox_date(label: nil, value: nil, **html_options)
|
|
7
|
+
id = sp_dom_id
|
|
7
8
|
opts = Components::Combobox::Date.default_opts.deep_merge(
|
|
8
9
|
input: { value: value },
|
|
9
|
-
|
|
10
|
+
trigger: { id: id, aria_label: label }.compact
|
|
10
11
|
)
|
|
11
|
-
opts = opts.deep_merge(trigger: { aria_label: label }) if label
|
|
12
12
|
Components::Combobox.new(self).render(
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
data: { input_format_type_value: "date" },
|
|
13
|
+
**opts,
|
|
14
|
+
data: { input_format_type_value: "date" },
|
|
16
15
|
**html_options
|
|
17
|
-
)
|
|
16
|
+
) do |popover_id|
|
|
17
|
+
Components::Combobox::Date.new(self).render(value: value, popover_id: popover_id)
|
|
18
|
+
end
|
|
18
19
|
end
|
|
19
20
|
|
|
20
21
|
def sp_combobox_dropdown(label: nil, options: [], value: nil, **html_options)
|
|
22
|
+
id = sp_dom_id
|
|
21
23
|
opts = Components::Combobox::Dropdown.default_opts.deep_merge(
|
|
22
24
|
input: { value: value },
|
|
23
|
-
|
|
24
|
-
)
|
|
25
|
-
opts = opts.deep_merge(trigger: { aria_label: label }) if label
|
|
26
|
-
Components::Combobox.new(self).render(
|
|
27
|
-
base_id: sp_dom_id,
|
|
28
|
-
options: opts,
|
|
29
|
-
**html_options
|
|
25
|
+
trigger: { id: id, aria_label: label }.compact
|
|
30
26
|
)
|
|
27
|
+
Components::Combobox.new(self).render(**opts, **html_options) do
|
|
28
|
+
Components::Combobox::Dropdown.new(self).render(options: options, value: value, label: label)
|
|
29
|
+
end
|
|
31
30
|
end
|
|
32
31
|
|
|
33
32
|
def sp_combobox_autocomplete(label: nil, options: [], value: nil, url: nil, **html_options)
|
|
34
|
-
id
|
|
35
|
-
|
|
36
|
-
opts = Components::Combobox::Autocomplete.default_opts.deep_merge(
|
|
33
|
+
id = sp_dom_id
|
|
34
|
+
opts = Components::Combobox::Autocomplete.default_opts.deep_merge(
|
|
37
35
|
input: { value: value },
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
options: options,
|
|
41
|
-
value: value,
|
|
42
|
-
label: label
|
|
43
|
-
),
|
|
44
|
-
data: url ? { combobox_dropdown_url_value: url } : {}
|
|
45
|
-
}
|
|
36
|
+
trigger: { id: id, aria_label: label }.compact,
|
|
37
|
+
popover: { data: url ? { combobox_dropdown_url_value: url } : {} }
|
|
46
38
|
)
|
|
47
|
-
opts = opts.deep_merge(trigger: { aria_label: label }) if label
|
|
48
39
|
Components::Combobox.new(self).render(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
input_combobox_combobox_dropdown_outlet: "##{popover_id}",
|
|
40
|
+
**opts,
|
|
41
|
+
data: {
|
|
42
|
+
input_combobox_combobox_dropdown_outlet: "##{Components::Combobox.popover_id_for(id)}",
|
|
53
43
|
action: "input->input-combobox#onInput"
|
|
54
44
|
},
|
|
55
45
|
**html_options
|
|
56
|
-
)
|
|
46
|
+
) do
|
|
47
|
+
Components::Combobox::Autocomplete.new(self).render(options: options, value: value, label: label)
|
|
48
|
+
end
|
|
57
49
|
end
|
|
58
50
|
|
|
59
51
|
def sp_combobox_time(format: :h12, label: nil, step: 1, value: nil, **html_options)
|
|
52
|
+
id = sp_dom_id
|
|
60
53
|
opts = Components::Combobox::Time.default_opts.deep_merge(
|
|
61
54
|
input: { value: value },
|
|
62
|
-
|
|
55
|
+
trigger: { id: id, aria_label: label }.compact
|
|
63
56
|
)
|
|
64
|
-
opts = opts.deep_merge(trigger: { aria_label: label }) if label
|
|
65
57
|
Components::Combobox.new(self).render(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
data: { input_format_type_value: "time", input_format_options_value: { format: format }.to_json },
|
|
58
|
+
**opts,
|
|
59
|
+
data: { input_format_type_value: "time", input_format_options_value: { format: format }.to_json },
|
|
69
60
|
**html_options
|
|
70
|
-
)
|
|
61
|
+
) do
|
|
62
|
+
Components::Combobox::Time.new(self).render(format: format, step: step, value: value)
|
|
63
|
+
end
|
|
71
64
|
end
|
|
72
65
|
end
|
|
73
66
|
end
|
|
@@ -8,6 +8,7 @@ require_relative "helpers/calendar_helper"
|
|
|
8
8
|
require_relative "helpers/calendar_turbo_helper"
|
|
9
9
|
require_relative "helpers/card_helper"
|
|
10
10
|
require_relative "helpers/combobox_helper"
|
|
11
|
+
require_relative "helpers/divider_helper"
|
|
11
12
|
require_relative "helpers/popover_helper"
|
|
12
13
|
|
|
13
14
|
module StimulusPlumbers
|
|
@@ -20,6 +21,7 @@ module StimulusPlumbers
|
|
|
20
21
|
include CalendarTurboHelper
|
|
21
22
|
include CardHelper
|
|
22
23
|
include ComboboxHelper
|
|
24
|
+
include DividerHelper
|
|
23
25
|
include PopoverHelper
|
|
24
26
|
end
|
|
25
27
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Plumber
|
|
5
|
+
module Dispatcher
|
|
6
|
+
module CallableInspector
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
def args_for(callable)
|
|
10
|
+
callable.arity.negative? ? args : args.take(callable.arity)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def accepts_kwargs?(callable)
|
|
14
|
+
callable.parameters.any? { |type, _| %i[key keyreq keyrest].include?(type) }
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Plumber
|
|
5
|
+
module Dispatcher
|
|
6
|
+
class InstanceExec
|
|
7
|
+
include CallableInspector
|
|
8
|
+
|
|
9
|
+
attr_reader :block, :args, :kwargs
|
|
10
|
+
|
|
11
|
+
def initialize(block, *args, **kwargs)
|
|
12
|
+
@block = block
|
|
13
|
+
@args = args
|
|
14
|
+
@kwargs = kwargs
|
|
15
|
+
validate!
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def call(target)
|
|
19
|
+
dispatched = args_for(block)
|
|
20
|
+
if accepts_kwargs?(block)
|
|
21
|
+
target.instance_exec(*dispatched, **kwargs, &block)
|
|
22
|
+
else
|
|
23
|
+
target.instance_exec(*dispatched, &block)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def validate!
|
|
30
|
+
raise ArgumentError, "invalid block: #{block.inspect}" unless block.is_a?(Proc)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Plumber
|
|
5
|
+
module Dispatcher
|
|
6
|
+
class KlassProxy
|
|
7
|
+
attr_reader :klass, :method_name, :args, :kwargs, :init_args, :init_kwargs
|
|
8
|
+
|
|
9
|
+
def initialize(klass, method_name, *args, init_args: [], init_kwargs: {}, **kwargs)
|
|
10
|
+
@klass = klass
|
|
11
|
+
@method_name = method_name
|
|
12
|
+
@args = args
|
|
13
|
+
@kwargs = kwargs
|
|
14
|
+
@init_args = init_args
|
|
15
|
+
@init_kwargs = init_kwargs
|
|
16
|
+
validate!
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def call(_target)
|
|
20
|
+
klass.new(*init_args, **init_kwargs).public_send(method_name, *args, **kwargs)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def validate!
|
|
26
|
+
raise ArgumentError, "invalid class: #{klass.inspect}" unless klass.is_a?(Module)
|
|
27
|
+
return if method_name.is_a?(String) || method_name.is_a?(Symbol)
|
|
28
|
+
|
|
29
|
+
raise ArgumentError, "invalid method name: #{method_name.inspect}"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module StimulusPlumbers
|
|
4
|
+
module Plumber
|
|
5
|
+
module Dispatcher
|
|
6
|
+
class MethodCall
|
|
7
|
+
include CallableInspector
|
|
8
|
+
|
|
9
|
+
attr_reader :method_name, :args, :kwargs
|
|
10
|
+
|
|
11
|
+
def initialize(method_name, *args, **kwargs)
|
|
12
|
+
@method_name = method_name
|
|
13
|
+
@args = args
|
|
14
|
+
@kwargs = kwargs
|
|
15
|
+
validate!
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def call(target)
|
|
19
|
+
raise NotImplementedError, "#{method_name.inspect} not implemented" unless target.respond_to?(method_name, true)
|
|
20
|
+
|
|
21
|
+
method_call = target.method(method_name)
|
|
22
|
+
dispatched = args_for(method_call)
|
|
23
|
+
accepts_kwargs?(method_call) ? method_call.call(*dispatched, **kwargs) : method_call.call(*dispatched)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def validate!
|
|
29
|
+
return if method_name.is_a?(String) || method_name.is_a?(Symbol)
|
|
30
|
+
|
|
31
|
+
raise ArgumentError, "invalid method name: #{method_name.inspect}"
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -1,96 +1,13 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
require_relative "dispatcher/callable_inspector"
|
|
4
|
+
require_relative "dispatcher/method_call"
|
|
5
|
+
require_relative "dispatcher/instance_exec"
|
|
6
|
+
require_relative "dispatcher/klass_proxy"
|
|
4
7
|
|
|
5
8
|
module StimulusPlumbers
|
|
6
9
|
module Plumber
|
|
7
10
|
module Dispatcher
|
|
8
|
-
class MethodCall
|
|
9
|
-
attr_reader :method_name, :args, :kwargs
|
|
10
|
-
|
|
11
|
-
def initialize(method_name, *args, **kwargs)
|
|
12
|
-
@method_name = method_name
|
|
13
|
-
@args = args
|
|
14
|
-
@kwargs = kwargs
|
|
15
|
-
validate!
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
def call(target)
|
|
19
|
-
raise NotImplementedError, "#{method_name.inspect} not implemented" unless target.respond_to?(method_name, true)
|
|
20
|
-
|
|
21
|
-
method_call = target.method(method_name)
|
|
22
|
-
accepts_args = method_call.arity.negative? ? args : args.take(method_call.arity)
|
|
23
|
-
accepts_kwargs = method_call.parameters.any? { |type, _| %i[key keyreq keyrest].include?(type) }
|
|
24
|
-
accepts_kwargs ? method_call.call(*accepts_args, **kwargs) : method_call.call(*accepts_args)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
private
|
|
28
|
-
|
|
29
|
-
def validate!
|
|
30
|
-
return if method_name.is_a?(String) || method_name.is_a?(Symbol)
|
|
31
|
-
|
|
32
|
-
raise ArgumentError, "invalid method name: #{method_name.inspect}"
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
class InstanceExec
|
|
37
|
-
attr_reader :block, :args, :kwargs
|
|
38
|
-
|
|
39
|
-
def initialize(block, *args, **kwargs)
|
|
40
|
-
@block = block
|
|
41
|
-
@args = args
|
|
42
|
-
@kwargs = kwargs
|
|
43
|
-
validate!
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def call(target)
|
|
47
|
-
accepts_args = block.arity.negative? ? args : args.take(block.arity)
|
|
48
|
-
accepts_kwargs = block.parameters.any? { |type, _| %i[key keyreq keyrest].include?(type) }
|
|
49
|
-
if accepts_kwargs
|
|
50
|
-
target.instance_exec(
|
|
51
|
-
*accepts_args,
|
|
52
|
-
**kwargs,
|
|
53
|
-
&block
|
|
54
|
-
)
|
|
55
|
-
else
|
|
56
|
-
target.instance_exec(*accepts_args, &block)
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
private
|
|
61
|
-
|
|
62
|
-
def validate!
|
|
63
|
-
raise ArgumentError, "invalid block: #{block.inspect}" unless block.is_a?(Proc)
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
class KlassProxy
|
|
68
|
-
attr_reader :klass, :method_name, :args, :kwargs, :init_args, :init_kwargs
|
|
69
|
-
|
|
70
|
-
def initialize(klass, method_name, *args, init_args: [], init_kwargs: {}, **kwargs)
|
|
71
|
-
@klass = klass
|
|
72
|
-
@method_name = method_name
|
|
73
|
-
@args = args
|
|
74
|
-
@kwargs = kwargs
|
|
75
|
-
@init_args = init_args
|
|
76
|
-
@init_kwargs = init_kwargs
|
|
77
|
-
validate!
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
def call(_target)
|
|
81
|
-
klass.new(*init_args, **init_kwargs).public_send(method_name, *args, **kwargs)
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
private
|
|
85
|
-
|
|
86
|
-
def validate!
|
|
87
|
-
raise ArgumentError, "invalid class: #{klass.inspect}" unless klass.is_a?(Module)
|
|
88
|
-
return if method_name.is_a?(String) || method_name.is_a?(Symbol)
|
|
89
|
-
|
|
90
|
-
raise ArgumentError, "invalid method name: #{method_name.inspect}"
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
11
|
def self.build(callable, *args, method_name: nil, init_args: [], init_kwargs: {}, **kwargs)
|
|
95
12
|
case callable
|
|
96
13
|
when Symbol
|
|
@@ -8,17 +8,18 @@ module StimulusPlumbers
|
|
|
8
8
|
extend ActiveSupport::Concern
|
|
9
9
|
|
|
10
10
|
def merge_html_options(*hashes)
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
class_value = merge_string_option(*extract_classes(*hashes)).presence
|
|
12
|
+
merged_data = merge_data_options(*hashes.map { |h| h[:data] || {} })
|
|
13
13
|
rest = hashes.map { |h| h.except(:class, :classes, :data) }.reduce({}, :deep_merge)
|
|
14
14
|
|
|
15
|
-
class_value = merge_string_option(*classes).presence
|
|
16
|
-
merged_data = merge_data_options(*data_hashes)
|
|
17
|
-
|
|
18
15
|
result = class_value ? rest.merge(class: class_value) : rest
|
|
19
16
|
merged_data.present? ? result.merge(data: merged_data) : result
|
|
20
17
|
end
|
|
21
18
|
|
|
19
|
+
def extract_classes(*hashes)
|
|
20
|
+
hashes.flat_map { |h| [h[:class], h[:classes]] }
|
|
21
|
+
end
|
|
22
|
+
|
|
22
23
|
STIMULUS_SPACEJOIN_KEYS = %i[controller action].freeze
|
|
23
24
|
|
|
24
25
|
def merge_data_options(*hashes, spacejoin: STIMULUS_SPACEJOIN_KEYS)
|
|
@@ -12,7 +12,7 @@ module StimulusPlumbers
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
module ClassMethods
|
|
15
|
-
# rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
15
|
+
# rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
16
16
|
def renders(method_name, with: nil, &block)
|
|
17
17
|
raise ArgumentError, "method_name must be Symbol" unless method_name.is_a?(Symbol)
|
|
18
18
|
raise ArgumentError, "provide either with: or a block" if !with.nil? && block_given?
|
|
@@ -26,7 +26,7 @@ module StimulusPlumbers
|
|
|
26
26
|
self.renderers = renderers.merge(method_name => with)
|
|
27
27
|
ActiveSupport.version >= "7.2" ? generate_renderer_method(method_name) : eval_renderer_method(method_name)
|
|
28
28
|
end
|
|
29
|
-
# rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
29
|
+
# rubocop:enable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
|
|
30
30
|
|
|
31
31
|
private
|
|
32
32
|
|