compony 0.9.0 → 0.10.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 +25 -0
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/VERSION +1 -1
- data/compony.gemspec +4 -4
- data/doc/ComponentGenerator.html +1 -1
- data/doc/Components.html +1 -1
- data/doc/ComponentsGenerator.html +1 -1
- data/doc/Compony/Component.html +2 -2
- data/doc/Compony/ComponentMixins/Default/Labelling.html +120 -39
- data/doc/Compony/ComponentMixins/Default/Standalone/ResourcefulVerbDsl.html +1 -1
- data/doc/Compony/ComponentMixins/Default/Standalone/StandaloneDsl.html +1 -1
- data/doc/Compony/ComponentMixins/Default/Standalone/VerbDsl.html +1 -1
- data/doc/Compony/ComponentMixins/Default/Standalone.html +1 -1
- data/doc/Compony/ComponentMixins/Default.html +1 -1
- data/doc/Compony/ComponentMixins/Resourceful.html +1 -1
- data/doc/Compony/ComponentMixins.html +1 -1
- data/doc/Compony/Components/Buttons/CssButton.html +1 -1
- data/doc/Compony/Components/Buttons/Link.html +1 -1
- data/doc/Compony/Components/Buttons.html +1 -1
- data/doc/Compony/Components/Destroy.html +13 -13
- data/doc/Compony/Components/Edit.html +17 -17
- data/doc/Compony/Components/Form.html +49 -49
- data/doc/Compony/Components/Index.html +1 -1
- data/doc/Compony/Components/List.html +346 -473
- data/doc/Compony/Components/New.html +13 -13
- data/doc/Compony/Components/Show.html +28 -28
- data/doc/Compony/Components/WithForm.html +1 -1
- data/doc/Compony/Components.html +1 -1
- data/doc/Compony/ControllerMixin.html +1 -1
- data/doc/Compony/Engine.html +1 -1
- data/doc/Compony/ExposedIntentsDsl.html +1 -1
- data/doc/Compony/Intent.html +83 -67
- data/doc/Compony/ManageIntentsDsl.html +409 -0
- data/doc/Compony/MethodAccessibleHash.html +1 -1
- data/doc/Compony/ModelFields/Anchormodel.html +1 -1
- data/doc/Compony/ModelFields/Association.html +1 -1
- data/doc/Compony/ModelFields/Attachment.html +1 -1
- data/doc/Compony/ModelFields/Base.html +1 -1
- data/doc/Compony/ModelFields/Boolean.html +1 -1
- data/doc/Compony/ModelFields/Color.html +1 -1
- data/doc/Compony/ModelFields/Currency.html +1 -1
- data/doc/Compony/ModelFields/Date.html +1 -1
- data/doc/Compony/ModelFields/Datetime.html +1 -1
- data/doc/Compony/ModelFields/Decimal.html +1 -1
- data/doc/Compony/ModelFields/Email.html +1 -1
- data/doc/Compony/ModelFields/Float.html +1 -1
- data/doc/Compony/ModelFields/Integer.html +1 -1
- data/doc/Compony/ModelFields/Percentage.html +1 -1
- data/doc/Compony/ModelFields/Phone.html +1 -1
- data/doc/Compony/ModelFields/RichText.html +1 -1
- data/doc/Compony/ModelFields/String.html +1 -1
- data/doc/Compony/ModelFields/Text.html +1 -1
- data/doc/Compony/ModelFields/Time.html +1 -1
- data/doc/Compony/ModelFields/Url.html +1 -1
- data/doc/Compony/ModelFields.html +1 -1
- data/doc/Compony/ModelMixin.html +1 -1
- data/doc/Compony/NaturalOrdering.html +1 -1
- data/doc/Compony/RequestContext.html +22 -10
- data/doc/Compony/Version.html +1 -1
- data/doc/Compony/ViewHelpers.html +1 -1
- data/doc/Compony/VirtualModel.html +1 -1
- data/doc/Compony.html +8 -8
- data/doc/ComponyController.html +1 -1
- data/doc/_index.html +8 -8
- data/doc/class_list.html +1 -1
- data/doc/file.README.html +2 -2
- data/doc/guide/nesting.md +26 -4
- data/doc/index.html +2 -2
- data/doc/method_list.html +174 -182
- data/doc/top-level-namespace.html +1 -1
- data/lib/compony/component.rb +1 -1
- data/lib/compony/component_mixins/default/labelling.rb +28 -17
- data/lib/compony/components/destroy.rb +2 -5
- data/lib/compony/components/edit.rb +2 -4
- data/lib/compony/components/form.rb +0 -1
- data/lib/compony/components/index.rb +1 -1
- data/lib/compony/components/list.rb +57 -62
- data/lib/compony/components/new.rb +0 -1
- data/lib/compony/components/show.rb +4 -11
- data/lib/compony/intent.rb +11 -4
- data/lib/compony/{exposed_intents_dsl.rb → manage_intents_dsl.rb} +6 -3
- data/lib/compony/model_mixin.rb +1 -1
- data/lib/compony/request_context.rb +10 -3
- data/lib/compony.rb +4 -4
- metadata +4 -3
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
</div>
|
|
103
103
|
|
|
104
104
|
<div id="footer">
|
|
105
|
-
Generated on
|
|
105
|
+
Generated on Mon Dec 8 17:21:03 2025 by
|
|
106
106
|
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
|
107
107
|
0.9.34 (ruby-3.3.5).
|
|
108
108
|
</div>
|
data/lib/compony/component.rb
CHANGED
|
@@ -223,7 +223,7 @@ module Compony
|
|
|
223
223
|
# Build the declared intents
|
|
224
224
|
return @exposed_intents if @exposed_intents
|
|
225
225
|
@exposed_intents = NaturalOrdering.new
|
|
226
|
-
@exposed_intent_blocks.each { |block|
|
|
226
|
+
@exposed_intent_blocks.each { |block| ManageIntentsDsl.new(@exposed_intents).evaluate(&block) } # alters @exposed_intents
|
|
227
227
|
return @exposed_intents.map!(&:payload)
|
|
228
228
|
end
|
|
229
229
|
end
|
|
@@ -53,23 +53,35 @@ module Compony
|
|
|
53
53
|
end
|
|
54
54
|
end
|
|
55
55
|
|
|
56
|
-
# DSL method
|
|
57
|
-
#
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
56
|
+
# DSL method
|
|
57
|
+
# Defines defaults for intents when rendering buttons. Just like in {label}, the block may be given a resource.
|
|
58
|
+
# @param [Symbol] keyword The name of the keyword that should be given to the button by the intent if not overwritten
|
|
59
|
+
# @param [Proc] block The block that, when called in the context of the component while rendering, returns the value for the arg given to the button.
|
|
60
|
+
# @see {Compony::Component#button_defaults}
|
|
61
|
+
def button(keyword, &block)
|
|
62
|
+
fail("Please pass a block to `button` in #{inspect}.") unless block_given?
|
|
63
|
+
@button_blocks ||= {}
|
|
64
|
+
@button_blocks[keyword.to_sym] = block
|
|
64
65
|
end
|
|
65
66
|
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
67
|
+
# Executes and retrieves the button blocks
|
|
68
|
+
# If this component is resourceful, give the block the resource. Expect the arity to match.
|
|
69
|
+
# @param resource Pass the resource if and only if the component is resourceful.
|
|
70
|
+
def button_defaults(resource = nil)
|
|
71
|
+
return @button_blocks.to_h do |keyword, block|
|
|
72
|
+
value = case block.arity
|
|
73
|
+
when 0
|
|
74
|
+
block.call
|
|
75
|
+
when 1
|
|
76
|
+
resource ||= data
|
|
77
|
+
if resource.blank?
|
|
78
|
+
fail("Button block #{keyword.inspect} of #{inspect} takes a resource, but none was provided and a call to `data` did not return any.")
|
|
79
|
+
end
|
|
80
|
+
block.call(resource)
|
|
81
|
+
else
|
|
82
|
+
fail "#{inspect} has a button block #{keyword.inspect} that takes 2 or more arguments, which is unsupported."
|
|
83
|
+
end
|
|
84
|
+
next [keyword, value]
|
|
73
85
|
end
|
|
74
86
|
end
|
|
75
87
|
|
|
@@ -81,8 +93,7 @@ module Compony
|
|
|
81
93
|
long: -> { "#{I18n.t(family_name.humanize)}: #{I18n.t(comp_name.humanize)}" },
|
|
82
94
|
short: -> { I18n.t(comp_name.humanize) }
|
|
83
95
|
}
|
|
84
|
-
@
|
|
85
|
-
@color_block = -> { :primary }
|
|
96
|
+
@button_blocks = {}
|
|
86
97
|
end
|
|
87
98
|
end
|
|
88
99
|
end
|
|
@@ -23,8 +23,6 @@ module Compony
|
|
|
23
23
|
|
|
24
24
|
label(:long) { |data| I18n.t('compony.components.destroy.label.long', data_label: data.label) }
|
|
25
25
|
label(:short) { |_| I18n.t('compony.components.destroy.label.short') }
|
|
26
|
-
icon { :trash }
|
|
27
|
-
color { :danger }
|
|
28
26
|
|
|
29
27
|
content :confirm_question, hidden: true do
|
|
30
28
|
div I18n.t('compony.components.destroy.confirm_question', data_label: @data.label)
|
|
@@ -47,9 +45,8 @@ module Compony
|
|
|
47
45
|
exposed_intents do
|
|
48
46
|
if data_class.owner_model_attr
|
|
49
47
|
add :show, @data.send(data_class.owner_model_attr),
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
name: :back_to_owner
|
|
48
|
+
label: I18n.t('compony.cancel'),
|
|
49
|
+
name: :back_to_owner
|
|
53
50
|
end
|
|
54
51
|
end
|
|
55
52
|
|
|
@@ -29,16 +29,14 @@ module Compony
|
|
|
29
29
|
|
|
30
30
|
label(:long) { |data| I18n.t('compony.components.edit.label.long', data_label: data.label) }
|
|
31
31
|
label(:short) { |_| I18n.t('compony.components.edit.label.short') }
|
|
32
|
-
icon { :pencil }
|
|
33
32
|
|
|
34
33
|
form_cancancan_action :edit
|
|
35
34
|
|
|
36
35
|
exposed_intents do
|
|
37
36
|
if data_class.owner_model_attr
|
|
38
37
|
add :show, @data.send(data_class.owner_model_attr),
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
name: :back_to_owner
|
|
38
|
+
label: I18n.t('compony.cancel'),
|
|
39
|
+
name: :back_to_owner
|
|
42
40
|
end
|
|
43
41
|
end
|
|
44
42
|
|
|
@@ -28,7 +28,6 @@ module Compony
|
|
|
28
28
|
# Fake submit button rendered by a button component and submitting the form via JS:
|
|
29
29
|
concat Compony.button_component_class.new(
|
|
30
30
|
label: @submit_label || I18n.t('compony.components.form.submit'),
|
|
31
|
-
icon: 'arrow-right',
|
|
32
31
|
href: '#',
|
|
33
32
|
onclick: "this.closest('form').requestSubmit(); return false;"
|
|
34
33
|
).render(controller)
|
|
@@ -25,19 +25,25 @@ module Compony
|
|
|
25
25
|
skip_sorting_in_filter: false,
|
|
26
26
|
skip_sorting_links: false,
|
|
27
27
|
skip_columns: [],
|
|
28
|
-
|
|
28
|
+
skip_row_intents: [],
|
|
29
29
|
skip_filters: [],
|
|
30
30
|
default_sorting: 'id asc',
|
|
31
31
|
**)
|
|
32
|
-
@pagination =
|
|
32
|
+
@pagination = true
|
|
33
|
+
@skip_pagination = !!skip_pagination
|
|
33
34
|
@results_per_page = results_per_page
|
|
34
|
-
@filtering =
|
|
35
|
-
@
|
|
36
|
-
@
|
|
35
|
+
@filtering = true
|
|
36
|
+
@skip_filtering = !!skip_filtering
|
|
37
|
+
@sorting = true
|
|
38
|
+
@sorting_in_filter = true
|
|
39
|
+
@sorting_links = true
|
|
40
|
+
@skip_sorting_in_filter = !!skip_sorting || !!skip_sorting_in_filter
|
|
41
|
+
@skip_sorting_links = !!skip_sorting || !!skip_sorting_links
|
|
37
42
|
@columns = Compony::NaturalOrdering.new
|
|
38
|
-
@
|
|
43
|
+
@row_intent_blocks = []
|
|
39
44
|
@skipped_columns = skip_columns.map(&:to_sym)
|
|
40
|
-
@
|
|
45
|
+
@skipped_row_intents = skip_row_intents.is_a?(Enumerable) ? skip_row_intents.map(&:to_sym) : []
|
|
46
|
+
@skip_row_intents = skip_row_intents.is_a?(TrueClass)
|
|
41
47
|
@filters = Compony::NaturalOrdering.new
|
|
42
48
|
@sorts = Compony::NaturalOrdering.new
|
|
43
49
|
@skipped_filters = skip_filters.map(&:to_sym)
|
|
@@ -50,9 +56,9 @@ module Compony
|
|
|
50
56
|
end
|
|
51
57
|
|
|
52
58
|
# DSL method
|
|
53
|
-
#
|
|
54
|
-
def
|
|
55
|
-
@pagination =
|
|
59
|
+
# Enables or disables pagination (caution: all records will be loaded).
|
|
60
|
+
def pagination(new_pagination)
|
|
61
|
+
@pagination = !!new_pagination
|
|
56
62
|
end
|
|
57
63
|
|
|
58
64
|
# DSL method
|
|
@@ -62,28 +68,28 @@ module Compony
|
|
|
62
68
|
end
|
|
63
69
|
|
|
64
70
|
# DSL method
|
|
65
|
-
#
|
|
66
|
-
def
|
|
67
|
-
@filtering =
|
|
71
|
+
# Enables or disables filtering entirely (sorting is independent of this setting).
|
|
72
|
+
def filtering(new_filtering)
|
|
73
|
+
@filtering = !!new_filtering
|
|
68
74
|
end
|
|
69
75
|
|
|
70
76
|
# DSL method
|
|
71
|
-
#
|
|
72
|
-
def
|
|
73
|
-
@sorting_in_filter =
|
|
74
|
-
@sorting_links =
|
|
77
|
+
# Enables or disables sorting entirely (both links and sorting input in filter).
|
|
78
|
+
def sorting(new_sorting)
|
|
79
|
+
@sorting_in_filter = !!new_sorting
|
|
80
|
+
@sorting_links = !!new_sorting
|
|
75
81
|
end
|
|
76
82
|
|
|
77
83
|
# DSL method
|
|
78
|
-
#
|
|
79
|
-
def
|
|
80
|
-
@sorting_in_filter =
|
|
84
|
+
# Enables or disables sorting in filter.
|
|
85
|
+
def sorting_in_filter(new_sorting_in_filter)
|
|
86
|
+
@sorting_in_filter = !!new_sorting_in_filter
|
|
81
87
|
end
|
|
82
88
|
|
|
83
89
|
# DSL method
|
|
84
|
-
#
|
|
85
|
-
def
|
|
86
|
-
@sorting_links =
|
|
90
|
+
# Enables or disables sorting links.
|
|
91
|
+
def sorting_links(new_sorting_links)
|
|
92
|
+
@sorting_links = !!new_sorting_links
|
|
87
93
|
end
|
|
88
94
|
|
|
89
95
|
# DSL method
|
|
@@ -157,28 +163,18 @@ module Compony
|
|
|
157
163
|
end
|
|
158
164
|
|
|
159
165
|
# DSL method
|
|
160
|
-
#
|
|
161
|
-
#
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
block
|
|
170
|
-
|
|
171
|
-
render_intent(name, record, **{ label: { format: :short } }.deep_merge(button_opts))
|
|
172
|
-
end
|
|
166
|
+
# If a block is given: Enters the DSL where row intents can be added or removed (use from {Component#setup} within the component).
|
|
167
|
+
# If no block is given: Builds the declared intents for the given record and returns them (use in `content` or `before_render`, pass kwarg `:data`).
|
|
168
|
+
def row_intents(**intent_opts, &block)
|
|
169
|
+
if block_given?
|
|
170
|
+
# Enter DSL
|
|
171
|
+
@row_intent_blocks << block
|
|
172
|
+
else
|
|
173
|
+
# Build the declared intents
|
|
174
|
+
intents_ordering = NaturalOrdering.new
|
|
175
|
+
@row_intent_blocks.each { |block| ManageIntentsDsl.new(intents_ordering, **intent_opts).evaluate(&block) } # this populates intents_ordering
|
|
176
|
+
return intents_ordering.map!(&:payload)
|
|
173
177
|
end
|
|
174
|
-
@row_actions.natural_push(name, block, **)
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
# DSL method
|
|
178
|
-
# Marks a single row action as skipped. It will not be displayed, even if it is defined.
|
|
179
|
-
# @param name [Symbol,String] Name of the row action to be skipped.
|
|
180
|
-
def skip_row_action(name)
|
|
181
|
-
@skipped_row_actions << name.to_sym
|
|
182
178
|
end
|
|
183
179
|
|
|
184
180
|
# DSL method
|
|
@@ -237,7 +233,7 @@ module Compony
|
|
|
237
233
|
def process_data!(controller)
|
|
238
234
|
fail('Data was already processed!') if @processed_data
|
|
239
235
|
# Filtering
|
|
240
|
-
if filtering_enabled?
|
|
236
|
+
if filtering_enabled? || sorting_enabled?
|
|
241
237
|
@q = @data.ransack(controller.params[param_name(:q)], auth_object: controller.current_ability, search_key: param_name(:q))
|
|
242
238
|
@q.sorts = @default_sorting if @q.sorts.empty?
|
|
243
239
|
filtered_data = @q.result.accessible_by(controller.current_ability)
|
|
@@ -274,10 +270,12 @@ module Compony
|
|
|
274
270
|
@data = data_class.accessible_by(controller.current_ability)
|
|
275
271
|
end
|
|
276
272
|
|
|
277
|
-
# Default row
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
273
|
+
# Default row intents
|
|
274
|
+
row_intents do
|
|
275
|
+
add :show, name: :show
|
|
276
|
+
add :edit, name: :edit
|
|
277
|
+
add :destroy, name: :destroy
|
|
278
|
+
end
|
|
281
279
|
|
|
282
280
|
before_render do
|
|
283
281
|
process_data!(controller)
|
|
@@ -336,7 +334,7 @@ module Compony
|
|
|
336
334
|
@columns.each do |column|
|
|
337
335
|
th column[:label], class: 'list-data-label'
|
|
338
336
|
end
|
|
339
|
-
|
|
337
|
+
unless @skip_row_intents
|
|
340
338
|
th I18n.t('compony.components.index.actions'), class: 'list-actions-label'
|
|
341
339
|
end
|
|
342
340
|
end
|
|
@@ -349,14 +347,11 @@ module Compony
|
|
|
349
347
|
instance_exec(record, &column[:payload])
|
|
350
348
|
end
|
|
351
349
|
end
|
|
352
|
-
|
|
353
|
-
next if @skipped_row_actions.include?(row_action[:name])
|
|
354
|
-
next instance_exec(record, &row_action[:payload])
|
|
355
|
-
end.compact
|
|
356
|
-
if rendered_row_actions.any?
|
|
350
|
+
unless @skip_row_intents
|
|
357
351
|
td do
|
|
358
|
-
|
|
359
|
-
|
|
352
|
+
row_intents(data: record, label: { format: :short }, button: { data: { 'turbo-frame': :_top } }).each do |row_intent|
|
|
353
|
+
next if @skipped_row_intents.include?(row_intent.name)
|
|
354
|
+
concat row_intent.render(controller)
|
|
360
355
|
end
|
|
361
356
|
end
|
|
362
357
|
end
|
|
@@ -390,27 +385,27 @@ module Compony
|
|
|
390
385
|
|
|
391
386
|
# Returns whether filtering is possible and wanted in general (regardless of whether there are any filters defined)
|
|
392
387
|
def filtering_enabled?
|
|
393
|
-
@filtering && defined?(Ransack)
|
|
388
|
+
@filtering && defined?(Ransack) && !@skip_filtering
|
|
394
389
|
end
|
|
395
390
|
|
|
396
391
|
# Returns whether sorting is possible and wanted in general (regardless of whether there are any sorts defined)
|
|
397
392
|
def sorting_enabled?
|
|
398
|
-
(@sorting_in_filter || @sorting_links) && defined?(Ransack)
|
|
393
|
+
((@sorting_in_filter && !@skip_sorting_in_filter) || (@sorting_links && !@skip_sorting_links)) && defined?(Ransack)
|
|
399
394
|
end
|
|
400
395
|
|
|
401
396
|
# Returns whether sorting in filter is possible and wanted in general (regardless of whether there are any sorts defined)
|
|
402
397
|
def sorting_in_filter_enabled?
|
|
403
|
-
sorting_enabled? && @sorting_in_filter
|
|
398
|
+
sorting_enabled? && @sorting_in_filter && !@skip_sorting_in_filter
|
|
404
399
|
end
|
|
405
400
|
|
|
406
401
|
# Returns whether generating sorting links is possible and wanted in general (regardless of whether there are any sorts defined)
|
|
407
402
|
def sorting_links_enabled?
|
|
408
|
-
sorting_enabled? && @sorting_links
|
|
403
|
+
sorting_enabled? && @sorting_links && !@skip_sorting_links
|
|
409
404
|
end
|
|
410
405
|
|
|
411
406
|
# Returns whether pagination is enabled (regardless of whether there is more than one page)
|
|
412
407
|
def pagination_enabled?
|
|
413
|
-
@pagination
|
|
408
|
+
@pagination && !@skip_pagination
|
|
414
409
|
end
|
|
415
410
|
|
|
416
411
|
# Returns the select options for sorting suitable for passing in a `f.select`. Used in sorting-in-filter feature. Useful for custom subclasses of List.
|
|
@@ -14,23 +14,16 @@ module Compony
|
|
|
14
14
|
|
|
15
15
|
label(:long) { |data| data.label } # rubocop:disable Style/SymbolProc
|
|
16
16
|
label(:short) { |_| I18n.t('compony.components.show.label.short') }
|
|
17
|
-
icon { :eye }
|
|
18
17
|
|
|
19
18
|
exposed_intents do
|
|
20
19
|
if data_class.owner_model_attr
|
|
21
20
|
add :show, @data.send(data_class.owner_model_attr),
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
name: :back_to_owner
|
|
21
|
+
label: I18n.t('compony.back'),
|
|
22
|
+
name: :back_to_owner
|
|
25
23
|
end
|
|
26
24
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
if Compony.comp_class_for(:destroy, family_name)
|
|
32
|
-
add :destroy, @data, label: { format: :short }, name: :destroy
|
|
33
|
-
end
|
|
25
|
+
add :edit, @data, label: { format: :short }, name: :edit
|
|
26
|
+
add :destroy, @data, label: { format: :short }, name: :destroy
|
|
34
27
|
end
|
|
35
28
|
|
|
36
29
|
content :label do
|
data/lib/compony/intent.rb
CHANGED
|
@@ -23,7 +23,7 @@ module Compony
|
|
|
23
23
|
# @param data_class [Class] If given, the target component will be instanciated with this argument.
|
|
24
24
|
# @param feasibility_target [ApplicationRecord] If given, will override the feasibility target (prevention framework)
|
|
25
25
|
# @param feasibility_action [ApplicationRecord] If given, will override the feasibility action (prevention framework)
|
|
26
|
-
def initialize(comp_name_or_cst_or_class,
|
|
26
|
+
def initialize(comp_name_or_cst_or_class = nil,
|
|
27
27
|
model_or_family_name_or_cst = nil,
|
|
28
28
|
standalone_name: nil,
|
|
29
29
|
name: nil,
|
|
@@ -42,7 +42,11 @@ module Compony
|
|
|
42
42
|
@data_class = data_class
|
|
43
43
|
|
|
44
44
|
# Figure out comp_class
|
|
45
|
-
if comp_name_or_cst_or_class.
|
|
45
|
+
if comp_name_or_cst_or_class.nil?
|
|
46
|
+
if name.blank? || !label.is_a?(String)
|
|
47
|
+
fail('An intent created without positional arguments must be given the kwargs `name:`, `label` (String).')
|
|
48
|
+
end
|
|
49
|
+
elsif comp_name_or_cst_or_class.is_a?(Class) && (comp_name_or_cst_or_class <= Compony::Component)
|
|
46
50
|
# A class was given as the first argument
|
|
47
51
|
@comp_class = comp_name_or_cst_or_class
|
|
48
52
|
else
|
|
@@ -75,6 +79,7 @@ module Compony
|
|
|
75
79
|
# Instanciates the component and returns the instance. If `data` and/or `data_class` were specified when instantiating this intent, they are passed.
|
|
76
80
|
# All given arguments will be given to the component's initializer, also overriding `data` and `data_class` if present.
|
|
77
81
|
def comp(*, **)
|
|
82
|
+
return nil if @comp_class.nil?
|
|
78
83
|
return @comp ||= @comp_class.new(*, data: @data, data_class: @data_class, **)
|
|
79
84
|
end
|
|
80
85
|
|
|
@@ -83,6 +88,8 @@ module Compony
|
|
|
83
88
|
# @param model [ApplicationRecord] If given and non-nil, will override the model passed to the component's path block
|
|
84
89
|
# @param standalone_name [Symbol] If given and non-nil, will override the `standalone_name` passed to the component's path block
|
|
85
90
|
def path(model = nil, *, standalone_name: nil, **path_opt_overrides)
|
|
91
|
+
return @path if @path.present?
|
|
92
|
+
return nil if @comp_class.nil?
|
|
86
93
|
path_opts = @path_opts.deep_merge(path_opt_overrides)
|
|
87
94
|
comp.path(model || (model? ? @data : nil), standalone_name: standalone_name || @standalone_name, **path_opts)
|
|
88
95
|
end
|
|
@@ -135,10 +142,10 @@ module Compony
|
|
|
135
142
|
# @param button_arg_overrides [Hash] Any further kwargs are passed to the button component's initializer.
|
|
136
143
|
def render(controller, parent_comp = nil, style: nil, **button_arg_overrides)
|
|
137
144
|
# Abort if not authorized
|
|
138
|
-
return nil
|
|
145
|
+
return nil if comp && !comp.standalone_access_permitted_for?(controller, standalone_name: @standalone_name, verb: method)
|
|
139
146
|
# Prepare opts
|
|
140
147
|
button_comp_class ||= Compony.button_component_class(*[style || @style].compact)
|
|
141
|
-
button_opts = button_comp_opts.merge(button_arg_overrides)
|
|
148
|
+
button_opts = (comp&.button_defaults || {}).merge(button_comp_opts).merge(button_arg_overrides) # overrides go right to left
|
|
142
149
|
# Perform render
|
|
143
150
|
if parent_comp
|
|
144
151
|
return parent_comp.sub_comp(button_comp_class, **button_opts).render(controller)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
module Compony
|
|
2
|
-
class
|
|
3
|
-
def initialize(previously_exposed_intents)
|
|
2
|
+
class ManageIntentsDsl < Dslblend::Base
|
|
3
|
+
def initialize(previously_exposed_intents, **intent_opts)
|
|
4
4
|
super()
|
|
5
5
|
@exposed_intents = previously_exposed_intents
|
|
6
|
+
@intent_opts = intent_opts
|
|
6
7
|
end
|
|
7
8
|
|
|
8
9
|
protected
|
|
@@ -12,8 +13,10 @@ module Compony
|
|
|
12
13
|
# Intents specified this way can be retrieved and rendered by the parent component or by calling `root_intents` in case of standalone access.
|
|
13
14
|
# @param [Symbol] before If specified, will insert the intent before the other. When replacing, an element keeps its position unless `before:`` is passed.
|
|
14
15
|
def add(*, before: nil, **)
|
|
15
|
-
intent = Compony.intent(*, **)
|
|
16
|
+
intent = Compony.intent(*, **@intent_opts, **)
|
|
16
17
|
@exposed_intents.natural_push(intent.name, intent, before:)
|
|
18
|
+
rescue NameError # Ignore if the component is not actually defined
|
|
19
|
+
return nil
|
|
17
20
|
end
|
|
18
21
|
|
|
19
22
|
# DSL method
|
data/lib/compony/model_mixin.rb
CHANGED
|
@@ -71,7 +71,7 @@ module Compony
|
|
|
71
71
|
return if autodetect_feasibilities_completed
|
|
72
72
|
# Add a prevention that reflects the `has_many` `dependent' properties. Avoids that users can press buttons that will result in a failed destroy.
|
|
73
73
|
reflect_on_all_associations.select { |assoc| %i[restrict_with_exception restrict_with_error].include? assoc.options[:dependent] }.each do |assoc|
|
|
74
|
-
prevent(:destroy, I18n.t('compony.feasibility.has_dependent_models', dependent_class:
|
|
74
|
+
prevent(:destroy, I18n.t('compony.feasibility.has_dependent_models', dependent_class: assoc.klass.model_name.human(count: 2))) do
|
|
75
75
|
if assoc.is_a? ActiveRecord::Reflection::HasOneReflection
|
|
76
76
|
!public_send(assoc.name).nil?
|
|
77
77
|
else
|
|
@@ -78,9 +78,16 @@ module Compony
|
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
# View helper that instanciates a sub comp and renders it.
|
|
81
|
-
# Example usage: `concat render_sub_comp(
|
|
82
|
-
|
|
83
|
-
|
|
81
|
+
# Example usage: `concat render_sub_comp(:list, @data.belongings)`
|
|
82
|
+
# If the parameter `turbo_frame` is given, the sub comp is rendered inside a hotwire turbo frame. This is useful when having forms in multiple nested comps.
|
|
83
|
+
def render_sub_comp(*, turbo_frame: nil, **)
|
|
84
|
+
if turbo_frame
|
|
85
|
+
turbo_frame_tag(turbo_frame.to_sym) do
|
|
86
|
+
concat sub_comp(*, **).render(controller)
|
|
87
|
+
end
|
|
88
|
+
else
|
|
89
|
+
sub_comp(*, **).render(controller)
|
|
90
|
+
end
|
|
84
91
|
end
|
|
85
92
|
end
|
|
86
93
|
end
|
data/lib/compony.rb
CHANGED
|
@@ -147,9 +147,9 @@ module Compony
|
|
|
147
147
|
# Same as Compony#comp_class_for but fails if none found
|
|
148
148
|
# @see Intent for allowed parameters.
|
|
149
149
|
# @see Compony#comp_class_for
|
|
150
|
-
def self.comp_class_for!(
|
|
151
|
-
comp_class_for(
|
|
152
|
-
"No component found for
|
|
150
|
+
def self.comp_class_for!(*args, **kwargs)
|
|
151
|
+
comp_class_for(*args, **kwargs) || fail(
|
|
152
|
+
"No component found for #{args.inspect}, #{kwargs.inspect}"
|
|
153
153
|
)
|
|
154
154
|
end
|
|
155
155
|
|
|
@@ -218,7 +218,7 @@ require 'compony/component_mixins/default/standalone/verb_dsl'
|
|
|
218
218
|
require 'compony/component_mixins/default/standalone/resourceful_verb_dsl'
|
|
219
219
|
require 'compony/component_mixins/default/labelling'
|
|
220
220
|
require 'compony/component_mixins/resourceful'
|
|
221
|
-
require 'compony/
|
|
221
|
+
require 'compony/manage_intents_dsl'
|
|
222
222
|
require 'compony/component'
|
|
223
223
|
require 'compony/components/buttons/link'
|
|
224
224
|
require 'compony/components/buttons/css_button'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: compony
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.10.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sandro Kalbermatter
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2025-
|
|
12
|
+
date: 2025-12-08 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: yard
|
|
@@ -218,6 +218,7 @@ files:
|
|
|
218
218
|
- doc/Compony/Engine.html
|
|
219
219
|
- doc/Compony/ExposedIntentsDsl.html
|
|
220
220
|
- doc/Compony/Intent.html
|
|
221
|
+
- doc/Compony/ManageIntentsDsl.html
|
|
221
222
|
- doc/Compony/MethodAccessibleHash.html
|
|
222
223
|
- doc/Compony/ModelFields.html
|
|
223
224
|
- doc/Compony/ModelFields/Anchormodel.html
|
|
@@ -312,8 +313,8 @@ files:
|
|
|
312
313
|
- lib/compony/components/with_form.rb
|
|
313
314
|
- lib/compony/controller_mixin.rb
|
|
314
315
|
- lib/compony/engine.rb
|
|
315
|
-
- lib/compony/exposed_intents_dsl.rb
|
|
316
316
|
- lib/compony/intent.rb
|
|
317
|
+
- lib/compony/manage_intents_dsl.rb
|
|
317
318
|
- lib/compony/method_accessible_hash.rb
|
|
318
319
|
- lib/compony/model_fields/anchormodel.rb
|
|
319
320
|
- lib/compony/model_fields/association.rb
|