compony 0.11.12 → 0.11.13

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 (138) hide show
  1. checksums.yaml +4 -4
  2. data/.rspec +2 -0
  3. data/CHANGELOG.md +9 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +145 -106
  6. data/README.md +12 -1
  7. data/Rakefile +10 -0
  8. data/TODO.md +2 -1
  9. data/VERSION +1 -1
  10. data/compony.gemspec +5 -3
  11. data/doc/ComponentGenerator.html +1 -1
  12. data/doc/Components.html +1 -1
  13. data/doc/ComponentsGenerator.html +1 -1
  14. data/doc/Compony/Component.html +1 -1
  15. data/doc/Compony/ComponentMixins/Default/Labelling.html +1 -1
  16. data/doc/Compony/ComponentMixins/Default/Standalone/ResourcefulVerbDsl.html +1 -1
  17. data/doc/Compony/ComponentMixins/Default/Standalone/StandaloneDsl.html +1 -1
  18. data/doc/Compony/ComponentMixins/Default/Standalone/VerbDsl.html +1 -1
  19. data/doc/Compony/ComponentMixins/Default/Standalone.html +1 -1
  20. data/doc/Compony/ComponentMixins/Default.html +1 -1
  21. data/doc/Compony/ComponentMixins/Resourceful.html +1 -1
  22. data/doc/Compony/ComponentMixins.html +1 -1
  23. data/doc/Compony/Components/Buttons/CssButton.html +1 -1
  24. data/doc/Compony/Components/Buttons/Link.html +1 -1
  25. data/doc/Compony/Components/Buttons.html +1 -1
  26. data/doc/Compony/Components/Destroy.html +1 -1
  27. data/doc/Compony/Components/Edit.html +1 -1
  28. data/doc/Compony/Components/Form.html +1 -1
  29. data/doc/Compony/Components/Index.html +1 -1
  30. data/doc/Compony/Components/List.html +1 -1
  31. data/doc/Compony/Components/New.html +1 -1
  32. data/doc/Compony/Components/Show.html +1 -1
  33. data/doc/Compony/Components/WithForm.html +1 -1
  34. data/doc/Compony/Components.html +1 -1
  35. data/doc/Compony/ControllerMixin.html +1 -1
  36. data/doc/Compony/Engine.html +1 -1
  37. data/doc/Compony/Intent.html +1 -1
  38. data/doc/Compony/ManageIntentsDsl.html +2 -2
  39. data/doc/Compony/MethodAccessibleHash.html +1 -1
  40. data/doc/Compony/ModelFields/Anchormodel.html +1 -1
  41. data/doc/Compony/ModelFields/Association.html +1 -1
  42. data/doc/Compony/ModelFields/Attachment.html +1 -1
  43. data/doc/Compony/ModelFields/Base.html +8 -2
  44. data/doc/Compony/ModelFields/Boolean.html +1 -1
  45. data/doc/Compony/ModelFields/Color.html +1 -1
  46. data/doc/Compony/ModelFields/Currency.html +1 -1
  47. data/doc/Compony/ModelFields/Date.html +1 -1
  48. data/doc/Compony/ModelFields/Datetime.html +1 -1
  49. data/doc/Compony/ModelFields/Decimal.html +1 -1
  50. data/doc/Compony/ModelFields/Email.html +1 -1
  51. data/doc/Compony/ModelFields/Float.html +1 -1
  52. data/doc/Compony/ModelFields/Integer.html +1 -1
  53. data/doc/Compony/ModelFields/Percentage.html +1 -1
  54. data/doc/Compony/ModelFields/Phone.html +1 -1
  55. data/doc/Compony/ModelFields/RichText.html +1 -1
  56. data/doc/Compony/ModelFields/String.html +1 -1
  57. data/doc/Compony/ModelFields/Text.html +1 -1
  58. data/doc/Compony/ModelFields/Time.html +1 -1
  59. data/doc/Compony/ModelFields/Url.html +1 -1
  60. data/doc/Compony/ModelFields.html +1 -1
  61. data/doc/Compony/ModelMixin.html +1 -1
  62. data/doc/Compony/NaturalOrdering.html +1 -1
  63. data/doc/Compony/RequestContext.html +1 -1
  64. data/doc/Compony/Version.html +1 -1
  65. data/doc/Compony/ViewHelpers.html +1 -1
  66. data/doc/Compony/VirtualModel.html +1 -1
  67. data/doc/Compony.html +1 -1
  68. data/doc/ComponyController.html +1 -1
  69. data/doc/_index.html +1 -1
  70. data/doc/file.CHANGELOG.html +84 -77
  71. data/doc/file.README.html +21 -11
  72. data/doc/file.basic_component.html +14 -14
  73. data/doc/file.cookbook.html +27 -27
  74. data/doc/file.destroy.html +2 -2
  75. data/doc/file.dsl_reference.html +199 -199
  76. data/doc/file.edit.html +3 -3
  77. data/doc/file.example.html +13 -13
  78. data/doc/file.example_advanced.html +10 -10
  79. data/doc/file.feasibility.html +2 -2
  80. data/doc/file.form.html +7 -7
  81. data/doc/file.generators.html +3 -3
  82. data/doc/file.glossary.html +44 -44
  83. data/doc/file.gotchas.html +35 -35
  84. data/doc/file.index.html +10 -10
  85. data/doc/file.inheritance.html +5 -5
  86. data/doc/file.installation.html +9 -9
  87. data/doc/file.integrations.html +46 -46
  88. data/doc/file.intents.html +11 -11
  89. data/doc/file.internal_datastructures.html +6 -6
  90. data/doc/file.list.html +35 -35
  91. data/doc/file.maintaining.html +21 -21
  92. data/doc/file.model_fields.html +3 -3
  93. data/doc/file.nesting.html +5 -5
  94. data/doc/file.new.html +3 -3
  95. data/doc/file.ownership.html +2 -2
  96. data/doc/file.patterns.html +50 -50
  97. data/doc/file.pre_built_components.html +2 -2
  98. data/doc/file.resourceful.html +5 -5
  99. data/doc/file.show.html +17 -17
  100. data/doc/file.standalone.html +12 -12
  101. data/doc/file.virtual_models.html +2 -2
  102. data/doc/file.with_form.html +23 -23
  103. data/doc/index.html +21 -11
  104. data/doc/top-level-namespace.html +1 -1
  105. data/lib/compony/model_fields/base.rb +3 -0
  106. data/spec/README.md +22 -0
  107. data/spec/compony/component_spec.rb +80 -0
  108. data/spec/compony/compony_spec.rb +102 -0
  109. data/spec/compony/intent_spec.rb +94 -0
  110. data/spec/compony/method_accessible_hash_spec.rb +32 -0
  111. data/spec/compony/model_fields_spec.rb +70 -0
  112. data/spec/compony/model_mixin_spec.rb +81 -0
  113. data/spec/compony/natural_ordering_spec.rb +61 -0
  114. data/spec/dummy/app/components/statics/home.rb +17 -0
  115. data/spec/dummy/app/components/statics/secret.rb +15 -0
  116. data/spec/dummy/app/components/users/destroy.rb +2 -0
  117. data/spec/dummy/app/components/users/edit.rb +2 -0
  118. data/spec/dummy/app/components/users/form.rb +14 -0
  119. data/spec/dummy/app/components/users/index.rb +29 -0
  120. data/spec/dummy/app/components/users/new.rb +2 -0
  121. data/spec/dummy/app/components/users/show.rb +26 -0
  122. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  123. data/spec/dummy/app/models/ability.rb +7 -0
  124. data/spec/dummy/app/models/application_record.rb +4 -0
  125. data/spec/dummy/app/models/user.rb +22 -0
  126. data/spec/dummy/app/views/layouts/application.html.erb +7 -0
  127. data/spec/dummy/config/application.rb +20 -0
  128. data/spec/dummy/config/database.yml +3 -0
  129. data/spec/dummy/config/environment.rb +3 -0
  130. data/spec/dummy/config/initializers/simple_form.rb +9 -0
  131. data/spec/dummy/config/routes.rb +3 -0
  132. data/spec/dummy/db/schema.rb +14 -0
  133. data/spec/rails_helper.rb +23 -0
  134. data/spec/requests/authorization_spec.rb +16 -0
  135. data/spec/requests/plain_component_spec.rb +9 -0
  136. data/spec/requests/routing_spec.rb +20 -0
  137. data/spec/requests/users_crud_spec.rb +99 -0
  138. metadata +62 -1
@@ -60,7 +60,7 @@
60
60
  <div id="content"><div id='filecontents'>
61
61
  <p><a href="/README_md.html#guide--documentation">Back to the guide</a></p>
62
62
 
63
- <h1 id="label-Intents">Intents</h1>
63
+ <h1 id="intents">Intents</h1>
64
64
 
65
65
  <p>An intent is a gateway to a component, along with relevant context. It encapsulates tools used to generate paths, checking <a href="/doc/guide/feasibility_md.html">feasibility</a> and rendering links and buttons pointing other components within your application.</p>
66
66
 
@@ -119,11 +119,11 @@
119
119
  <p>Any further arguments are passed to the initializer of the button if/when the intent gets rendered.</p>
120
120
  </li></ul>
121
121
 
122
- <h2 id="label-Helpers">Helpers</h2>
122
+ <h2 id="helpers">Helpers</h2>
123
123
 
124
124
  <p>In practice, you will rarely call <code>Compony.intent</code> directly, and likely never <code>Compony::Intent.new</code>. Instead, you will be interacting with one of the following helpers that will instanciate an intent under the hood and thus all accept similar arguments as those described above:</p>
125
125
 
126
- <h3 id="label-Compony.path"><code>Compony.path</code></h3>
126
+ <h3 id="componypath"><code>Compony.path</code></h3>
127
127
 
128
128
  <p>This helper is useful when generating paths without rendering any HTML, such as when redirecting. Internally, this builds an intent which will in turn use the target component’s <code>path</code> block to generate a Rails path (String) pointing to the correct location. Any keyword arguments are passed to the intent’s <code>path</code> method, allowing you to override the model (to refer to <a href="/doc/guide/resourceful_md.html">resourceful</a> target components), as well as specifying a <code>standalone_name</code> or pass extra arguments to the target component’s <code>path</code> block.</p>
129
129
 
@@ -135,7 +135,7 @@
135
135
  </span><span class='id identifier rubyid_redirect_to'>redirect_to</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='symbol'>:step_2</span><span class='comma'>,</span> <span class='symbol'>:registrations</span><span class='comma'>,</span> <span class='label'>accept_terms:</span> <span class='symbol'>:yes</span><span class='rparen'>)</span> <span class='comment'># Redirects to something like /registrations/step_2?accept_terms=yes
136
136
  </span></code></pre>
137
137
 
138
- <h3 id="label-render_intent"><code>render_intent</code></h3>
138
+ <h3 id="renderintent"><code>render_intent</code></h3>
139
139
 
140
140
  <p>This is the preferred way of quickly rendering <a href="#buttons-and-styles">links or buttons to components</a> from other parts of your application. The method is implemented twice:</p>
141
141
  <ul><li>
@@ -155,7 +155,7 @@ end
155
155
 
156
156
  <p>In the example above, there is a lot more going on than it seems. Due to the <a href="#buttons-and-styles">specified style</a>, a plain HTML link will be generated, pointing to the component <code>Components::Users::Show</code> and instanciating it with the first user as <code>@data</code>. Assuming that component inherits from <code>Compony::Components::Show</code> and did not override label or path, the link will be labelled “Show” (which is the default short format label generated by Compony’s default <a href="/doc/guide/pre_built_components/show_md.html">pre-built Show component</a>), and the <code>href</code> will be <code>/users/:id</code> where ID will automatically be <code>User.first</code>‘s ID (e.g. <code>/users/1</code>). However, if <code>:show</code> was <a href="/doc/guide/feasibility_md.html">prevented</a>, the link will be strikethrough, non-clickable, greyed out and have a title explaining why it can’t be clicked. Further, if the current user does not have <a href="/doc/guide/standalone_md.html">authorization</a> to display the target user, the link will not show up at all, and no HTML will be generated within the <code>div</code>.</p>
157
157
 
158
- <h3 id="label-render_sub_comp"><code>render_sub_comp</code></h3>
158
+ <h3 id="rendersubcomp"><code>render_sub_comp</code></h3>
159
159
 
160
160
  <p>This is used within a component’s <code>content</code> block to instanciate another component and <a href="/doc/guide/nesting_md.html">nest it within</a>. Internally, the current component’s <code>sub_comp</code> method is used and all arguments are passed to that.</p>
161
161
 
@@ -173,13 +173,13 @@ end
173
173
 
174
174
  <p>This implicitely builds an intent which auto-detects the family name <code>:quotes</code> from <code>@data.quotes</code>, which is an active record collection and thus implements <code>model_name</code>. The List component is then instanciated with its <code>@data</code> being that very collection and the users’s Show component as <code>parent_comp</code>, resulting in the proper nesting and display of the desired resources.</p>
175
175
 
176
- <h3 id="label-Compony.comp_class_for"><code>Compony.comp_class_for</code></h3>
176
+ <h3 id="componycompclassfor"><code>Compony.comp_class_for</code></h3>
177
177
 
178
178
  <p>This helper is useful for checking whether a component is implemented. For instance, when implementing <a href="/doc/guide/inheritance_md.html">abstract components to inherit from later</a>, you can check for <code>if Compony.comp_class_for(:destroy, family_name)</code> to only provide some functionality of a Destroy component exists for the current family.</p>
179
179
 
180
180
  <p>This method also has its sibling <code>Compony.comp_class_for!</code>, which will fail if no such component could be found. It is however mostly used internally.</p>
181
181
 
182
- <h2 id="label-Buttons+and+styles">Buttons and styles</h2>
182
+ <h2 id="buttons-and-styles">Buttons and styles</h2>
183
183
 
184
184
  <p>Button components are used as presenters for intents, hyperlinks to other components or submit buttons. They are a central way to define how buttons all over the application should look like. Their interface is adapted to intents, creating a standardized “slim waist” that greatly simplifies linking between components. Note that the term “button” refers to how they look and not how they are actually implemented - actual HTML buttons have several disadvantages (e.g. requiring drop-in forms and not responding to Ctrl+Click or middle-click when the user would prefer a new tab).</p>
185
185
 
@@ -207,7 +207,7 @@ end
207
207
 
208
208
  <p>Note: it is possible to use a button component to submit a form. In order to achieve this, you must implement a hidden submit button (for handling keyboard Enter and Return), as well as pass <code>onclick: &quot;this.closest(&#39;form&#39;).requestSubmit(); return false;&quot;</code> as an argument. See the pre-built Form component’s implementation for an example.</p>
209
209
 
210
- <h3 id="label-Adding+your+own+styles">Adding your own styles</h3>
210
+ <h3 id="adding-your-own-styles">Adding your own styles</h3>
211
211
 
212
212
  <p>In your application, you will likely want to implement your own button styles. Create a component (e.g. <code>Components::Commons::MyButton</code>) and inherit from <code>Compony::Components::Buttons::Link</code>. Override the method <code>prepare_opts!</code> and don’t forget to call <code>super</code> first. Then, go through any <code>@comp_args</code> that might be of interest to you and mutate <code>@comp_args[:style]</code> and/or <code>@comp_args[:class]</code> to suit your needs. Make sure to handle the class <code>disabled</code>, as intents will set them if the intent is not feasible. Note that if a user is lacking authorization to perform an intent, the intent will not even instanciate the button.</p>
213
213
 
@@ -215,7 +215,7 @@ end
215
215
 
216
216
  <p>If you have multiple kinds of buttons (e.g. dropdown items, pill-style buttons, compact forms etc.), you should create a separate style and button component class for every kind. This will make it easy to refer to them by supplying something like <code>style: :dropdown_item</code> in <code>render_intent</code>.</p>
217
217
 
218
- <h2 id="label-Exposed+intents">Exposed intents</h2>
218
+ <h2 id="exposed-intents">Exposed intents</h2>
219
219
 
220
220
  <p>Components can expose a set of intents to be displayed elsewhere. Those can either be rendered by the parent comp, or by the application layout itself in case the component exposing them is currently <code>root_comp</code> (see the chapter about <a href="/doc/guide/standalone_md.html">standalone</a>). This is useful if you have something like an actions toolbar that changes depending on the currently contained component.</p>
221
221
 
@@ -240,7 +240,7 @@ end
240
240
 
241
241
  <p>Note that the <code>add</code> method has full intent argument support and thus also accepts parameters related to the button (e.g. <code>style</code>), path generation, feasibility etc.</p>
242
242
 
243
- <h3 id="label-Rendering+exposed+intents">Rendering exposed intents</h3>
243
+ <h3 id="rendering-exposed-intents">Rendering exposed intents</h3>
244
244
 
245
245
  <p>You can render exposed intents in the parent component or in the application layout. To do so, call <code>component.exposed_intents</code>, loop across them and call <code>.render(controller)</code> on each (perhaps inside a <code>div</code> tag or whatever suits your needs).</p>
246
246
 
@@ -255,7 +255,7 @@ end
255
255
  </div></div>
256
256
 
257
257
  <div id="footer">
258
- Generated on Tue Jun 30 22:41:18 2026 by
258
+ Generated on Tue Aug 18 14:36:34 2026 by
259
259
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
260
260
  0.9.34 (ruby-3.4.9).
261
261
  </div>
@@ -60,11 +60,11 @@
60
60
  <div id="content"><div id='filecontents'>
61
61
  <p><a href="/README_md.html#guide--documentation">Back to the guide</a></p>
62
62
 
63
- <h1 id="label-Internal+datastructures">Internal datastructures</h1>
63
+ <h1 id="internal-datastructures">Internal datastructures</h1>
64
64
 
65
65
  <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>
66
66
 
67
- <h2 id="label-MethodAccessibleHash">MethodAccessibleHash</h2>
67
+ <h2 id="methodaccessiblehash">MethodAccessibleHash</h2>
68
68
 
69
69
  <p>This is a simpler and safer version of <a href="https://github.com/ruby/ostruct">OpenStruct</a>, allowing you to access a hash’s keys via method accessors.</p>
70
70
 
@@ -77,11 +77,11 @@
77
77
  </span><span class='id identifier rubyid_options'>options</span><span class='period'>.</span><span class='id identifier rubyid_color'>color</span> <span class='comment'># =&gt; green
78
78
  </span></code></pre>
79
79
 
80
- <p>This part of Compony is also made available under the MIT license at: <a href="https://gist.github.com/kalsan/87826048ea0ade92ab1be93c0919b405">gist.github.com/kalsan/87826048ea0ade92ab1be93c0919b405</a>.</p>
80
+ <p>This part of Compony is also made available under the MIT license at: <a href="https://gist.github.com/kalsan/87826048ea0ade92ab1be93c0919b405">https://gist.github.com/kalsan/87826048ea0ade92ab1be93c0919b405</a>.</p>
81
81
 
82
- <h2 id="label-RequestContext">RequestContext</h2>
82
+ <h2 id="requestcontext">RequestContext</h2>
83
83
 
84
- <p>The content blocks, as well as Form’s <code>form_fields</code> block all run within a <code>Compony::RequestContext</code>, which encapsulates useful methods for accessing data within a request. RequestContext is a Dslblend object and contains all the magic described in <a href="https://github.com/kalsan/dslblend">github.com/kalsan/dslblend</a>.</p>
84
+ <p>The content blocks, as well as Form’s <code>form_fields</code> block all run within a <code>Compony::RequestContext</code>, which encapsulates useful methods for accessing data within a request. RequestContext is a Dslblend object and contains all the magic described in <a href="https://github.com/kalsan/dslblend">https://github.com/kalsan/dslblend</a>.</p>
85
85
 
86
86
  <p>The main provider (refer to the Dslblend documentation to find out what that means) is set to the component. Additional providers are controller’s helpers, the controller itself, as well as custom additional providers that can be fed to RequestContext in the initializer.</p>
87
87
 
@@ -119,7 +119,7 @@
119
119
  </div></div>
120
120
 
121
121
  <div id="footer">
122
- Generated on Tue Jun 30 22:41:19 2026 by
122
+ Generated on Tue Aug 18 14:36:34 2026 by
123
123
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
124
124
  0.9.34 (ruby-3.4.9).
125
125
  </div>
data/doc/file.list.html CHANGED
@@ -63,13 +63,13 @@
63
63
  <p><a href="/doc/guide/pre_built_components_md.html">List of pre-built components</a></p>
64
64
  </li></ul>
65
65
 
66
- <h1 id="label-Pre-built+components-3A+List">Pre-built components: List</h1>
66
+ <h1 id="pre-built-components-list">Pre-built components: List</h1>
67
67
 
68
- <p><code>Compony::Components::List</code> is a resourceful component that renders a table/list of records. It is <strong>not</strong> standalone — it is meant to be nested, typically inside href="./index_md.html"></a> of the same family or href="./show_md.html"></a> of an owning family, via <code>render_sub_comp(:list, collection)</code>.</p>
68
+ <p><code>Compony::Components::List</code> is a resourceful component that renders a table/list of records. It is <strong>not</strong> standalone — it is meant to be nested, typically inside <a href="./index_md.html"><code>Index</code></a> of the same family or <a href="./show_md.html"><code>Show</code></a> of an owning family, via <code>render_sub_comp(:list, collection)</code>.</p>
69
69
 
70
70
  <p>Features: field-inferred or custom columns, per-row intents, pagination, and — when the <a href="https://github.com/activerecord-hackery/ransack">Ransack</a> gem is present and at least one sort/filter is declared — sorting links, a sort select, and a filter/search form.</p>
71
71
 
72
- <h2 id="label-Column+DSL">Column DSL</h2>
72
+ <h2 id="column-dsl">Column DSL</h2>
73
73
 
74
74
  <table role="table">
75
75
  <thead>
@@ -81,18 +81,18 @@
81
81
  </thead>
82
82
  <tbody>
83
83
  <tr>
84
- <td>‘column`</td>
85
- <td>‘column(:name, label: nil, class: nil, link_opts: {}) { |record| }`</td>
86
- <td>Add/define a column. No block → model-field column (auto label, value via value_for`, only if `:index` permitted). Block is instance-exec’d per row and renders the cell.</td>
84
+ <td><code>column</code></td>
85
+ <td><code>column(:name, label: nil, class: nil, link_opts: {}) { |record| ... }</code></td>
86
+ <td>Add/define a column. No block → model-field column (auto label, value via <code>value_for</code>, only if <code>:index</code> permitted). Block is instance-exec’d per row and renders the cell.</td>
87
87
  </tr>
88
88
  <tr>
89
- <td>‘columns`</td>
90
- <td>‘columns(:a, :b, as_title: false, **kw)`</td>
91
- <td>Bulk column`. `as_title: true` marks title columns (shown as the card heading in mobile/card layouts).</td>
89
+ <td><code>columns</code></td>
90
+ <td><code>columns(:a, :b, as_title: false, **kw)</code></td>
91
+ <td>Bulk <code>column</code>. <code>as_title: true</code> marks title columns (shown as the card heading in mobile/card layouts).</td>
92
92
  </tr>
93
93
  <tr>
94
- <td>‘skip_column`</td>
95
- <td>‘skip_column(:name)`</td>
94
+ <td><code>skip_column</code></td>
95
+ <td><code>skip_column(:name)</code></td>
96
96
  <td>Hide a (possibly inherited) column.</td>
97
97
  </tr>
98
98
  </tbody>
@@ -109,7 +109,7 @@
109
109
  <span class='kw'>end</span>
110
110
  </code></pre>
111
111
 
112
- <h2 id="label-Filtering+-26+sorting+-28Ransack-29">Filtering &amp; sorting (Ransack)</h2>
112
+ <h2 id="filtering--sorting-ransack">Filtering &amp; sorting (Ransack)</h2>
113
113
 
114
114
  <table role="table">
115
115
  <thead>
@@ -121,28 +121,28 @@
121
121
  </thead>
122
122
  <tbody>
123
123
  <tr>
124
- <td>‘filter`</td>
125
- <td>‘filter(:name, label: nil) { |f| }`</td>
126
- <td>Add a filter. No block → field filter or a Ransack predicate string (e.g. ‘:id_eq`). Block gets the Ransack search form and renders label + input.</td>
124
+ <td><code>filter</code></td>
125
+ <td><code>filter(:name, label: nil) { |f| ... }</code></td>
126
+ <td>Add a filter. No block → field filter or a Ransack predicate string (e.g. <code>:id_eq</code>). Block gets the Ransack search form and renders label + input.</td>
127
127
  </tr>
128
128
  <tr>
129
- <td>‘filters`</td>
130
- <td>‘filters(:a, :b, **kw)`</td>
131
- <td>Bulk filter`.</td>
129
+ <td><code>filters</code></td>
130
+ <td><code>filters(:a, :b, **kw)</code></td>
131
+ <td>Bulk <code>filter</code>.</td>
132
132
  </tr>
133
133
  <tr>
134
- <td>‘sort`</td>
135
- <td>‘sort(:name, label: nil)`</td>
134
+ <td><code>sort</code></td>
135
+ <td><code>sort(:name, label: nil)</code></td>
136
136
  <td>Add a sort criterion (must be Ransack-sortable). Generates one sort link + asc/desc entries.</td>
137
137
  </tr>
138
138
  <tr>
139
- <td>‘sorts`</td>
140
- <td>‘sorts(:a, :b)`</td>
141
- <td>Bulk sort`.</td>
139
+ <td><code>sorts</code></td>
140
+ <td><code>sorts(:a, :b)</code></td>
141
+ <td>Bulk <code>sort</code>.</td>
142
142
  </tr>
143
143
  <tr>
144
- <td>‘default_sorting`</td>
145
- <td>‘default_sorting(id desc)‘</td>
144
+ <td><code>default_sorting</code></td>
145
+ <td><code>default_sorting(&#39;id desc&#39;)</code></td>
146
146
  <td>Default Ransack sort applied when none chosen.</td>
147
147
  </tr>
148
148
  </tbody>
@@ -158,7 +158,7 @@
158
158
  <span class='kw'>end</span>
159
159
  </code></pre>
160
160
 
161
- <h2 id="label-Per-row+intents">Per-row intents</h2>
161
+ <h2 id="per-row-intents">Per-row intents</h2>
162
162
 
163
163
  <p><code>row_intents</code> opens the <a href="/doc/guide/intents_md.html#exposed-intents">intent management DSL</a> (<code>add</code>/<code>remove</code>, <code>before:</code>) applied to each row’s record:</p>
164
164
 
@@ -170,7 +170,7 @@
170
170
  <span class='kw'>end</span>
171
171
  </code></pre>
172
172
 
173
- <h2 id="label-Toggles-2C+paging-2C+styling">Toggles, paging, styling</h2>
173
+ <h2 id="toggles-paging-styling">Toggles, paging, styling</h2>
174
174
 
175
175
  <p>All have matching constructor kwargs so a nesting parent can override per render (<code>render_sub_comp(:list, coll, skip_pagination: true, skip_columns: [:order])</code>).</p>
176
176
 
@@ -184,27 +184,27 @@
184
184
  </thead>
185
185
  <tbody>
186
186
  <tr>
187
- <td>‘pagination(bool)`</td>
187
+ <td><code>pagination(bool)</code></td>
188
188
  <td>on</td>
189
189
  <td>Enable/disable paging (off loads all rows).</td>
190
190
  </tr>
191
191
  <tr>
192
- <td>‘results_per_page(n)`</td>
192
+ <td><code>results_per_page(n)</code></td>
193
193
  <td>20</td>
194
194
  <td>Rows per page.</td>
195
195
  </tr>
196
196
  <tr>
197
- <td>‘filtering(bool)`</td>
197
+ <td><code>filtering(bool)</code></td>
198
198
  <td>on</td>
199
199
  <td>Enable/disable the filter form.</td>
200
200
  </tr>
201
201
  <tr>
202
- <td>‘sorting(bool)` / `sorting_in_filter(bool)` / `sorting_links(bool)`</td>
202
+ <td><code>sorting(bool)</code> / <code>sorting_in_filter(bool)</code> / <code>sorting_links(bool)</code></td>
203
203
  <td>on</td>
204
204
  <td>Toggle sort UIs.</td>
205
205
  </tr>
206
206
  <tr>
207
- <td>‘filter_label_class` / `filter_input_class` / `filter_select_class` / `filter_item_wrapper_class`</td>
207
+ <td><code>filter_label_class</code> / <code>filter_input_class</code> / <code>filter_select_class</code> / <code>filter_item_wrapper_class</code></td>
208
208
  <td>—</td>
209
209
  <td>CSS classes for filter form elements.</td>
210
210
  </tr>
@@ -213,9 +213,9 @@
213
213
 
214
214
  <p>Constructor <code>skip_*</code> kwargs: <code>skip_pagination</code>, <code>skip_filtering</code>, <code>skip_sorting</code>, <code>skip_sorting_in_filter</code>, <code>skip_sorting_links</code>, <code>skip_columns:</code>, <code>skip_row_intents:</code>, <code>skip_filters:</code>, <code>results_per_page:</code>, <code>default_sorting:</code>.</p>
215
215
 
216
- <h2 id="label-Customizing+rendering">Customizing rendering</h2>
216
+ <h2 id="customizing-rendering">Customizing rendering</h2>
217
217
 
218
- <p><code>List</code> exposes named <code>content</code> blocks (<code>:data</code>, <code>:filter</code>, <code>:pagination</code>, <code>:sorting_links</code>, …) that you override — almost always once, in an app <code>BaseComponents::List</code>, to fit your UI framework, then inherited everywhere:</p>
218
+ <p><code>List</code> exposes named <code>content</code> blocks (<code>:data</code>, <code>:filter</code>, <code>:pagination</code>, <code>:sorting_links</code>,…) that you override — almost always once, in an app <code>BaseComponents::List</code>, to fit your UI framework, then inherited everywhere:</p>
219
219
 
220
220
  <pre class="code ruby"><code class="ruby"><span class='kw'>module</span> <span class='const'>BaseComponents</span>
221
221
  <span class='kw'>class</span> <span class='const'>List</span> <span class='op'>&lt;</span> <span class='const'><span class='object_link'><a href="Compony.html" title="Compony (module)">Compony</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Compony/Components.html" title="Compony::Components (module)">Components</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Compony/Components/List.html" title="Compony::Components::List (class)">List</a></span></span>
@@ -243,7 +243,7 @@
243
243
  </div></div>
244
244
 
245
245
  <div id="footer">
246
- Generated on Tue Jun 30 22:41:19 2026 by
246
+ Generated on Tue Aug 18 14:36:34 2026 by
247
247
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
248
248
  0.9.34 (ruby-3.4.9).
249
249
  </div>
@@ -60,64 +60,64 @@
60
60
  <div id="content"><div id='filecontents'>
61
61
  <p><a href="/README_md.html#guide--documentation">Back to the guide</a></p>
62
62
 
63
- <h1 id="label-Maintaining+Compony">Maintaining Compony</h1>
63
+ <h1 id="maintaining-compony">Maintaining Compony</h1>
64
64
 
65
65
  <p>Conventions for contributors and AI assistants working <strong>on the gem itself</strong> (not apps using it). Keeps docs, gemspec and the rendered API reference from drifting.</p>
66
66
 
67
- <h2 id="label-Every+change">Every change</h2>
67
+ <h2 id="every-change">Every change</h2>
68
68
  <ul><li>
69
- <p><strong>Behavior change → CHANGELOG.</strong> Add a bullet under the top <code># unreleased</code> heading in <a href="/CHANGELOG_md.html">CHANGELOG.md</a>. If it breaks existing apps, add a <code>## Steps to take</code> subsection with the migration (see prior entries for the format).</p>
69
+ <p><strong>Behavior change → CHANGELOG.</strong> Add a bullet under the top <code># unreleased</code> heading in <a href="/CHANGELOG_md.html">CHANGELOG.md</a>. If it breaks existing apps, add a <code>## Steps to take</code> subsection with the migration (see prior entries for the format).</p>
70
70
  </li><li>
71
- <p><strong>New/renamed DSL method → docs.</strong> Update <a href="/doc/guide/dsl_reference_md.html">dsl_reference.md</a> (keep the <code># DSL method</code> source marker) and add a YARD summary with <code>@param</code>/<code>@return</code>/<code>@api</code> on the method.</p>
71
+ <p><strong>New/renamed DSL method → docs.</strong> Update <a href="/doc/guide/dsl_reference_md.html">dsl_reference.md</a> (keep the <code># DSL method</code> source marker) and add a YARD summary with <code>@param</code>/<code>@return</code>/<code>@api</code> on the method.</p>
72
72
  </li><li>
73
- <p><strong>Comments only / docs only is fine to note as such</strong> in the CHANGELOG (“no behavior change”).</p>
73
+ <p><strong>Comments only / docs only is fine to note as such</strong> in the CHANGELOG (“no behavior change”).</p>
74
74
  </li></ul>
75
75
 
76
- <h2 id="label-Releasing">Releasing</h2>
76
+ <h2 id="releasing">Releasing</h2>
77
77
  <ol><li>
78
- <p>Bump the version in <code>lib/compony/version.rb</code>. Loading the Rakefile rewrites <code>VERSION</code> from it; <code>.edge</code> marks an unreleased prerelease.</p>
78
+ <p>Bump the version in <code>lib/compony/version.rb</code>. Loading the Rakefile rewrites <code>VERSION</code> from it; <code>.edge</code> marks an unreleased prerelease.</p>
79
79
  </li><li>
80
80
  <p>Run <code>rake gemspec</code> — regenerates <code>compony.gemspec</code> (never hand-edit it; it says so).</p>
81
81
  </li><li>
82
- <p>Move the <code># unreleased</code> CHANGELOG block to a <code># X.Y.Z</code> heading; start a fresh <code># unreleased</code>.</p>
82
+ <p>Move the <code># unreleased</code> CHANGELOG block to a <code># X.Y.Z</code> heading; start a fresh <code># unreleased</code>.</p>
83
83
  </li><li>
84
- <p>Run <code>yard doc</code> and <strong>commit the regenerated <code>doc/*.html</code></strong> — the rendered API reference is committed and shipped in the gem (<code>s.files</code>), so stale HTML ships otherwise.</p>
84
+ <p>Run <code>yard doc</code> and <strong>commit the regenerated <code>doc/*.html</code></strong> — the rendered API reference is committed and shipped in the gem (<code>s.files</code>), so stale HTML ships otherwise.</p>
85
85
  </li><li>
86
86
  <p><code>bundle exec rubocop</code> clean.</p>
87
87
  </li><li>
88
88
  <p>Tag / push / <code>gem build</code> per your release flow (<code>bundler/gem_tasks</code>).</p>
89
89
  </li></ol>
90
90
 
91
- <h2 id="label-Dependencies">Dependencies</h2>
91
+ <h2 id="dependencies">Dependencies</h2>
92
92
  <ul><li>
93
- <p>Hard deps and their constraints live <strong>only</strong> in the <code>:gemspec</code> task in href="/Rakefile"></a>. Change them there, run <code>rake gemspec</code>, and update the mirror table in <a href="/doc/integrations_md.html">integrations.md</a> in the same commit.</p>
93
+ <p>Hard deps and their constraints live <strong>only</strong> in the <code>:gemspec</code> task in <a href="/Rakefile"><code>Rakefile</code></a>. Change them there, run <code>rake gemspec</code>, and update the mirror table in <a href="/doc/integrations_md.html">integrations.md</a> in the same commit.</p>
94
94
  </li><li>
95
- <p>Keep the README’s stated Rails/Ruby support in sync with <code>required_ruby_version</code> / the <code>rails</code> constraint — the gemspec is authoritative.</p>
95
+ <p>Keep the README’s stated Rails/Ruby support in sync with <code>required_ruby_version</code> / the <code>rails</code> constraint — the gemspec is authoritative.</p>
96
96
  </li></ul>
97
97
 
98
- <h2 id="label-Documentation+rendering">Documentation rendering</h2>
98
+ <h2 id="documentation-rendering">Documentation rendering</h2>
99
99
  <ul><li>
100
- <p><code>doc/*.html</code> is generated by YARD from <code>lib/</code> + the extra files listed in href="/.yardopts"></a>.</p>
100
+ <p><code>doc/*.html</code> is generated by YARD from <code>lib/</code> + the extra files listed in <a href="/.yardopts"><code>.yardopts</code></a>.</p>
101
101
  </li><li>
102
- <p><strong>Adding a guide page?</strong> Add its path to <code>.yardopts</code> (after the <code>-</code> separator) or it will not appear in the rendered reference, then re-run <code>yard doc</code>.</p>
102
+ <p><strong>Adding a guide page?</strong> Add its path to <code>.yardopts</code> (after the <code>-</code> separator) or it will not appear in the rendered reference, then re-run <code>yard doc</code>.</p>
103
103
  </li><li>
104
- <p>Markdown changes to already-listed guide pages still need a <code>yard doc</code> re-run + commit to refresh the HTML.</p>
104
+ <p>Markdown changes to already-listed guide pages still need a <code>yard doc</code> re-run + commit to refresh the HTML.</p>
105
105
  </li><li>
106
- <p><code>yard doc</code> logs <code>Cannot resolve link to … from text:</code> for guide-to-guide Markdown links and inline <code>&lt;code&gt;</code> in the guide pages. This is <strong>expected and benign</strong> — YARD tries to treat them as Ruby code-object links. The pages render correctly and the links work on GitHub. Do not rewrite guide cross-links to silence YARD. (Genuine broken <code>{Xref}</code> in Ruby <code>lib/</code> comments are worth fixing; guide-Markdown link warnings are not.)</p>
106
+ <p><code>yard doc</code> logs <code>Cannot resolve link to … from text:</code> for guide-to-guide Markdown links and inline <code>&lt;code&gt;</code> in the guide pages. This is <strong>expected and benign</strong> — YARD tries to treat them as Ruby code-object links. The pages render correctly and the links work on GitHub. Do not rewrite guide cross-links to silence YARD. (Genuine broken <code>{Xref}</code> in Ruby <code>lib/</code> comments are worth fixing; guide-Markdown link warnings are not.)</p>
107
107
  </li></ul>
108
108
 
109
- <h2 id="label-Patterns+derived+from+real+apps">Patterns derived from real apps</h2>
109
+ <h2 id="patterns-derived-from-real-apps">Patterns derived from real apps</h2>
110
110
  <ul><li>
111
- <p><a href="/doc/guide/patterns_md.html">patterns.md</a> / <a href="/doc/guide/cookbook_md.html">cookbook.md</a> are <strong>fully anonymized</strong>: no app, business, author or domain-model names; neutral domain (<code>Account</code>, <code>Order</code>, <code>LineItem</code>, <code>Document</code>); app-wrapper-only APIs excluded or generalized (this is a public LGPL repo).</p>
111
+ <p><a href="/doc/guide/patterns_md.html">patterns.md</a> / <a href="/doc/guide/cookbook_md.html">cookbook.md</a> are <strong>fully anonymized</strong>: no app, business, author or domain-model names; neutral domain (<code>Account</code>, <code>Order</code>, <code>LineItem</code>, <code>Document</code>); app-wrapper-only APIs excluded or generalized (this is a public LGPL repo).</p>
112
112
  </li><li>
113
- <p>Document the <strong>secure</strong> variant only. Never reproduce an insecure shape (e.g. a capability token without expiry) even if that is what an app currently does — show the hardened form and call out the requirement.</p>
113
+ <p>Document the <strong>secure</strong> variant only. Never reproduce an insecure shape (e.g. a capability token without expiry) even if that is what an app currently does — show the hardened form and call out the requirement.</p>
114
114
  </li></ul>
115
115
 
116
116
  <p><a href="/README_md.html#guide--documentation">Guide index</a></p>
117
117
  </div></div>
118
118
 
119
119
  <div id="footer">
120
- Generated on Tue Jun 30 22:41:20 2026 by
120
+ Generated on Tue Aug 18 14:36:35 2026 by
121
121
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
122
122
  0.9.34 (ruby-3.4.9).
123
123
  </div>
@@ -60,7 +60,7 @@
60
60
  <div id="content"><div id='filecontents'>
61
61
  <p><a href="/README_md.html#guide--documentation">Back to the guide</a></p>
62
62
 
63
- <h1 id="label-Model+fields">Model fields</h1>
63
+ <h1 id="model-fields">Model fields</h1>
64
64
 
65
65
  <p>Compony fields are your models’ attributes that you wish to expose in your application’s UI. They are a central place to store important information about those attributes, accessible from everywhere and without the need for a database connection.</p>
66
66
 
@@ -111,7 +111,7 @@
111
111
  <span class='kw'>end</span>
112
112
  </code></pre>
113
113
 
114
- <h2 id="label-Implementing+your+own+fields">Implementing your own fields</h2>
114
+ <h2 id="implementing-your-own-fields">Implementing your own fields</h2>
115
115
 
116
116
  <p>You can implement your own model fields. Make sure they are all within the same namespace and inherit at least from <code>Compony::ModelFields::Base</code>. To enable them, write an initializer that overwrites the array <code>Compony.model_field_namespaces</code>. Namespaces listed in the array are prioritized from first to last. If a field (e.g. <code>String</code>) exists in multiple declared namespaces, the first will be used. This allows you to overwrite Compony fields.</p>
117
117
 
@@ -127,7 +127,7 @@
127
127
  </div></div>
128
128
 
129
129
  <div id="footer">
130
- Generated on Tue Jun 30 22:41:19 2026 by
130
+ Generated on Tue Aug 18 14:36:34 2026 by
131
131
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
132
132
  0.9.34 (ruby-3.4.9).
133
133
  </div>
@@ -60,7 +60,7 @@
60
60
  <div id="content"><div id='filecontents'>
61
61
  <p><a href="/README_md.html#guide--documentation">Back to the guide</a></p>
62
62
 
63
- <h1 id="label-Nesting">Nesting</h1>
63
+ <h1 id="nesting">Nesting</h1>
64
64
 
65
65
  <p>Components can be arbitrarily nested. This means that any component exposing content can instantiate an arbitrary number of sub-components that will be rendered as part of its own content. This results in a component tree. Sub-components are aware of the nesting and even of their position within the parent. The topmost component is called the <strong>root component</strong> and it’s the only component that must be standalone. If you instead render the topmost component from a custom view, there is conceptually no root component, but Compony has no way to detect this special case.</p>
66
66
 
@@ -123,7 +123,7 @@ The number 3 has the binary form 11.
123
123
 
124
124
  <p>However, this is static and no fun. We cannot use the HTTP GET parameter any more because all three <code>Binary</code> sub-components listen to the same parameter <code>number</code>. To fix this, we will need to scope the parameter using the <code>param_name</code> as explained in the next subsection.</p>
125
125
 
126
- <h2 id="label-Proper+parameter+naming+for+-28nested-29+components">Proper parameter naming for (nested) components</h2>
126
+ <h2 id="proper-parameter-naming-for-nested-components">Proper parameter naming for (nested) components</h2>
127
127
 
128
128
  <p>As seen above, components can be arbitrarily nested, making it harder to identify which HTTP GET parameter in the request is intended for which component. To resolve this, Compony provides nesting-aware scoping of parameter names:</p>
129
129
  <ul><li>
@@ -198,7 +198,7 @@ The number 8 has the binary form 1000. Enter a number and press ENTER: [8]
198
198
 
199
199
  <p>Note that this example is completely stateless, as all the info is encoded in the URL.</p>
200
200
 
201
- <h2 id="label-Rendering+List+as+sub+comp+in+Show">Rendering <code>List</code> as sub comp in <code>Show</code></h2>
201
+ <h2 id="rendering-list-as-sub-comp-in-show">Rendering <code>List</code> as sub comp in <code>Show</code></h2>
202
202
 
203
203
  <p>A pattern often used is the following:</p>
204
204
 
@@ -220,14 +220,14 @@ The number 8 has the binary form 1000. Enter a number and press ENTER: [8]
220
220
  </li><li>
221
221
  <p><code>render_sub_comp</code> is thus given an <code>ActiveRecord</code> collection of <code>Quote</code> models and it builts an <a href="/doc/guide/intents_md.html">Intent</a> to figure out that <code>Components::Quotes::List</code> is the component that will be instanciated, given the appropriate quotes, and rendered here.</p>
222
222
  </li><li>
223
- <p>We also give <code>:&quot;user_#{@data.id}_quotes&quot;</code> to the parameter <code>turbo_frame</code>, which causes <code>render_sub_comp</code> to place the sub comp inside a frame that is named something like <code>:user_1_quotes</code>. Since compony’s List component[/doc/guide/pre_built_components/list.md] contains search and filter forms, the turbo frame makes sure that anything entered there does not interfere with other parameters.</p>
223
+ <p>We also give <code>:&quot;user_#{@data.id}_quotes&quot;</code> to the parameter <code>turbo_frame</code>, which causes <code>render_sub_comp</code> to place the sub comp inside a frame that is named something like <code>:user_1_quotes</code>. Since compony’s <a href="/doc/guide/pre_built_components/list_md.html">pre-built List component</a> contains search and filter forms, the turbo frame makes sure that anything entered there does not interfere with other parameters.</p>
224
224
  </li></ul>
225
225
 
226
226
  <p><a href="/README_md.html#guide--documentation">Guide index</a></p>
227
227
  </div></div>
228
228
 
229
229
  <div id="footer">
230
- Generated on Tue Jun 30 22:41:18 2026 by
230
+ Generated on Tue Aug 18 14:36:33 2026 by
231
231
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
232
232
  0.9.34 (ruby-3.4.9).
233
233
  </div>
data/doc/file.new.html CHANGED
@@ -63,11 +63,11 @@
63
63
  <p><a href="/doc/guide/pre_built_components_md.html">List of pre-built components</a></p>
64
64
  </li></ul>
65
65
 
66
- <h1 id="label-Pre-built+components-3A+New">Pre-built components: New</h1>
66
+ <h1 id="pre-built-components-new">Pre-built components: New</h1>
67
67
 
68
68
  <p>This component is the Compony equivalent to a typical Rails controller’s <code>new</code> and <code>create</code> actions.</p>
69
69
 
70
- <p><code>Compony::Components::New</code> is a resourceful standalone component based on href="./with_form_md.html"></a> that listens to two verbs:</p>
70
+ <p><code>Compony::Components::New</code> is a resourceful standalone component based on <a href="./with_form_md.html"><code>WithForm</code></a> that listens to two verbs:</p>
71
71
  <ul><li>
72
72
  <p>GET will cause the New component to create a fresh instance of its <code>data_class</code> and render the form.</p>
73
73
  </li><li>
@@ -99,7 +99,7 @@
99
99
  </div></div>
100
100
 
101
101
  <div id="footer">
102
- Generated on Tue Jun 30 22:41:19 2026 by
102
+ Generated on Tue Aug 18 14:36:34 2026 by
103
103
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
104
104
  0.9.34 (ruby-3.4.9).
105
105
  </div>
@@ -60,7 +60,7 @@
60
60
  <div id="content"><div id='filecontents'>
61
61
  <p><a href="/README_md.html#guide--documentation">Back to the guide</a></p>
62
62
 
63
- <h1 id="label-Ownership">Ownership</h1>
63
+ <h1 id="ownership">Ownership</h1>
64
64
 
65
65
  <p>Ownership is a concept that captures the nature of data to be presented by Compony. It means that an object only makes sense within the context of another that it belongs to. Owned objects have therefore no index component, because they don’t have meaning on their own. For instance:</p>
66
66
  <ul><li>
@@ -88,7 +88,7 @@
88
88
  </div></div>
89
89
 
90
90
  <div id="footer">
91
- Generated on Tue Jun 30 22:41:19 2026 by
91
+ Generated on Tue Aug 18 14:36:34 2026 by
92
92
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
93
93
  0.9.34 (ruby-3.4.9).
94
94
  </div>