compony 0.5.6 → 0.5.8

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.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +11 -0
  3. data/Gemfile.lock +37 -29
  4. data/README.md +20 -3
  5. data/VERSION +1 -1
  6. data/compony.gemspec +3 -3
  7. data/doc/ComponentGenerator.html +20 -4
  8. data/doc/Components.html +1 -1
  9. data/doc/ComponentsGenerator.html +1 -1
  10. data/doc/Compony/Component.html +62 -322
  11. data/doc/Compony/ComponentMixins/Default/Labelling.html +1 -1
  12. data/doc/Compony/ComponentMixins/Default/Standalone/ResourcefulVerbDsl.html +1 -1
  13. data/doc/Compony/ComponentMixins/Default/Standalone/StandaloneDsl.html +1 -1
  14. data/doc/Compony/ComponentMixins/Default/Standalone/VerbDsl.html +1 -1
  15. data/doc/Compony/ComponentMixins/Default/Standalone.html +1 -1
  16. data/doc/Compony/ComponentMixins/Default.html +1 -1
  17. data/doc/Compony/ComponentMixins/Resourceful.html +1 -1
  18. data/doc/Compony/ComponentMixins.html +1 -1
  19. data/doc/Compony/Components/Button.html +2 -2
  20. data/doc/Compony/Components/Destroy.html +2 -2
  21. data/doc/Compony/Components/Edit.html +14 -14
  22. data/doc/Compony/Components/Form.html +2 -2
  23. data/doc/Compony/Components/New.html +2 -2
  24. data/doc/Compony/Components/WithForm.html +2 -2
  25. data/doc/Compony/Components.html +1 -1
  26. data/doc/Compony/ControllerMixin.html +1 -1
  27. data/doc/Compony/Engine.html +1 -1
  28. data/doc/Compony/MethodAccessibleHash.html +1 -1
  29. data/doc/Compony/ModelFields/Anchormodel.html +1 -1
  30. data/doc/Compony/ModelFields/Association.html +1 -1
  31. data/doc/Compony/ModelFields/Attachment.html +1 -1
  32. data/doc/Compony/ModelFields/Base.html +1 -1
  33. data/doc/Compony/ModelFields/Boolean.html +1 -1
  34. data/doc/Compony/ModelFields/Color.html +1 -1
  35. data/doc/Compony/ModelFields/Currency.html +1 -1
  36. data/doc/Compony/ModelFields/Date.html +1 -1
  37. data/doc/Compony/ModelFields/Datetime.html +1 -1
  38. data/doc/Compony/ModelFields/Decimal.html +1 -1
  39. data/doc/Compony/ModelFields/Email.html +1 -1
  40. data/doc/Compony/ModelFields/Float.html +1 -1
  41. data/doc/Compony/ModelFields/Integer.html +1 -1
  42. data/doc/Compony/ModelFields/Percentage.html +1 -1
  43. data/doc/Compony/ModelFields/Phone.html +1 -1
  44. data/doc/Compony/ModelFields/RichText.html +2 -2
  45. data/doc/Compony/ModelFields/String.html +1 -1
  46. data/doc/Compony/ModelFields/Text.html +1 -1
  47. data/doc/Compony/ModelFields/Time.html +1 -1
  48. data/doc/Compony/ModelFields/Url.html +1 -1
  49. data/doc/Compony/ModelFields.html +1 -1
  50. data/doc/Compony/ModelMixin.html +1 -1
  51. data/doc/Compony/NaturalOrdering.html +1 -1
  52. data/doc/Compony/RequestContext.html +3 -3
  53. data/doc/Compony/Version.html +1 -1
  54. data/doc/Compony/ViewHelpers.html +27 -13
  55. data/doc/Compony.html +1 -1
  56. data/doc/ComponyController.html +1 -1
  57. data/doc/_index.html +1 -1
  58. data/doc/file.README.html +26 -4
  59. data/doc/index.html +26 -4
  60. data/doc/method_list.html +0 -32
  61. data/doc/top-level-namespace.html +1 -1
  62. data/lib/compony/component.rb +5 -13
  63. data/lib/compony/components/edit.rb +2 -1
  64. data/lib/compony/components/new.rb +1 -1
  65. data/lib/compony/model_fields/rich_text.rb +1 -1
  66. data/lib/compony/request_context.rb +1 -1
  67. data/lib/compony/view_helpers.rb +12 -5
  68. data/lib/generators/component/component_generator.rb +8 -0
  69. data/lib/generators/component/templates/with_base_component.rb.erb +4 -0
  70. metadata +2 -1
@@ -172,7 +172,7 @@
172
172
  <li class="public ">
173
173
  <span class="summary_signature">
174
174
 
175
- <a href="#compony_link-instance_method" title="#compony_link (instance method)">#<strong>compony_link</strong>(comp_name_or_cst_or_class, model_or_family_name_or_cst = nil, *link_args, label: nil, label_opts: {}, params: {}, standalone_name: nil, **link_kwargs) &#x21d2; Object </a>
175
+ <a href="#compony_link-instance_method" title="#compony_link (instance method)">#<strong>compony_link</strong>(comp_name_or_cst_or_class, model_or_family_name_or_cst = nil, *link_args, label: nil, label_opts: {}, params: {}, feasibility_action: nil, feasibility_target: nil, standalone_name: nil, **link_kwargs) &#x21d2; Object </a>
176
176
 
177
177
 
178
178
 
@@ -280,12 +280,12 @@
280
280
  <pre class="lines">
281
281
 
282
282
 
283
- 47
284
- 48
285
- 49</pre>
283
+ 54
284
+ 55
285
+ 56</pre>
286
286
  </td>
287
287
  <td>
288
- <pre class="code"><span class="info file"># File 'lib/compony/view_helpers.rb', line 47</span>
288
+ <pre class="code"><span class="info file"># File 'lib/compony/view_helpers.rb', line 54</span>
289
289
 
290
290
  <span class='kw'>def</span> <span class='id identifier rubyid_compony_button'>compony_button</span><span class='lparen'>(</span><span class='op'>...</span><span class='rparen'>)</span>
291
291
  <span class='const'><span class='object_link'><a href="../Compony.html" title="Compony (module)">Compony</a></span></span><span class='period'>.</span><span class='id identifier rubyid_button'><span class='object_link'><a href="../Compony.html#button-class_method" title="Compony.button (method)">button</a></span></span><span class='lparen'>(</span><span class='op'>...</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_render'>render</span><span class='lparen'>(</span><span class='id identifier rubyid_helpers'>helpers</span><span class='period'>.</span><span class='id identifier rubyid_controller'>controller</span><span class='rparen'>)</span>
@@ -298,7 +298,7 @@
298
298
  <div class="method_details ">
299
299
  <h3 class="signature " id="compony_link-instance_method">
300
300
 
301
- #<strong>compony_link</strong>(comp_name_or_cst_or_class, model_or_family_name_or_cst = nil, *link_args, label: nil, label_opts: {}, params: {}, standalone_name: nil, **link_kwargs) &#x21d2; <tt>Object</tt>
301
+ #<strong>compony_link</strong>(comp_name_or_cst_or_class, model_or_family_name_or_cst = nil, *link_args, label: nil, label_opts: {}, params: {}, feasibility_action: nil, feasibility_target: nil, standalone_name: nil, **link_kwargs) &#x21d2; <tt>Object</tt>
302
302
 
303
303
 
304
304
 
@@ -429,7 +429,14 @@
429
429
  39
430
430
  40
431
431
  41
432
- 42</pre>
432
+ 42
433
+ 43
434
+ 44
435
+ 45
436
+ 46
437
+ 47
438
+ 48
439
+ 49</pre>
433
440
  </td>
434
441
  <td>
435
442
  <pre class="code"><span class="info file"># File 'lib/compony/view_helpers.rb', line 22</span>
@@ -440,6 +447,8 @@
440
447
  <span class='label'>label:</span> <span class='kw'>nil</span><span class='comma'>,</span>
441
448
  <span class='label'>label_opts:</span> <span class='lbrace'>{</span><span class='rbrace'>}</span><span class='comma'>,</span>
442
449
  <span class='label'>params:</span> <span class='lbrace'>{</span><span class='rbrace'>}</span><span class='comma'>,</span>
450
+ <span class='label'>feasibility_action:</span> <span class='kw'>nil</span><span class='comma'>,</span>
451
+ <span class='label'>feasibility_target:</span> <span class='kw'>nil</span><span class='comma'>,</span>
443
452
  <span class='label'>standalone_name:</span> <span class='kw'>nil</span><span class='comma'>,</span>
444
453
  <span class='op'>**</span><span class='id identifier rubyid_link_kwargs'>link_kwargs</span><span class='rparen'>)</span>
445
454
  <span class='id identifier rubyid_model'>model</span> <span class='op'>=</span> <span class='id identifier rubyid_model_or_family_name_or_cst'>model_or_family_name_or_cst</span><span class='period'>.</span><span class='id identifier rubyid_respond_to?'>respond_to?</span><span class='lparen'>(</span><span class='symbol'>:model_name</span><span class='rparen'>)</span> <span class='op'>?</span> <span class='id identifier rubyid_model_or_family_name_or_cst'>model_or_family_name_or_cst</span> <span class='op'>:</span> <span class='kw'>nil</span>
@@ -449,11 +458,16 @@
449
458
  <span class='id identifier rubyid_target_comp_instance'>target_comp_instance</span> <span class='op'>=</span> <span class='const'><span class='object_link'><a href="../Compony.html" title="Compony (module)">Compony</a></span></span><span class='period'>.</span><span class='id identifier rubyid_comp_class_for!'><span class='object_link'><a href="../Compony.html#comp_class_for!-class_method" title="Compony.comp_class_for! (method)">comp_class_for!</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_comp_name_or_cst_or_class'>comp_name_or_cst_or_class</span><span class='comma'>,</span> <span class='id identifier rubyid_model_or_family_name_or_cst'>model_or_family_name_or_cst</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_new'>new</span><span class='lparen'>(</span><span class='label'>data:</span> <span class='id identifier rubyid_model'>model</span><span class='rparen'>)</span>
450
459
  <span class='kw'>end</span>
451
460
  <span class='kw'>return</span> <span class='kw'>unless</span> <span class='id identifier rubyid_target_comp_instance'>target_comp_instance</span><span class='period'>.</span><span class='id identifier rubyid_standalone_access_permitted_for?'>standalone_access_permitted_for?</span><span class='lparen'>(</span><span class='kw'>self</span><span class='comma'>,</span> <span class='label'>standalone_name:</span><span class='rparen'>)</span>
452
- <span class='kw'>return</span> <span class='id identifier rubyid_helpers'>helpers</span><span class='period'>.</span><span class='id identifier rubyid_link_to'>link_to</span><span class='lparen'>(</span>
453
- <span class='id identifier rubyid_label'>label</span> <span class='op'>||</span> <span class='id identifier rubyid_target_comp_instance'>target_comp_instance</span><span class='period'>.</span><span class='id identifier rubyid_label'>label</span><span class='lparen'>(</span><span class='id identifier rubyid_model'>model</span><span class='comma'>,</span> <span class='op'>**</span><span class='id identifier rubyid_label_opts'>label_opts</span><span class='rparen'>)</span><span class='comma'>,</span>
454
- <span class='const'><span class='object_link'><a href="../Compony.html" title="Compony (module)">Compony</a></span></span><span class='period'>.</span><span class='id identifier rubyid_path'><span class='object_link'><a href="../Compony.html#path-class_method" title="Compony.path (method)">path</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_target_comp_instance'>target_comp_instance</span><span class='period'>.</span><span class='id identifier rubyid_comp_name'>comp_name</span><span class='comma'>,</span> <span class='id identifier rubyid_target_comp_instance'>target_comp_instance</span><span class='period'>.</span><span class='id identifier rubyid_family_name'>family_name</span><span class='comma'>,</span> <span class='id identifier rubyid_model'>model</span><span class='comma'>,</span> <span class='label'>standalone_name:</span><span class='comma'>,</span> <span class='op'>**</span><span class='id identifier rubyid_params'>params</span><span class='rparen'>)</span><span class='comma'>,</span>
455
- <span class='op'>*</span><span class='id identifier rubyid_link_args'>link_args</span><span class='comma'>,</span> <span class='op'>**</span><span class='id identifier rubyid_link_kwargs'>link_kwargs</span>
456
- <span class='rparen'>)</span>
461
+ <span class='id identifier rubyid_feasibility_action'>feasibility_action</span> <span class='op'>||=</span> <span class='id identifier rubyid_comp_name_or_cst_or_class'>comp_name_or_cst_or_class</span><span class='period'>.</span><span class='id identifier rubyid_to_s'>to_s</span><span class='period'>.</span><span class='id identifier rubyid_underscore'>underscore</span><span class='period'>.</span><span class='id identifier rubyid_to_sym'>to_sym</span>
462
+ <span class='id identifier rubyid_feasibility_target'>feasibility_target</span> <span class='op'>||=</span> <span class='id identifier rubyid_model'>model</span>
463
+ <span class='id identifier rubyid_label'>label</span> <span class='op'>||=</span> <span class='id identifier rubyid_target_comp_instance'>target_comp_instance</span><span class='period'>.</span><span class='id identifier rubyid_label'>label</span><span class='lparen'>(</span><span class='id identifier rubyid_model'>model</span><span class='comma'>,</span> <span class='op'>**</span><span class='id identifier rubyid_label_opts'>label_opts</span><span class='rparen'>)</span>
464
+ <span class='id identifier rubyid_path'>path</span> <span class='op'>||=</span> <span class='const'><span class='object_link'><a href="../Compony.html" title="Compony (module)">Compony</a></span></span><span class='period'>.</span><span class='id identifier rubyid_path'><span class='object_link'><a href="../Compony.html#path-class_method" title="Compony.path (method)">path</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_target_comp_instance'>target_comp_instance</span><span class='period'>.</span><span class='id identifier rubyid_comp_name'>comp_name</span><span class='comma'>,</span> <span class='id identifier rubyid_target_comp_instance'>target_comp_instance</span><span class='period'>.</span><span class='id identifier rubyid_family_name'>family_name</span><span class='comma'>,</span> <span class='id identifier rubyid_model'>model</span><span class='comma'>,</span> <span class='label'>standalone_name:</span><span class='comma'>,</span> <span class='op'>**</span><span class='id identifier rubyid_params'>params</span><span class='rparen'>)</span>
465
+ <span class='kw'>if</span> <span class='id identifier rubyid_feasibility_target'>feasibility_target</span> <span class='op'>&amp;&amp;</span> <span class='op'>!</span><span class='id identifier rubyid_feasibility_target'>feasibility_target</span><span class='period'>.</span><span class='id identifier rubyid_feasible?'>feasible?</span><span class='lparen'>(</span><span class='id identifier rubyid_feasibility_action'>feasibility_action</span><span class='rparen'>)</span>
466
+ <span class='id identifier rubyid_path'>path</span> <span class='op'>=</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>#</span><span class='tstring_end'>&#39;</span></span>
467
+ <span class='id identifier rubyid_link_kwargs'>link_kwargs</span><span class='lbracket'>[</span><span class='symbol'>:class</span><span class='rbracket'>]</span> <span class='op'>=</span> <span class='id identifier rubyid_link_kwargs'>link_kwargs</span><span class='lbracket'>[</span><span class='symbol'>:class</span><span class='rbracket'>]</span><span class='period'>.</span><span class='id identifier rubyid_is_a?'>is_a?</span><span class='lparen'>(</span><span class='const'>String</span><span class='rparen'>)</span> <span class='op'>?</span> <span class='tstring'><span class='tstring_beg'>&quot;</span><span class='embexpr_beg'>#{</span><span class='id identifier rubyid_link_kwargs'>link_kwargs</span><span class='lbracket'>[</span><span class='symbol'>:class</span><span class='rbracket'>]</span><span class='embexpr_end'>}</span><span class='tstring_content'> disabled</span><span class='tstring_end'>&quot;</span></span> <span class='op'>:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>disabled</span><span class='tstring_end'>&#39;</span></span>
468
+ <span class='id identifier rubyid_link_kwargs'>link_kwargs</span><span class='lbracket'>[</span><span class='symbol'>:title</span><span class='rbracket'>]</span> <span class='op'>=</span> <span class='id identifier rubyid_feasibility_target'>feasibility_target</span><span class='period'>.</span><span class='id identifier rubyid_full_feasibility_messages'>full_feasibility_messages</span><span class='lparen'>(</span><span class='id identifier rubyid_feasibility_action'>feasibility_action</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_presence'>presence</span>
469
+ <span class='kw'>end</span>
470
+ <span class='kw'>return</span> <span class='id identifier rubyid_helpers'>helpers</span><span class='period'>.</span><span class='id identifier rubyid_link_to'>link_to</span><span class='lparen'>(</span><span class='id identifier rubyid_label'>label</span><span class='comma'>,</span> <span class='id identifier rubyid_path'>path</span><span class='comma'>,</span> <span class='op'>*</span><span class='id identifier rubyid_link_args'>link_args</span><span class='comma'>,</span> <span class='op'>**</span><span class='id identifier rubyid_link_kwargs'>link_kwargs</span><span class='rparen'>)</span>
457
471
  <span class='kw'>end</span></pre>
458
472
  </td>
459
473
  </tr>
@@ -465,7 +479,7 @@
465
479
  </div>
466
480
 
467
481
  <div id="footer">
468
- Generated on Thu Jul 10 11:04:51 2025 by
482
+ Generated on Thu Aug 28 14:18:36 2025 by
469
483
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
470
484
  0.9.37 (ruby-3.3.5).
471
485
  </div>
data/doc/Compony.html CHANGED
@@ -2206,7 +2206,7 @@
2206
2206
  </div>
2207
2207
 
2208
2208
  <div id="footer">
2209
- Generated on Thu Jul 10 11:04:51 2025 by
2209
+ Generated on Thu Aug 28 14:18:36 2025 by
2210
2210
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
2211
2211
  0.9.37 (ruby-3.3.5).
2212
2212
  </div>
@@ -114,7 +114,7 @@
114
114
  </div>
115
115
 
116
116
  <div id="footer">
117
- Generated on Thu Jul 10 11:04:51 2025 by
117
+ Generated on Thu Aug 28 14:18:37 2025 by
118
118
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
119
119
  0.9.37 (ruby-3.3.5).
120
120
  </div>
data/doc/_index.html CHANGED
@@ -566,7 +566,7 @@
566
566
  </div>
567
567
 
568
568
  <div id="footer">
569
- Generated on Thu Jul 10 11:04:48 2025 by
569
+ Generated on Thu Aug 28 14:18:34 2025 by
570
570
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
571
571
  0.9.37 (ruby-3.3.5).
572
572
  </div>
data/doc/file.README.html CHANGED
@@ -1272,18 +1272,20 @@ my_button = Compony.button(:index, :users, enabled: -&gt; { |controller| control
1272
1272
  <span class='kw'>end</span>
1273
1273
  </code></pre>
1274
1274
 
1275
- <p><strong>Note that the feasibility framework currently only affects buttons pointing to actions, not the action itself.</strong> If a user were to issue the HTTP call manually, the component happily responds and performs the action. This is why you should always back important preventions with an appropriate Rails model validation:</p>
1275
+ <p><strong>Note that the feasibility framework currently only affects buttons/links pointing to actions, not the action itself.</strong> If a user were to issue the HTTP call manually, the component happily responds and performs the action. This is why you should always back important preventions with an appropriate Rails model validation:</p>
1276
1276
  <ul><li>
1277
1277
  <p>The Rails model validation prevents that invalid data can be saved to the database.</p>
1278
1278
  </li><li>
1279
- <p>The feasibility framework disables buttons and explains to guide the user.</p>
1279
+ <p>The feasibility framework disables buttons and links and explains to guide the user.</p>
1280
+ </li><li>
1281
+ <p>Links are disabled by changing the href to <code>&#39;#&#39;</code> and adding the <code>.disabled</code> class, which is useful when bootstrap is used.</p>
1280
1282
  </li><li>
1281
1283
  <p>Authorization is orthogonal to this, limiting the actions of a specific user.</p>
1282
1284
  </li><li>
1283
1285
  <p>If an action is both prevented and not authorized, the authorization “wins” and the action button is not shown at all.</p>
1284
1286
  </li></ul>
1285
1287
 
1286
- <p>Compony has a feature that auto-detects feasibility. In particular, it checks for <code>dependent</code> relations in the <code>has_one</code>/<code>has_many</code> relations and disables delete buttons that point to objects that have dependent objects that cannot automatically be destroyed.</p>
1288
+ <p>Compony has a feature that auto-detects feasibility of some actions. In particular, it checks for <code>dependent</code> relations in the <code>has_one</code>/<code>has_many</code> relations and disables delete buttons that point to objects that have dependent objects that cannot automatically be destroyed.</p>
1287
1289
 
1288
1290
  <p>To disable auto detection, call <code>skip_autodetect_feasibilities</code> in your model.</p>
1289
1291
 
@@ -1632,6 +1634,26 @@ my_button = Compony.button(:index, :users, enabled: -&gt; { |controller| control
1632
1634
  <p><code>rails g components Users</code> will generate a set of the most used components.</p>
1633
1635
  </li></ul>
1634
1636
 
1637
+ <h3 id="label-Support+for+custom+base+components">Support for custom base components</h3>
1638
+
1639
+ <p>If your application has many components that share the same functionality, it may be a good idea to create a custom base component, which acts as an abstract class.</p>
1640
+
1641
+ <p>Examples:</p>
1642
+ <ul><li>
1643
+ <p>Perhaps you have code shared in all of your <code>New</code> components. In this case, create <code>BaseComponents::New</code> and inherit from <code>Compony::Components::New</code>. In <code>setup</code> of your base component, you can now perform all the configurations needed. Now you may inherit from it: <code>class Components::Users::New &lt; BaseComponents::New</code>.</p>
1644
+ </li><li>
1645
+ <p>Perhaps you often implement the same kind of component, for instance an index component displaying a filterable list. In this case, create <code>BaseComponents::Index</code> and inherit as follows: <code>class Components::Users::Index &lt; BaseComponents::Index</code>.</p>
1646
+ </li></ul>
1647
+
1648
+ <p>Compony has the following convention:</p>
1649
+ <ul><li>
1650
+ <p>implement a custom base component in the directory <code>app/compony/base_components/your_component.rb</code></p>
1651
+ </li><li>
1652
+ <p>name the class <code>BaseComponents::YourComponent</code> where <code>BaseComponents</code> is typically a module simple meant for namespacing</p>
1653
+ </li></ul>
1654
+
1655
+ <p>When respecting these conventions, compony’s generators will automatically make generated classes inherit from the suitable base component if one is available. In the example above, <code>rails g component Users::Index</code> will automatically make the generated class inherit from <code>BaseComponent::Index</code>.</p>
1656
+
1635
1657
  <h2 id="label-Internal+datastructures">Internal datastructures</h2>
1636
1658
 
1637
1659
  <p>Compony has a few internal data structures that are worth mentioning. Especially when building your own UI framework on top of Compony, these might come in handy.</p>
@@ -1730,7 +1752,7 @@ my_button = Compony.button(:index, :users, enabled: -&gt; { |controller| control
1730
1752
  </div></div>
1731
1753
 
1732
1754
  <div id="footer">
1733
- Generated on Thu Jul 10 11:04:50 2025 by
1755
+ Generated on Thu Aug 28 14:18:36 2025 by
1734
1756
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
1735
1757
  0.9.37 (ruby-3.3.5).
1736
1758
  </div>
data/doc/index.html CHANGED
@@ -1272,18 +1272,20 @@ my_button = Compony.button(:index, :users, enabled: -&gt; { |controller| control
1272
1272
  <span class='kw'>end</span>
1273
1273
  </code></pre>
1274
1274
 
1275
- <p><strong>Note that the feasibility framework currently only affects buttons pointing to actions, not the action itself.</strong> If a user were to issue the HTTP call manually, the component happily responds and performs the action. This is why you should always back important preventions with an appropriate Rails model validation:</p>
1275
+ <p><strong>Note that the feasibility framework currently only affects buttons/links pointing to actions, not the action itself.</strong> If a user were to issue the HTTP call manually, the component happily responds and performs the action. This is why you should always back important preventions with an appropriate Rails model validation:</p>
1276
1276
  <ul><li>
1277
1277
  <p>The Rails model validation prevents that invalid data can be saved to the database.</p>
1278
1278
  </li><li>
1279
- <p>The feasibility framework disables buttons and explains to guide the user.</p>
1279
+ <p>The feasibility framework disables buttons and links and explains to guide the user.</p>
1280
+ </li><li>
1281
+ <p>Links are disabled by changing the href to <code>&#39;#&#39;</code> and adding the <code>.disabled</code> class, which is useful when bootstrap is used.</p>
1280
1282
  </li><li>
1281
1283
  <p>Authorization is orthogonal to this, limiting the actions of a specific user.</p>
1282
1284
  </li><li>
1283
1285
  <p>If an action is both prevented and not authorized, the authorization “wins” and the action button is not shown at all.</p>
1284
1286
  </li></ul>
1285
1287
 
1286
- <p>Compony has a feature that auto-detects feasibility. In particular, it checks for <code>dependent</code> relations in the <code>has_one</code>/<code>has_many</code> relations and disables delete buttons that point to objects that have dependent objects that cannot automatically be destroyed.</p>
1288
+ <p>Compony has a feature that auto-detects feasibility of some actions. In particular, it checks for <code>dependent</code> relations in the <code>has_one</code>/<code>has_many</code> relations and disables delete buttons that point to objects that have dependent objects that cannot automatically be destroyed.</p>
1287
1289
 
1288
1290
  <p>To disable auto detection, call <code>skip_autodetect_feasibilities</code> in your model.</p>
1289
1291
 
@@ -1632,6 +1634,26 @@ my_button = Compony.button(:index, :users, enabled: -&gt; { |controller| control
1632
1634
  <p><code>rails g components Users</code> will generate a set of the most used components.</p>
1633
1635
  </li></ul>
1634
1636
 
1637
+ <h3 id="label-Support+for+custom+base+components">Support for custom base components</h3>
1638
+
1639
+ <p>If your application has many components that share the same functionality, it may be a good idea to create a custom base component, which acts as an abstract class.</p>
1640
+
1641
+ <p>Examples:</p>
1642
+ <ul><li>
1643
+ <p>Perhaps you have code shared in all of your <code>New</code> components. In this case, create <code>BaseComponents::New</code> and inherit from <code>Compony::Components::New</code>. In <code>setup</code> of your base component, you can now perform all the configurations needed. Now you may inherit from it: <code>class Components::Users::New &lt; BaseComponents::New</code>.</p>
1644
+ </li><li>
1645
+ <p>Perhaps you often implement the same kind of component, for instance an index component displaying a filterable list. In this case, create <code>BaseComponents::Index</code> and inherit as follows: <code>class Components::Users::Index &lt; BaseComponents::Index</code>.</p>
1646
+ </li></ul>
1647
+
1648
+ <p>Compony has the following convention:</p>
1649
+ <ul><li>
1650
+ <p>implement a custom base component in the directory <code>app/compony/base_components/your_component.rb</code></p>
1651
+ </li><li>
1652
+ <p>name the class <code>BaseComponents::YourComponent</code> where <code>BaseComponents</code> is typically a module simple meant for namespacing</p>
1653
+ </li></ul>
1654
+
1655
+ <p>When respecting these conventions, compony’s generators will automatically make generated classes inherit from the suitable base component if one is available. In the example above, <code>rails g component Users::Index</code> will automatically make the generated class inherit from <code>BaseComponent::Index</code>.</p>
1656
+
1635
1657
  <h2 id="label-Internal+datastructures">Internal datastructures</h2>
1636
1658
 
1637
1659
  <p>Compony has a few internal data structures that are worth mentioning. Especially when building your own UI framework on top of Compony, these might come in handy.</p>
@@ -1730,7 +1752,7 @@ my_button = Compony.button(:index, :users, enabled: -&gt; { |controller| control
1730
1752
  </div></div>
1731
1753
 
1732
1754
  <div id="footer">
1733
- Generated on Thu Jul 10 11:04:49 2025 by
1755
+ Generated on Thu Aug 28 14:18:35 2025 by
1734
1756
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
1735
1757
  0.9.37 (ruby-3.3.5).
1736
1758
  </div>
data/doc/method_list.html CHANGED
@@ -232,14 +232,6 @@
232
232
 
233
233
 
234
234
  <li class="even ">
235
- <div class="item">
236
- <span class='object_link'><a href="Compony/Component.html#comp_cst-instance_method" title="Compony::Component#comp_cst (method)">#comp_cst</a></span>
237
- <small>Compony::Component</small>
238
- </div>
239
- </li>
240
-
241
-
242
- <li class="odd ">
243
235
  <div class="item">
244
236
  <span class='object_link'><a href="Compony/Component.html#comp_name-class_method" title="Compony::Component.comp_name (method)">comp_name</a></span>
245
237
  <small>Compony::Component</small>
@@ -247,14 +239,6 @@
247
239
  </li>
248
240
 
249
241
 
250
- <li class="even ">
251
- <div class="item">
252
- <span class='object_link'><a href="Compony/Component.html#comp_name-instance_method" title="Compony::Component#comp_name (method)">#comp_name</a></span>
253
- <small>Compony::Component</small>
254
- </div>
255
- </li>
256
-
257
-
258
242
  <li class="odd ">
259
243
  <div class="item">
260
244
  <span class='object_link'><a href="Compony/Component.html#comp_opts-instance_method" title="Compony::Component#comp_opts (method)">#comp_opts</a></span>
@@ -432,14 +416,6 @@
432
416
 
433
417
 
434
418
  <li class="odd ">
435
- <div class="item">
436
- <span class='object_link'><a href="Compony/Component.html#family_cst-instance_method" title="Compony::Component#family_cst (method)">#family_cst</a></span>
437
- <small>Compony::Component</small>
438
- </div>
439
- </li>
440
-
441
-
442
- <li class="even ">
443
419
  <div class="item">
444
420
  <span class='object_link'><a href="Compony/Component.html#family_name-class_method" title="Compony::Component.family_name (method)">family_name</a></span>
445
421
  <small>Compony::Component</small>
@@ -447,14 +423,6 @@
447
423
  </li>
448
424
 
449
425
 
450
- <li class="odd ">
451
- <div class="item">
452
- <span class='object_link'><a href="Compony/Component.html#family_name-instance_method" title="Compony::Component#family_name (method)">#family_name</a></span>
453
- <small>Compony::Component</small>
454
- </div>
455
- </li>
456
-
457
-
458
426
  <li class="even ">
459
427
  <div class="item">
460
428
  <span class='object_link'><a href="Compony.html#family_name_for-class_method" title="Compony.family_name_for (method)">family_name_for</a></span>
@@ -102,7 +102,7 @@
102
102
  </div>
103
103
 
104
104
  <div id="footer">
105
- Generated on Thu Jul 10 11:04:50 2025 by
105
+ Generated on Thu Aug 28 14:18:36 2025 by
106
106
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
107
107
  0.9.37 (ruby-3.3.5).
108
108
  </div>
@@ -113,24 +113,16 @@ module Compony
113
113
  end
114
114
 
115
115
  # Returns the name of the module constant (=family) of this component. Do not override.
116
- def family_cst
117
- self.class.family_cst
118
- end
116
+ delegate :family_cst, to: :class
119
117
 
120
118
  # Returns the family name
121
- def family_name
122
- self.class.family_name
123
- end
119
+ delegate :family_name, to: :class
124
120
 
125
121
  # Returns the name of the class constant of this component. Do not override.
126
- def comp_cst
127
- self.class.comp_cst
128
- end
122
+ delegate :comp_cst, to: :class
129
123
 
130
124
  # Returns the component name
131
- def comp_name
132
- self.class.comp_name
133
- end
125
+ delegate :comp_name, to: :class
134
126
 
135
127
  # DSL method
136
128
  # Adds or overrides a before_render block.
@@ -160,7 +152,7 @@ module Compony
160
152
  # DSL method
161
153
  # Removes a content block. Use this in subclasses if a content block defined in the parent should be removed from the child.
162
154
  # @param [Symbol,String] name Name of the content block that should be removed
163
- def remove_content(name)
155
+ def remove_content(name) # rubocop:disable Naming/PredicateMethod
164
156
  existing_index = @content_blocks.find_index { |el| el.name == name.to_sym }
165
157
  if existing_index.nil?
166
158
  return false
@@ -4,6 +4,7 @@ module Compony
4
4
  # This component is used for the Rails edit and update paradigm. Performs update when the form is submitted.
5
5
  class Edit < Compony::Components::WithForm
6
6
  include Compony::ComponentMixins::Resourceful
7
+
7
8
  setup do
8
9
  submit_verb :patch
9
10
  standalone path: "#{family_name}/:id/edit" do
@@ -77,7 +78,7 @@ module Compony
77
78
  elsif data_class.owner_model_attr.present?
78
79
  Compony.path(:show, @data.send(data_class.owner_model_attr))
79
80
  else
80
- Compony.path(:index, @data)
81
+ Compony.path(:index, family_cst)
81
82
  end
82
83
  end
83
84
 
@@ -69,7 +69,7 @@ module Compony
69
69
  elsif data_class.owner_model_attr.present?
70
70
  Compony.path(:show, @data.send(data_class.owner_model_attr))
71
71
  else
72
- Compony.path(:index, @data)
72
+ Compony.path(:index, family_cst)
73
73
  end
74
74
  end
75
75
 
@@ -2,7 +2,7 @@ module Compony
2
2
  module ModelFields
3
3
  class RichText < Base
4
4
  def simpleform_input(form, _component, name: nil, **input_opts)
5
- return form.input name || @name, **input_opts.merge(as: :rich_text_area)
5
+ return form.input name || @name, **input_opts, as: :rich_text_area
6
6
  end
7
7
  end
8
8
  end
@@ -43,7 +43,7 @@ module Compony
43
43
  end
44
44
 
45
45
  # Renders a content block from the current component.
46
- def content(name)
46
+ def content(name) # rubocop:disable Naming/PredicateMethod
47
47
  name = name.to_sym
48
48
  content_block = component.content_blocks.find { |el| el.name == name }
49
49
  return false if content_block.nil?
@@ -25,6 +25,8 @@ module Compony
25
25
  label: nil,
26
26
  label_opts: {},
27
27
  params: {},
28
+ feasibility_action: nil,
29
+ feasibility_target: nil,
28
30
  standalone_name: nil,
29
31
  **link_kwargs)
30
32
  model = model_or_family_name_or_cst.respond_to?(:model_name) ? model_or_family_name_or_cst : nil
@@ -34,11 +36,16 @@ module Compony
34
36
  target_comp_instance = Compony.comp_class_for!(comp_name_or_cst_or_class, model_or_family_name_or_cst).new(data: model)
35
37
  end
36
38
  return unless target_comp_instance.standalone_access_permitted_for?(self, standalone_name:)
37
- return helpers.link_to(
38
- label || target_comp_instance.label(model, **label_opts),
39
- Compony.path(target_comp_instance.comp_name, target_comp_instance.family_name, model, standalone_name:, **params),
40
- *link_args, **link_kwargs
41
- )
39
+ feasibility_action ||= comp_name_or_cst_or_class.to_s.underscore.to_sym
40
+ feasibility_target ||= model
41
+ label ||= target_comp_instance.label(model, **label_opts)
42
+ path ||= Compony.path(target_comp_instance.comp_name, target_comp_instance.family_name, model, standalone_name:, **params)
43
+ if feasibility_target && !feasibility_target.feasible?(feasibility_action)
44
+ path = '#'
45
+ link_kwargs[:class] = link_kwargs[:class].is_a?(String) ? "#{link_kwargs[:class]} disabled" : 'disabled'
46
+ link_kwargs[:title] = feasibility_target.full_feasibility_messages(feasibility_action).presence
47
+ end
48
+ return helpers.link_to(label, path, *link_args, **link_kwargs)
42
49
  end
43
50
 
44
51
  # Given a component and a family/model, this instanciates and renders a button component.
@@ -10,6 +10,13 @@ class ComponentGenerator < Rails::Generators::NamedBase
10
10
  @comp_cst = @comp.camelize # Tolerate singular and plural
11
11
  @args = args
12
12
 
13
+ # If BaseComponents::ComponentAboutToBeGenerated is present, inherit from that
14
+ if defined?(BaseComponents.const_defined?(@comp_cst))
15
+ @parent_base_component_class = BaseComponents.const_get(@comp_cst)
16
+ template 'with_base_component.rb.erb', "app/components/#{@family}/#{@comp}.rb"
17
+ return
18
+ end
19
+ # If a Compony component with the specified name exists, inherit from that
13
20
  case @comp_cst
14
21
  when 'Destroy'
15
22
  template 'destroy.rb.erb', "app/components/#{@family}/#{@comp}.rb"
@@ -20,6 +27,7 @@ class ComponentGenerator < Rails::Generators::NamedBase
20
27
  when 'New'
21
28
  template 'new.rb.erb', "app/components/#{@family}/#{@comp}.rb"
22
29
  else
30
+ # Inherit from regular component
23
31
  template 'component.rb.erb', "app/components/#{@family}/#{@comp}.rb"
24
32
  end
25
33
  end
@@ -0,0 +1,4 @@
1
+ class Components::<%= @family_cst %>::<%= @comp_cst %> < <%= @parent_base_component_class %>
2
+ setup do
3
+ end
4
+ end
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.5.6
4
+ version: 0.5.8
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sandro Kalbermatter
@@ -306,6 +306,7 @@ files:
306
306
  - lib/generators/component/templates/edit.rb.erb
307
307
  - lib/generators/component/templates/form.rb.erb
308
308
  - lib/generators/component/templates/new.rb.erb
309
+ - lib/generators/component/templates/with_base_component.rb.erb
309
310
  - lib/generators/components/USAGE
310
311
  - lib/generators/components/components_generator.rb
311
312
  - logo.svg