compony 0.11.8 → 0.11.9

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 (132) hide show
  1. checksums.yaml +4 -4
  2. data/.yardopts +36 -1
  3. data/CHANGELOG.md +31 -0
  4. data/CLAUDE.md +85 -0
  5. data/Gemfile.lock +1 -1
  6. data/README.md +13 -3
  7. data/VERSION +1 -1
  8. data/compony.gemspec +3 -3
  9. data/doc/ComponentGenerator.html +1 -1
  10. data/doc/Components.html +1 -1
  11. data/doc/ComponentsGenerator.html +1 -1
  12. data/doc/Compony/Component.html +54 -54
  13. data/doc/Compony/ComponentMixins/Default/Labelling.html +1 -1
  14. data/doc/Compony/ComponentMixins/Default/Standalone/ResourcefulVerbDsl.html +1 -1
  15. data/doc/Compony/ComponentMixins/Default/Standalone/StandaloneDsl.html +109 -70
  16. data/doc/Compony/ComponentMixins/Default/Standalone/VerbDsl.html +64 -28
  17. data/doc/Compony/ComponentMixins/Default/Standalone.html +1 -1
  18. data/doc/Compony/ComponentMixins/Default.html +1 -1
  19. data/doc/Compony/ComponentMixins/Resourceful.html +213 -74
  20. data/doc/Compony/ComponentMixins.html +1 -1
  21. data/doc/Compony/Components/Buttons/CssButton.html +1 -1
  22. data/doc/Compony/Components/Buttons/Link.html +1 -1
  23. data/doc/Compony/Components/Buttons.html +1 -1
  24. data/doc/Compony/Components/Destroy.html +83 -29
  25. data/doc/Compony/Components/Edit.html +110 -38
  26. data/doc/Compony/Components/Form.html +551 -208
  27. data/doc/Compony/Components/Index.html +1 -1
  28. data/doc/Compony/Components/List.html +3 -3
  29. data/doc/Compony/Components/New.html +110 -38
  30. data/doc/Compony/Components/Show.html +1 -1
  31. data/doc/Compony/Components/WithForm.html +194 -47
  32. data/doc/Compony/Components.html +1 -1
  33. data/doc/Compony/ControllerMixin.html +1 -1
  34. data/doc/Compony/Engine.html +1 -1
  35. data/doc/Compony/Intent.html +2 -2
  36. data/doc/Compony/ManageIntentsDsl.html +1 -1
  37. data/doc/Compony/MethodAccessibleHash.html +1 -1
  38. data/doc/Compony/ModelFields/Anchormodel.html +1 -1
  39. data/doc/Compony/ModelFields/Association.html +1 -1
  40. data/doc/Compony/ModelFields/Attachment.html +1 -1
  41. data/doc/Compony/ModelFields/Base.html +1 -1
  42. data/doc/Compony/ModelFields/Boolean.html +1 -1
  43. data/doc/Compony/ModelFields/Color.html +1 -1
  44. data/doc/Compony/ModelFields/Currency.html +1 -1
  45. data/doc/Compony/ModelFields/Date.html +1 -1
  46. data/doc/Compony/ModelFields/Datetime.html +1 -1
  47. data/doc/Compony/ModelFields/Decimal.html +1 -1
  48. data/doc/Compony/ModelFields/Email.html +1 -1
  49. data/doc/Compony/ModelFields/Float.html +1 -1
  50. data/doc/Compony/ModelFields/Integer.html +1 -1
  51. data/doc/Compony/ModelFields/Percentage.html +1 -1
  52. data/doc/Compony/ModelFields/Phone.html +1 -1
  53. data/doc/Compony/ModelFields/RichText.html +1 -1
  54. data/doc/Compony/ModelFields/String.html +1 -1
  55. data/doc/Compony/ModelFields/Text.html +1 -1
  56. data/doc/Compony/ModelFields/Time.html +1 -1
  57. data/doc/Compony/ModelFields/Url.html +1 -1
  58. data/doc/Compony/ModelFields.html +1 -1
  59. data/doc/Compony/ModelMixin.html +1 -1
  60. data/doc/Compony/NaturalOrdering.html +1 -1
  61. data/doc/Compony/RequestContext.html +1 -1
  62. data/doc/Compony/Version.html +1 -1
  63. data/doc/Compony/ViewHelpers.html +1 -1
  64. data/doc/Compony/VirtualModel.html +1 -1
  65. data/doc/Compony.html +1 -1
  66. data/doc/ComponyController.html +1 -1
  67. data/doc/_index.html +97 -1
  68. data/doc/file.CHANGELOG.html +758 -0
  69. data/doc/file.README.html +25 -4
  70. data/doc/file.basic_component.html +314 -0
  71. data/doc/file.cookbook.html +189 -0
  72. data/doc/file.destroy.html +105 -0
  73. data/doc/file.dsl_reference.html +672 -0
  74. data/doc/file.edit.html +109 -0
  75. data/doc/file.example.html +291 -0
  76. data/doc/file.example_advanced.html +257 -0
  77. data/doc/file.feasibility.html +115 -0
  78. data/doc/file.form.html +195 -0
  79. data/doc/file.generators.html +89 -0
  80. data/doc/file.glossary.html +217 -0
  81. data/doc/file.gotchas.html +222 -0
  82. data/doc/file.index.html +135 -0
  83. data/doc/file.inheritance.html +136 -0
  84. data/doc/file.installation.html +115 -0
  85. data/doc/file.integrations.html +218 -0
  86. data/doc/file.intents.html +265 -0
  87. data/doc/file.internal_datastructures.html +129 -0
  88. data/doc/file.list.html +253 -0
  89. data/doc/file.maintaining.html +127 -0
  90. data/doc/file.model_fields.html +137 -0
  91. data/doc/file.nesting.html +237 -0
  92. data/doc/file.new.html +109 -0
  93. data/doc/file.ownership.html +98 -0
  94. data/doc/file.patterns.html +669 -0
  95. data/doc/file.pre_built_components.html +99 -0
  96. data/doc/file.resourceful.html +181 -0
  97. data/doc/file.show.html +158 -0
  98. data/doc/file.standalone.html +233 -0
  99. data/doc/file.virtual_models.html +117 -0
  100. data/doc/file.with_form.html +157 -0
  101. data/doc/file_list.html +160 -0
  102. data/doc/guide/cookbook.md +41 -0
  103. data/doc/guide/dsl_reference.md +155 -0
  104. data/doc/guide/example_advanced.md +209 -0
  105. data/doc/guide/generators.md +1 -1
  106. data/doc/guide/glossary.md +42 -0
  107. data/doc/guide/gotchas.md +125 -0
  108. data/doc/guide/maintaining.md +64 -0
  109. data/doc/guide/patterns.md +681 -0
  110. data/doc/guide/pre_built_components/edit.md +1 -1
  111. data/doc/guide/pre_built_components/index.md +64 -1
  112. data/doc/guide/pre_built_components/list.md +111 -7
  113. data/doc/guide/pre_built_components/show.md +57 -2
  114. data/doc/guide/pre_built_components/with_form.md +56 -9
  115. data/doc/guide/pre_built_components.md +7 -2
  116. data/doc/guide/standalone.md +16 -1
  117. data/doc/index.html +25 -4
  118. data/doc/integrations.md +61 -0
  119. data/doc/llms.txt +62 -0
  120. data/doc/top-level-namespace.html +1 -1
  121. data/lib/compony/component.rb +8 -3
  122. data/lib/compony/component_mixins/default/standalone/standalone_dsl.rb +32 -15
  123. data/lib/compony/component_mixins/default/standalone/verb_dsl.rb +11 -3
  124. data/lib/compony/component_mixins/resourceful.rb +30 -16
  125. data/lib/compony/components/destroy.rb +21 -1
  126. data/lib/compony/components/edit.rb +25 -1
  127. data/lib/compony/components/form.rb +63 -21
  128. data/lib/compony/components/list.rb +1 -1
  129. data/lib/compony/components/new.rb +25 -1
  130. data/lib/compony/components/with_form.rb +20 -5
  131. data/lib/compony/intent.rb +1 -1
  132. metadata +43 -1
@@ -0,0 +1,218 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>
7
+ File: integrations
8
+
9
+ &mdash; Documentation by YARD 0.9.34
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" />
16
+
17
+ <script type="text/javascript">
18
+ pathId = "integrations";
19
+ relpath = '';
20
+ </script>
21
+
22
+
23
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
24
+
25
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
26
+
27
+
28
+ </head>
29
+ <body>
30
+ <div class="nav_wrap">
31
+ <iframe id="nav" src="file_list.html?1"></iframe>
32
+ <div id="resizer"></div>
33
+ </div>
34
+
35
+ <div id="main" tabindex="-1">
36
+ <div id="header">
37
+ <div id="menu">
38
+
39
+ <a href="_index.html">Index</a> &raquo;
40
+ <span class="title">File: integrations</span>
41
+
42
+ </div>
43
+
44
+ <div id="search">
45
+
46
+ <a class="full_list_link" id="class_list_link"
47
+ href="class_list.html">
48
+
49
+ <svg width="24" height="24">
50
+ <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
51
+ <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
52
+ <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
53
+ </svg>
54
+ </a>
55
+
56
+ </div>
57
+ <div class="clear"></div>
58
+ </div>
59
+
60
+ <div id="content"><div id='filecontents'>
61
+ <p><a href="/README_md.html#guide--documentation">Back to the guide</a></p>
62
+
63
+ <h1 id="label-Integrations+-26+companion+gems">Integrations &amp; companion gems</h1>
64
+
65
+ <p>What Compony pulls in, what it optionally lights up, and what you are expected to add yourself. The authoritative source for hard dependencies and their version constraints is the <code>:gemspec</code> task in href="/Rakefile"></a> (it generates <code>compony.gemspec</code>); the table below mirrors it and must be updated together with it (see <a href="/doc/guide/maintaining_md.html">maintaining.md</a>).</p>
66
+
67
+ <h2 id="label-Hard+runtime+dependencies+-28installed+automatically-29">Hard runtime dependencies (installed automatically)</h2>
68
+
69
+ <table role="table">
70
+ <thead>
71
+ <tr>
72
+ <th>Gem</th>
73
+ <th>Constraint</th>
74
+ <th>Why Compony needs it</th>
75
+ </tr>
76
+ </thead>
77
+ <tbody>
78
+ <tr>
79
+ <td>‘rails`</td>
80
+ <td>‘&gt;= 7.2.1`</td>
81
+ <td>Routing, controllers, views Compony plugs into.</td>
82
+ </tr>
83
+ <tr>
84
+ <td>‘request_store`</td>
85
+ <td>‘&gt;= 1.7`</td>
86
+ <td>Per-request storage (e.g. ‘Compony.root_comp`).</td>
87
+ </tr>
88
+ <tr>
89
+ <td>‘dyny`</td>
90
+ <td>‘&gt;= 0.0.3`</td>
91
+ <td>HTML-as-Ruby templating used in ‘content` blocks.</td>
92
+ </tr>
93
+ <tr>
94
+ <td>‘schemacop`</td>
95
+ <td>‘&gt;= 3.0.17`</td>
96
+ <td>Strong-param schema validation behind ‘schema_field` / `schema_line`.</td>
97
+ </tr>
98
+ <tr>
99
+ <td>‘simple_form`</td>
100
+ <td>‘&gt;= 5.3.1`</td>
101
+ <td>The form builder behind the Form component’s ‘field`.</td>
102
+ </tr>
103
+ <tr>
104
+ <td>‘dslblend`</td>
105
+ <td>‘&gt;= 0.0.3`</td>
106
+ <td>Powers the ‘RequestContext` multi-provider DSL.</td>
107
+ </tr>
108
+ <tr>
109
+ <td>‘anchormodel`</td>
110
+ <td>‘&gt;= 0.3.0`</td>
111
+ <td>‘anchormodel` model-field type + enum-like associations.</td>
112
+ </tr>
113
+ <tr>
114
+ <td>‘cancancan`</td>
115
+ <td>‘~&gt; 3.6.1`</td>
116
+ <td>Authorization: ‘authorize { can?(…) }`, `accessible_by`, per-field `permitted_attributes`.</td>
117
+ </tr>
118
+ </tbody>
119
+ </table>
120
+
121
+ <p>The Ruby floor is <code>&gt;= 3.3.5</code> (<code>required_ruby_version</code>).</p>
122
+
123
+ <blockquote>
124
+ <p>Version note: the gemspec requires <code>rails &gt;= 7.2.1</code>. If the README’s prose mentions an older range, the gemspec wins — keep them in sync when bumping (see maintaining.md).</p>
125
+ </blockquote>
126
+
127
+ <h2 id="label-Optional+-E2-80-94+presence+unlocks+a+feature">Optional — presence unlocks a feature</h2>
128
+
129
+ <p>These are <strong>not</strong> declared dependencies; Compony detects them and enables behavior if they are in the host app’s bundle.</p>
130
+
131
+ <table role="table">
132
+ <thead>
133
+ <tr>
134
+ <th>Gem</th>
135
+ <th>Unlocks</th>
136
+ </tr>
137
+ </thead>
138
+ <tbody>
139
+ <tr>
140
+ <td>‘active_type`</td>
141
+ <td>‘Compony::VirtualModel` (loaded only if `ActiveType::Object` is defined). Non-persistent / upload / wizard forms — [patterns §12](/doc/guide/patterns.md#12-virtual-model-for-non-persistent–upload-forms), [virtual_models.md](/doc/guide/virtual_models.md).</td>
142
+ </tr>
143
+ <tr>
144
+ <td>‘ransack`</td>
145
+ <td>List filtering, sorting links and the sort select in [‘List`](/doc/guide/pre_built_components/list.md). Without it, declare no `filter`/`sort` and those UIs stay off.</td>
146
+ </tr>
147
+ </tbody>
148
+ </table>
149
+
150
+ <h2 id="label-App-side+companions+-28you+add+these-29">App-side companions (you add these)</h2>
151
+
152
+ <p>Compony is UI- and auth-agnostic; these are conventional choices in real apps, pulled in by your app, not by Compony.</p>
153
+
154
+ <table role="table">
155
+ <thead>
156
+ <tr>
157
+ <th>Concern</th>
158
+ <th>Typical choice</th>
159
+ <th>Used by</th>
160
+ </tr>
161
+ </thead>
162
+ <tbody>
163
+ <tr>
164
+ <td>Authentication</td>
165
+ <td>Devise, or a custom login component + ‘Compony.authentication_before_action=`</td>
166
+ <td>Compony ships <strong>no</strong> auth ([README](/README.md)); token-link flows → [patterns §18](/doc/guide/patterns.md#18-signed-token-capability-links-auth-less-onboarding–magic-links)</td>
167
+ </tr>
168
+ <tr>
169
+ <td>Turbo / Stimulus</td>
170
+ <td>‘turbo-rails`, `stimulus-rails`</td>
171
+ <td>Compony fully supports Turbo Drive; inline-edit → [patterns §15](/doc/guide/patterns.md#15-inline-edit-card-with-a-turbo-frame), ajax PATCH → [patterns §17](/doc/guide/patterns.md#17-inline-patch-without-a-form-reorder–quick-toggle)</td>
172
+ </tr>
173
+ <tr>
174
+ <td>Select / date inputs</td>
175
+ <td>TomSelect, Flatpickr (as registered ‘simple_form` inputs)</td>
176
+ <td>‘field(:x, as: :tom_select/:flatpickr_*)` — [patterns §5–6](/doc/guide/patterns.md#5-custom-form–schemacop-kept-in-sync)</td>
177
+ </tr>
178
+ <tr>
179
+ <td>i18n</td>
180
+ <td>Rails I18n and/or FastGettext</td>
181
+ <td>Component labels; gem ships ‘config/locales/de,en,fr.yml`</td>
182
+ </tr>
183
+ <tr>
184
+ <td>File handling</td>
185
+ <td>ActiveStorage (+ a variant/processor)</td>
186
+ <td>Attachment fields, virtual-model uploads — [patterns §12](/doc/guide/patterns.md#12-virtual-model-for-non-persistent–upload-forms)</td>
187
+ </tr>
188
+ <tr>
189
+ <td>PDF / CSV</td>
190
+ <td>Prawn / wicked_pdf, Ruby ‘CSV`</td>
191
+ <td>Export endpoints — [patterns §10](/doc/guide/patterns.md#10-csv–pdf-via-respond-format)</td>
192
+ </tr>
193
+ <tr>
194
+ <td>Signed tokens</td>
195
+ <td>‘jwt`</td>
196
+ <td>Capability links — [patterns §18](/doc/guide/patterns.md#18-signed-token-capability-links-auth-less-onboarding–magic-links)</td>
197
+ </tr>
198
+ </tbody>
199
+ </table>
200
+
201
+ <p>Incompatibility: <code>tailwindcss-rails</code> purges Compony component CSS (component HTML is not in <code>app/views</code>); see <a href="/doc/guide/gotchas_md.html#13-tailwindcss-rails-purges-compony-styles">gotchas.md #13</a>.</p>
202
+
203
+ <h2 id="label-Development+dependencies">Development dependencies</h2>
204
+
205
+ <p><code>yard &gt;= 0.9.28</code> (docs), <code>rubocop &gt;= 1.48</code>, <code>rubocop-rails &gt;= 2.18.0</code> (lint).</p>
206
+
207
+ <p><a href="/README_md.html#guide--documentation">Guide index</a></p>
208
+ </div></div>
209
+
210
+ <div id="footer">
211
+ Generated on Mon May 18 13:55:34 2026 by
212
+ <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
213
+ 0.9.34 (ruby-3.3.5).
214
+ </div>
215
+
216
+ </div>
217
+ </body>
218
+ </html>
@@ -0,0 +1,265 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>
7
+ File: intents
8
+
9
+ &mdash; Documentation by YARD 0.9.34
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" />
16
+
17
+ <script type="text/javascript">
18
+ pathId = "intents";
19
+ relpath = '';
20
+ </script>
21
+
22
+
23
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
24
+
25
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
26
+
27
+
28
+ </head>
29
+ <body>
30
+ <div class="nav_wrap">
31
+ <iframe id="nav" src="file_list.html?1"></iframe>
32
+ <div id="resizer"></div>
33
+ </div>
34
+
35
+ <div id="main" tabindex="-1">
36
+ <div id="header">
37
+ <div id="menu">
38
+
39
+ <a href="_index.html">Index</a> &raquo;
40
+ <span class="title">File: intents</span>
41
+
42
+ </div>
43
+
44
+ <div id="search">
45
+
46
+ <a class="full_list_link" id="class_list_link"
47
+ href="class_list.html">
48
+
49
+ <svg width="24" height="24">
50
+ <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
51
+ <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
52
+ <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
53
+ </svg>
54
+ </a>
55
+
56
+ </div>
57
+ <div class="clear"></div>
58
+ </div>
59
+
60
+ <div id="content"><div id='filecontents'>
61
+ <p><a href="/README_md.html#guide--documentation">Back to the guide</a></p>
62
+
63
+ <h1 id="label-Intents">Intents</h1>
64
+
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
+
67
+ <p>To instanciate an intent, use either the raw <code>Compony.intent</code> method or one of the <a href="#helpers">helpers</a>. All methods that use intents under the hood have a similar interface, especially regarding the first two positional arguments. The most commonly used forms are:</p>
68
+ <ul><li>
69
+ <p><code>Compony.intent(:index, :users)</code>: point to a component by its comp and family name</p>
70
+ </li><li>
71
+ <p><code>Compony.intent(:show, User.first)</code>: pass the intent a single model and let it figure out the family name from <code>model_name</code></p>
72
+ </li><li>
73
+ <p><code>Compony.intent(:list, current_user.quotes)</code>: pass the intent an active record collection and let it figure out the family name from <code>model_name</code></p>
74
+ </li><li>
75
+ <p><code>Compony.intent(Components::Users::Index)</code>: point to a component by giving its class as a single argument</p>
76
+ </li></ul>
77
+
78
+ <p>The returned intent can then be used to:</p>
79
+ <ul><li>
80
+ <p>Retrieve the target component class using <code>intent.comp_class</code></p>
81
+ </li><li>
82
+ <p>Build an instance of the target component using <code>intent.comp</code></p>
83
+ <ul><li>
84
+ <p>If a model was given when building the intent, such as in the second form above, the comp instance will contain it as <code>@data</code>.</p>
85
+ </li></ul>
86
+ </li><li>
87
+ <p>Retrieve the <code>path</code> to the target component (only works for standalone components; will automatically set ID parameter if a model was given)</p>
88
+ </li><li>
89
+ <p>Retrieve the <code>name</code> that can be used to store the intent in a hash or similar</p>
90
+ </li><li>
91
+ <p>Retrieve the <code>label</code> that can be used to refer to the component (if a model was given, passes it to the target component’s label block)</p>
92
+ </li><li>
93
+ <p>Check for <a href="/doc/guide/feasibility_md.html">feasibility</a> using <code>feasible?</code></p>
94
+ </li><li>
95
+ <p>Render a <a href="#buttons-and-styles">button</a> to the target component which automatically includes all of the above along with the suitable behavior using <code>render</code> and passing a controller</p>
96
+ </li></ul>
97
+
98
+ <p>An intent’s behavior can be customized by passing some of the following keyword arguments when building it:</p>
99
+ <ul><li>
100
+ <p><code>standalone_name</code> allows you to point to another endpoint within your target component, which is especially useful for generating paths. Keep in mind that within each standalone name, multiple HTTP methods can exist. This argument defaults to <code>nil</code>, which is the main endpoint created by <code>standalone</code>.</p>
101
+ </li><li>
102
+ <p><code>method</code> defines the HTTP verb within the standalone configuration that should be addressed. Defaults to <code>:get</code>, but can be overriden to be <code>:patch</code>, <code>:put</code>, <code>:post</code> or <code>:delete</code>. When generating a button, the <code>turbo_method</code> will be automatically be derived from this argument.</p>
103
+ </li><li>
104
+ <p><code>name</code> overrides the auto-generated name of the intent, affecting the result of the reader of the same name.</p>
105
+ </li><li>
106
+ <p><code>label</code> accepts two forms:</p>
107
+ <ul><li>
108
+ <p>When passing a String, it will be returned as-is when returning the label, also affecting buttons generated by this intent.</p>
109
+ </li><li>
110
+ <p>When passing a Hash, any included key-value pair will be used as keyword arguments to the <code>label</code> reader method.</p>
111
+ </li></ul>
112
+ </li><li>
113
+ <p><code>path</code> allows altering generated paths and accepts either a String or a Hash just like <code>label</code>.</p>
114
+ </li><li>
115
+ <p><code>data</code> and <code>data_class</code> will be given to the target component if/when it gets instanciated. This is used to point to <a href="/doc/guide/resourceful_md.html">resourceful</a> components. If a model was passed as the second positional argument (instead of a family name), it will become <code>data</code> and this argument can be omitted. If <code>data</code> responds to <code>model_name</code>, it will be considered a model-like class.</p>
116
+ </li><li>
117
+ <p><code>feasibility_target</code> and <code>feasibility_action</code> can be given to alter the behavior of the <code>feasible?</code> reader.</p>
118
+ </li><li>
119
+ <p>Any further arguments are passed to the initializer of the button if/when the intent gets rendered.</p>
120
+ </li></ul>
121
+
122
+ <h2 id="label-Helpers">Helpers</h2>
123
+
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
+
126
+ <h3 id="label-Compony.path"><code>Compony.path</code></h3>
127
+
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
+
130
+ <p>Examples:</p>
131
+
132
+ <pre class="code ruby"><code class="ruby"><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'>:index</span><span class='comma'>,</span> <span class='symbol'>:users</span><span class='rparen'>)</span> <span class='comment'># Redirects to /users
133
+ </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'>:show</span><span class='comma'>,</span> <span class='ivar'>@data</span><span class='period'>.</span><span class='id identifier rubyid_author'>author</span><span class='rparen'>)</span> <span class='comment'># Redirects to something like /authors/42
134
+ </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'>:thank_you</span><span class='comma'>,</span> <span class='ivar'>@data</span><span class='rparen'>)</span> <span class='comment'># Redirects to something like /feedbacks/42/thank_you
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
+ </span></code></pre>
137
+
138
+ <h3 id="label-render_intent"><code>render_intent</code></h3>
139
+
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
+ <ul><li>
142
+ <p>When called from within a <code>content</code> block of a component, the method implemented in the <code>RequestContext</code> is used, which automatically detects the component from which it is called and passes it as <code>parent_comp</code> to the rendered button.</p>
143
+ </li><li>
144
+ <p>When called from a regular Rails view, the Rails helper method is used, which instanciates a button without passing a <code>parent_comp</code>.</p>
145
+ </li></ul>
146
+
147
+ <p>Use the <code>button</code> argument to customize the generated button. Example:</p>
148
+
149
+ <pre class="code ruby"><code class="ruby">setup do
150
+ content do
151
+ div render_intent(:show, User.first, style: :link, label: { format: :short })`
152
+ end
153
+ end
154
+ </code></pre>
155
+
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
+
158
+ <h3 id="label-render_sub_comp"><code>render_sub_comp</code></h3>
159
+
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
+
162
+ <p>For example, let us consider you want to build your user management’s Show component to include the list of quotes belonging to the displayed user. Assuming you have already built <code>Components::Quotes::List</code> for quotes’ Index component, this helper allows you to simply write:</p>
163
+
164
+ <pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'><span class='object_link'><a href="Components.html" title="Components (module)">Components</a></span></span><span class='op'>::</span><span class='const'>Users</span><span class='op'>::</span><span class='const'>Show</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/Show.html" title="Compony::Components::Show (class)">Show</a></span></span>
165
+ <span class='id identifier rubyid_setup'>setup</span> <span class='kw'>do</span>
166
+ <span class='comment'># ...
167
+ </span> <span class='id identifier rubyid_content'>content</span> <span class='symbol'>:quotes</span> <span class='kw'>do</span>
168
+ <span class='id identifier rubyid_concat'>concat</span> <span class='id identifier rubyid_render_sub_comp'>render_sub_comp</span><span class='lparen'>(</span><span class='symbol'>:list</span><span class='comma'>,</span> <span class='ivar'>@data</span><span class='period'>.</span><span class='id identifier rubyid_quotes'>quotes</span><span class='rparen'>)</span>
169
+ <span class='kw'>end</span>
170
+ <span class='kw'>end</span>
171
+ <span class='kw'>end</span>
172
+ </code></pre>
173
+
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
+
176
+ <h3 id="label-Compony.comp_class_for"><code>Compony.comp_class_for</code></h3>
177
+
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
+
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
+
182
+ <h2 id="label-Buttons+and+styles">Buttons and styles</h2>
183
+
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
+
186
+ <p>Compony comes with two button styles:</p>
187
+ <ul><li>
188
+ <p><code>:css_button</code> is the default button style and creates a div that looks similar to a HTML button rendered in Firefox. If the class <code>disabled</code> is given, it is greyed out.</p>
189
+ </li><li>
190
+ <p><code>:link</code> is mostly just a regular <code>&lt;a&gt;</code> tag, but will appear greyed out and strikethrough if the class <code>disabled</code> is given.</p>
191
+ </li></ul>
192
+
193
+ <p>All button styles support the following keyword arguments to their initializer:</p>
194
+ <ul><li>
195
+ <p><code>label</code> is a String which will be displayed as the text of the link or button.</p>
196
+ </li><li>
197
+ <p><code>href</code> is the url/path that the button points to. If <code>nil</code>, will change to <code>javascript:void(0)</code>.</p>
198
+ </li><li>
199
+ <p><code>method</code> takes a HTTP verb will generate a suitable <code>turbo_method</code> data attribute.</p>
200
+ </li><li>
201
+ <p><code>class</code> will mostly let be as-is, but checked for the <code>disabled</code> class - if given, the style will be ovewritten.</p>
202
+ </li></ul>
203
+
204
+ <p>Note that since buttons are full components, they can be <a href="/doc/guide/nesting_md.html">nested</a> into another component by providing <code>parent_comp</code>. If called from within a component’s <code>content</code> block, the helper <code>render_intent</code> does this automatically.</p>
205
+
206
+ <p>As implicitely mentioned above, Compony buttons are referenced to by a name called a style (<code>:css_button</code> actually points to <code>Compony::Components::Buttons::CssButton</code>). When rendering an intent, the style can be passed as an argument: <code>render_intent(:show, User.first, style: :link)</code> and the intent will automatically instanciate the desired component class.</p>
207
+
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
+
210
+ <h3 id="label-Adding+your+own+styles">Adding your own styles</h3>
211
+
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
+
214
+ <p>Once your button class is ready, register it in <code>config/initializers/compony.rb</code> with: <code>Compony.register_button_style :my_button, &#39;::Components::Commons::MyButton&#39;</code>. You can also change the default button style there using: <code>Compony.default_button_style = :my_button</code>.</p>
215
+
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
+
218
+ <h2 id="label-Exposed+intents">Exposed intents</h2>
219
+
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
+
222
+ <p>To expose an intent, proceed as shown in the following example:</p>
223
+
224
+ <pre class="code ruby"><code class="ruby"><span class='comment'># ...
225
+ </span><span class='kw'>class</span> <span class='const'><span class='object_link'><a href="Components.html" title="Components (module)">Components</a></span></span><span class='op'>::</span><span class='const'>Quotes</span><span class='op'>::</span><span class='const'>Show</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/Show.html" title="Compony::Components::Show (class)">Show</a></span></span>
226
+ <span class='id identifier rubyid_setup'>setup</span> <span class='kw'>do</span>
227
+ <span class='id identifier rubyid_exposed_intents'>exposed_intents</span> <span class='kw'>do</span>
228
+ <span class='id identifier rubyid_add'>add</span> <span class='symbol'>:index</span><span class='comma'>,</span> <span class='id identifier rubyid_family_name'>family_name</span><span class='comma'>,</span> <span class='label'>label:</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>Show all</span><span class='tstring_end'>&#39;</span></span><span class='comma'>,</span> <span class='label'>name:</span> <span class='symbol'>:index</span>
229
+ <span class='id identifier rubyid_remove'>remove</span> <span class='symbol'>:destroy</span>
230
+ <span class='kw'>end</span>
231
+ <span class='kw'>end</span>
232
+ <span class='kw'>end</span>
233
+ </code></pre>
234
+
235
+ <p>In this example, the shown component exposes an intent pointing to the Index component of it’s own family (<code>:users</code>). The <code>:name</code> argument causes the new intent to be just named <code>:index</code> rather than <code>:index_users</code>. This is for the sake of the example and would allow a component inheriting from this component to use <code>exposed_intents { remove :index }</code> rather than <code>exposed_intents { remove :index_users }</code>.</p>
236
+
237
+ <p>Similarly, this component removes the exposed intent <code>:destroy</code> which it whould otherwise inherit from <code>Comopony::Components::Show</code>. Every call to <code>exposed_intents</code> overrides properties set in previous calls by the same component, primarly useful for <a href="/doc/guide/inheritance_md.html">reusing components by inheritance</a>.</p>
238
+
239
+ <p>In order to replace an existing intent defined by a previous call to <code>exposed_intents</code> (e.g. in a parent class), simply call <code>add</code> again and make sure the intent’s name matches that of the one to override. <code>add</code> also accepts the <code>before:</code> keyword, allowing you to reorder intents or insert a new one into a specific place in the intent list.</p>
240
+
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
+
243
+ <h3 id="label-Rendering+exposed+intents">Rendering exposed intents</h3>
244
+
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
+
247
+ <p>Example in <code>layouts/application.html.erb</code></p>
248
+
249
+ <pre class="code ruby"><code class="ruby">&lt;% Compony.root_comp&amp;.exposed_intents&amp;.each do |intent| %&gt;
250
+ &lt;div class=&quot;root-intent&quot;&gt;&lt;%= intent.render(controller) %&gt;
251
+ &lt;% end %&gt;
252
+ </code></pre>
253
+
254
+ <p><a href="/README_md.html#guide--documentation">Guide index</a></p>
255
+ </div></div>
256
+
257
+ <div id="footer">
258
+ Generated on Mon May 18 13:55:33 2026 by
259
+ <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
260
+ 0.9.34 (ruby-3.3.5).
261
+ </div>
262
+
263
+ </div>
264
+ </body>
265
+ </html>
@@ -0,0 +1,129 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>
7
+ File: internal_datastructures
8
+
9
+ &mdash; Documentation by YARD 0.9.34
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" />
16
+
17
+ <script type="text/javascript">
18
+ pathId = "internal_datastructures";
19
+ relpath = '';
20
+ </script>
21
+
22
+
23
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
24
+
25
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
26
+
27
+
28
+ </head>
29
+ <body>
30
+ <div class="nav_wrap">
31
+ <iframe id="nav" src="file_list.html?1"></iframe>
32
+ <div id="resizer"></div>
33
+ </div>
34
+
35
+ <div id="main" tabindex="-1">
36
+ <div id="header">
37
+ <div id="menu">
38
+
39
+ <a href="_index.html">Index</a> &raquo;
40
+ <span class="title">File: internal_datastructures</span>
41
+
42
+ </div>
43
+
44
+ <div id="search">
45
+
46
+ <a class="full_list_link" id="class_list_link"
47
+ href="class_list.html">
48
+
49
+ <svg width="24" height="24">
50
+ <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
51
+ <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
52
+ <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
53
+ </svg>
54
+ </a>
55
+
56
+ </div>
57
+ <div class="clear"></div>
58
+ </div>
59
+
60
+ <div id="content"><div id='filecontents'>
61
+ <p><a href="/README_md.html#guide--documentation">Back to the guide</a></p>
62
+
63
+ <h1 id="label-Internal+datastructures">Internal datastructures</h1>
64
+
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
+
67
+ <h2 id="label-MethodAccessibleHash">MethodAccessibleHash</h2>
68
+
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
+
71
+ <p>Usage example:</p>
72
+
73
+ <pre class="code ruby"><code class="ruby"><span class='id identifier rubyid_default_options'>default_options</span> <span class='op'>=</span> <span class='lbrace'>{</span> <span class='label'>foo:</span> <span class='symbol'>:bar</span> <span class='rbrace'>}</span>
74
+ <span class='id identifier rubyid_options'>options</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='op'>::</span><span class='const'><span class='object_link'><a href="Compony/MethodAccessibleHash.html" title="Compony::MethodAccessibleHash (class)">MethodAccessibleHash</a></span></span><span class='period'>.</span><span class='id identifier rubyid_new'><span class='object_link'><a href="Compony/MethodAccessibleHash.html#initialize-instance_method" title="Compony::MethodAccessibleHash#initialize (method)">new</a></span></span><span class='lparen'>(</span><span class='id identifier rubyid_default_options'>default_options</span><span class='rparen'>)</span>
75
+ <span class='id identifier rubyid_options'>options</span><span class='lbracket'>[</span><span class='symbol'>:color</span><span class='rbracket'>]</span> <span class='op'>=</span> <span class='symbol'>:green</span>
76
+ <span class='id identifier rubyid_options'>options</span><span class='period'>.</span><span class='id identifier rubyid_foo'>foo</span> <span class='comment'># =&gt; :bar
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
+ </span></code></pre>
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>
81
+
82
+ <h2 id="label-RequestContext">RequestContext</h2>
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>
85
+
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
+
88
+ <p>To instantiate a RequestContext, the following arguments must be given:</p>
89
+ <ul><li>
90
+ <p>The first argument must be the component instantiating the RequestContext.</p>
91
+ </li><li>
92
+ <p>The second argument must be the controller holding the current HTTP request.</p>
93
+ </li><li>
94
+ <p>Optional: any further arguments will be given to Dslblend as additional providers.</p>
95
+ </li><li>
96
+ <p>Optional: the keyword argument <code>helpers</code> can be given to overwrite the <code>helpers</code> context. If not given, the helpers will be extracted from the controller.</p>
97
+ </li><li>
98
+ <p>Optional: the keyword argument <code>locals</code> can be given a hash of local assigns to be made available within the context.</p>
99
+ </li></ul>
100
+
101
+ <p>RequestContext further provides the following methods on its own:</p>
102
+ <ul><li>
103
+ <p><code>controller</code> returns the controller.</p>
104
+ </li><li>
105
+ <p><code>helpers</code> returns the helpers (either from the initializer or the controller).</p>
106
+ </li><li>
107
+ <p><code>local_assigns</code> returns the locals that can be given to the RequestContext on instantiation through the <code>locals</code> keyword argument.</p>
108
+ </li><li>
109
+ <p><code>evaluate_with_backfire</code> is <code>evaluate</code> with enabled backfiring.</p>
110
+ </li><li>
111
+ <p><code>component</code> returns the component the RequestContext was instantiated with.</p>
112
+ </li><li>
113
+ <p><code>request_context</code> returns self. This is for disambiguation purposes.</p>
114
+ </li><li>
115
+ <p>Any call to an unknown method will first be evaluated as a potential hit in <code>locals</code>. Only if no matching local is found, Dslblend takes over.</p>
116
+ </li></ul>
117
+
118
+ <p><a href="/README_md.html#guide--documentation">Guide index</a></p>
119
+ </div></div>
120
+
121
+ <div id="footer">
122
+ Generated on Mon May 18 13:55:33 2026 by
123
+ <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
124
+ 0.9.34 (ruby-3.3.5).
125
+ </div>
126
+
127
+ </div>
128
+ </body>
129
+ </html>