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.
- checksums.yaml +4 -4
- data/.yardopts +36 -1
- data/CHANGELOG.md +31 -0
- data/CLAUDE.md +85 -0
- data/Gemfile.lock +1 -1
- data/README.md +13 -3
- data/VERSION +1 -1
- data/compony.gemspec +3 -3
- data/doc/ComponentGenerator.html +1 -1
- data/doc/Components.html +1 -1
- data/doc/ComponentsGenerator.html +1 -1
- data/doc/Compony/Component.html +54 -54
- data/doc/Compony/ComponentMixins/Default/Labelling.html +1 -1
- data/doc/Compony/ComponentMixins/Default/Standalone/ResourcefulVerbDsl.html +1 -1
- data/doc/Compony/ComponentMixins/Default/Standalone/StandaloneDsl.html +109 -70
- data/doc/Compony/ComponentMixins/Default/Standalone/VerbDsl.html +64 -28
- data/doc/Compony/ComponentMixins/Default/Standalone.html +1 -1
- data/doc/Compony/ComponentMixins/Default.html +1 -1
- data/doc/Compony/ComponentMixins/Resourceful.html +213 -74
- data/doc/Compony/ComponentMixins.html +1 -1
- data/doc/Compony/Components/Buttons/CssButton.html +1 -1
- data/doc/Compony/Components/Buttons/Link.html +1 -1
- data/doc/Compony/Components/Buttons.html +1 -1
- data/doc/Compony/Components/Destroy.html +83 -29
- data/doc/Compony/Components/Edit.html +110 -38
- data/doc/Compony/Components/Form.html +551 -208
- data/doc/Compony/Components/Index.html +1 -1
- data/doc/Compony/Components/List.html +3 -3
- data/doc/Compony/Components/New.html +110 -38
- data/doc/Compony/Components/Show.html +1 -1
- data/doc/Compony/Components/WithForm.html +194 -47
- data/doc/Compony/Components.html +1 -1
- data/doc/Compony/ControllerMixin.html +1 -1
- data/doc/Compony/Engine.html +1 -1
- data/doc/Compony/Intent.html +2 -2
- data/doc/Compony/ManageIntentsDsl.html +1 -1
- data/doc/Compony/MethodAccessibleHash.html +1 -1
- data/doc/Compony/ModelFields/Anchormodel.html +1 -1
- data/doc/Compony/ModelFields/Association.html +1 -1
- data/doc/Compony/ModelFields/Attachment.html +1 -1
- data/doc/Compony/ModelFields/Base.html +1 -1
- data/doc/Compony/ModelFields/Boolean.html +1 -1
- data/doc/Compony/ModelFields/Color.html +1 -1
- data/doc/Compony/ModelFields/Currency.html +1 -1
- data/doc/Compony/ModelFields/Date.html +1 -1
- data/doc/Compony/ModelFields/Datetime.html +1 -1
- data/doc/Compony/ModelFields/Decimal.html +1 -1
- data/doc/Compony/ModelFields/Email.html +1 -1
- data/doc/Compony/ModelFields/Float.html +1 -1
- data/doc/Compony/ModelFields/Integer.html +1 -1
- data/doc/Compony/ModelFields/Percentage.html +1 -1
- data/doc/Compony/ModelFields/Phone.html +1 -1
- data/doc/Compony/ModelFields/RichText.html +1 -1
- data/doc/Compony/ModelFields/String.html +1 -1
- data/doc/Compony/ModelFields/Text.html +1 -1
- data/doc/Compony/ModelFields/Time.html +1 -1
- data/doc/Compony/ModelFields/Url.html +1 -1
- data/doc/Compony/ModelFields.html +1 -1
- data/doc/Compony/ModelMixin.html +1 -1
- data/doc/Compony/NaturalOrdering.html +1 -1
- data/doc/Compony/RequestContext.html +1 -1
- data/doc/Compony/Version.html +1 -1
- data/doc/Compony/ViewHelpers.html +1 -1
- data/doc/Compony/VirtualModel.html +1 -1
- data/doc/Compony.html +1 -1
- data/doc/ComponyController.html +1 -1
- data/doc/_index.html +97 -1
- data/doc/file.CHANGELOG.html +758 -0
- data/doc/file.README.html +25 -4
- data/doc/file.basic_component.html +314 -0
- data/doc/file.cookbook.html +189 -0
- data/doc/file.destroy.html +105 -0
- data/doc/file.dsl_reference.html +672 -0
- data/doc/file.edit.html +109 -0
- data/doc/file.example.html +291 -0
- data/doc/file.example_advanced.html +257 -0
- data/doc/file.feasibility.html +115 -0
- data/doc/file.form.html +195 -0
- data/doc/file.generators.html +89 -0
- data/doc/file.glossary.html +217 -0
- data/doc/file.gotchas.html +222 -0
- data/doc/file.index.html +135 -0
- data/doc/file.inheritance.html +136 -0
- data/doc/file.installation.html +115 -0
- data/doc/file.integrations.html +218 -0
- data/doc/file.intents.html +265 -0
- data/doc/file.internal_datastructures.html +129 -0
- data/doc/file.list.html +253 -0
- data/doc/file.maintaining.html +127 -0
- data/doc/file.model_fields.html +137 -0
- data/doc/file.nesting.html +237 -0
- data/doc/file.new.html +109 -0
- data/doc/file.ownership.html +98 -0
- data/doc/file.patterns.html +669 -0
- data/doc/file.pre_built_components.html +99 -0
- data/doc/file.resourceful.html +181 -0
- data/doc/file.show.html +158 -0
- data/doc/file.standalone.html +233 -0
- data/doc/file.virtual_models.html +117 -0
- data/doc/file.with_form.html +157 -0
- data/doc/file_list.html +160 -0
- data/doc/guide/cookbook.md +41 -0
- data/doc/guide/dsl_reference.md +155 -0
- data/doc/guide/example_advanced.md +209 -0
- data/doc/guide/generators.md +1 -1
- data/doc/guide/glossary.md +42 -0
- data/doc/guide/gotchas.md +125 -0
- data/doc/guide/maintaining.md +64 -0
- data/doc/guide/patterns.md +681 -0
- data/doc/guide/pre_built_components/edit.md +1 -1
- data/doc/guide/pre_built_components/index.md +64 -1
- data/doc/guide/pre_built_components/list.md +111 -7
- data/doc/guide/pre_built_components/show.md +57 -2
- data/doc/guide/pre_built_components/with_form.md +56 -9
- data/doc/guide/pre_built_components.md +7 -2
- data/doc/guide/standalone.md +16 -1
- data/doc/index.html +25 -4
- data/doc/integrations.md +61 -0
- data/doc/llms.txt +62 -0
- data/doc/top-level-namespace.html +1 -1
- data/lib/compony/component.rb +8 -3
- data/lib/compony/component_mixins/default/standalone/standalone_dsl.rb +32 -15
- data/lib/compony/component_mixins/default/standalone/verb_dsl.rb +11 -3
- data/lib/compony/component_mixins/resourceful.rb +30 -16
- data/lib/compony/components/destroy.rb +21 -1
- data/lib/compony/components/edit.rb +25 -1
- data/lib/compony/components/form.rb +63 -21
- data/lib/compony/components/list.rb +1 -1
- data/lib/compony/components/new.rb +25 -1
- data/lib/compony/components/with_form.rb +20 -5
- data/lib/compony/intent.rb +1 -1
- metadata +43 -1
|
@@ -0,0 +1,237 @@
|
|
|
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: nesting
|
|
8
|
+
|
|
9
|
+
— 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 = "nesting";
|
|
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> »
|
|
40
|
+
<span class="title">File: nesting</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-Nesting">Nesting</h1>
|
|
64
|
+
|
|
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
|
+
|
|
67
|
+
<p>Nesting is orthogonal to inheritance, they are two entirely different concepts. For disambiguating “parent component”, we will make an effort to apply that term to nesting only, while writing “parent component class” if inheritance is meant.</p>
|
|
68
|
+
|
|
69
|
+
<p>Sub-components are particularly useful for DRYing up your code, e.g. when a visual element is used in multiple places of your application or even multiple times on the same page.</p>
|
|
70
|
+
|
|
71
|
+
<p>Nesting occurs when a component is being rendered. It is perfectly feasible to use an otherwise standalone component as a sub-component. Doing so simply plugs it into the content of another component and any arguments can be given to its constructor.</p>
|
|
72
|
+
|
|
73
|
+
<p>Note that only the root component runs authentication and authorization. Thus, be careful which components you nest.</p>
|
|
74
|
+
|
|
75
|
+
<p>To create a sub-component, use <code>render_sub_comp</code> in a component’s content block. Any keyword arguments given will be passed to the sub-component. It is strictly recommended to exclusively use <code>render_sub_comp</code>, <code>sub_comp</code> or its <a href="./resourceful_md.html#nesting-resourceful-components">resourceful</a> pendent to nest components, as this method makes a component aware of its exact nesting.</p>
|
|
76
|
+
|
|
77
|
+
<p>Here is a simple example of a component that displays numbers as binary:</p>
|
|
78
|
+
|
|
79
|
+
<pre class="code ruby"><code class="ruby"><span class='comment'># app/components/numbers/binary.rb
|
|
80
|
+
</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'>Nestings</span><span class='op'>::</span><span class='const'>Binary</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/Component.html" title="Compony::Component (class)">Component</a></span></span>
|
|
81
|
+
<span class='kw'>def</span> <span class='id identifier rubyid_initialize'>initialize</span><span class='lparen'>(</span><span class='op'>*</span><span class='id identifier rubyid_args'>args</span><span class='comma'>,</span> <span class='label'>number:</span> <span class='kw'>nil</span><span class='comma'>,</span> <span class='op'>**</span><span class='id identifier rubyid_kwargs'>kwargs</span><span class='comma'>,</span> <span class='op'>&</span><span class='id identifier rubyid_block'>block</span><span class='rparen'>)</span>
|
|
82
|
+
<span class='ivar'>@number</span> <span class='op'>=</span> <span class='kw'>nil</span> <span class='comment'># If this component is initialized with the argument `number`, it will be stored in the component instance.
|
|
83
|
+
</span> <span class='kw'>end</span>
|
|
84
|
+
<span class='id identifier rubyid_setup'>setup</span> <span class='kw'>do</span>
|
|
85
|
+
<span class='comment'># standalone and other configs are omitted in this example.
|
|
86
|
+
</span> <span class='id identifier rubyid_content'>content</span> <span class='kw'>do</span>
|
|
87
|
+
<span class='comment'># If the initializer did not store `number`, check whether the Rails request contains the parameter `number`:
|
|
88
|
+
</span> <span class='comment'># Note: do not do that, as we will demonstrate below.
|
|
89
|
+
</span> <span class='ivar'>@number</span> <span class='op'>||=</span> <span class='id identifier rubyid_params'>params</span><span class='lbracket'>[</span><span class='symbol'>:number</span><span class='rbracket'>]</span><span class='period'>.</span><span class='id identifier rubyid_presence'>presence</span><span class='op'>&.</span><span class='id identifier rubyid_to_i'>to_i</span> <span class='op'>||</span> <span class='int'>0</span>
|
|
90
|
+
<span class='comment'># Display the number as binary
|
|
91
|
+
</span> <span class='id identifier rubyid_para'>para</span> <span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>The number </span><span class='embexpr_beg'>#{</span><span class='ivar'>@number</span><span class='embexpr_end'>}</span><span class='tstring_content'> has the binary form </span><span class='embexpr_beg'>#{</span><span class='ivar'>@number</span><span class='period'>.</span><span class='id identifier rubyid_to_s'>to_s</span><span class='lparen'>(</span><span class='int'>2</span><span class='rparen'>)</span><span class='embexpr_end'>}</span><span class='tstring_content'>.</span><span class='tstring_end'>"</span></span>
|
|
92
|
+
<span class='kw'>end</span>
|
|
93
|
+
<span class='kw'>end</span>
|
|
94
|
+
<span class='kw'>end</span>
|
|
95
|
+
</code></pre>
|
|
96
|
+
|
|
97
|
+
<p>If used standalone, the number can be set by using a GET parameter, e.g. <code>?number=5</code>. The result is something like this:</p>
|
|
98
|
+
|
|
99
|
+
<pre class="code ruby"><code class="ruby">The number 5 has the binary form 101.
|
|
100
|
+
</code></pre>
|
|
101
|
+
|
|
102
|
+
<p>Now, let’s write a component that displays three different numbers side-by-side:</p>
|
|
103
|
+
|
|
104
|
+
<pre class="code ruby"><code class="ruby"><span class='comment'># app/components/numbers/binary_comparator.rb
|
|
105
|
+
</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'>Nestings</span><span class='op'>::</span><span class='const'>BinaryComparator</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/Component.html" title="Compony::Component (class)">Component</a></span></span>
|
|
106
|
+
<span class='id identifier rubyid_setup'>setup</span> <span class='kw'>do</span>
|
|
107
|
+
<span class='comment'># standalone and other configs are omitted in this example.
|
|
108
|
+
</span> <span class='id identifier rubyid_content'>content</span> <span class='kw'>do</span>
|
|
109
|
+
<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='const'><span class='object_link'><a href="Components.html" title="Components (module)">Components</a></span></span><span class='op'>::</span><span class='const'>Nestings</span><span class='op'>::</span><span class='const'>Binary</span><span class='comma'>,</span> <span class='label'>number:</span> <span class='int'>1</span><span class='rparen'>)</span>
|
|
110
|
+
<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='const'><span class='object_link'><a href="Components.html" title="Components (module)">Components</a></span></span><span class='op'>::</span><span class='const'>Nestings</span><span class='op'>::</span><span class='const'>Binary</span><span class='comma'>,</span> <span class='label'>number:</span> <span class='int'>2</span><span class='rparen'>)</span>
|
|
111
|
+
<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='const'><span class='object_link'><a href="Components.html" title="Components (module)">Components</a></span></span><span class='op'>::</span><span class='const'>Nestings</span><span class='op'>::</span><span class='const'>Binary</span><span class='comma'>,</span> <span class='label'>number:</span> <span class='int'>3</span><span class='rparen'>)</span>
|
|
112
|
+
<span class='kw'>end</span>
|
|
113
|
+
<span class='kw'>end</span>
|
|
114
|
+
<span class='kw'>end</span>
|
|
115
|
+
</code></pre>
|
|
116
|
+
|
|
117
|
+
<p>The result is something like this:</p>
|
|
118
|
+
|
|
119
|
+
<pre class="code ruby"><code class="ruby">The number 1 has the binary form 1.
|
|
120
|
+
The number 2 has the binary form 10.
|
|
121
|
+
The number 3 has the binary form 11.
|
|
122
|
+
</code></pre>
|
|
123
|
+
|
|
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
|
+
|
|
126
|
+
<h2 id="label-Proper+parameter+naming+for+-28nested-29+components">Proper parameter naming for (nested) components</h2>
|
|
127
|
+
|
|
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
|
+
<ul><li>
|
|
130
|
+
<p>Each component has an <code>index</code>, given to it by the <code>sub_comp</code> call in the parent, informing it witch n-th child of the parent it is.</p>
|
|
131
|
+
</li><li>
|
|
132
|
+
<p>For instance, in the example above, the three <code>Binary</code> components have indices 0, 1 and 2.</p>
|
|
133
|
+
</li><li>
|
|
134
|
+
<p>Each component has an <code>id</code> which corresponds to <code>"#{family_name}_#{comp_name}_#{@index}"</code>.</p>
|
|
135
|
+
</li><li>
|
|
136
|
+
<p>For instance, the last <code>Binary</code> component from the example above has ID <code>nestings_binary_2</code>.</p>
|
|
137
|
+
</li><li>
|
|
138
|
+
<p>The <code>BinaryComparator</code> has ID <code>nestings_binary_comparator_0</code>.</p>
|
|
139
|
+
</li><li>
|
|
140
|
+
<p>Each component has a <code>path</code> indicating its exact position in the nesting tree as seen from the root component.</p>
|
|
141
|
+
</li><li>
|
|
142
|
+
<p>In the example above, the last <code>Binary</code> component has path <code>nestings_binary_comparator_0/nestings_binary_2</code>.</p>
|
|
143
|
+
</li><li>
|
|
144
|
+
<p><code>BinaryComparator</code> has path <code>nestings_binary_comparator_0</code>.</p>
|
|
145
|
+
</li><li>
|
|
146
|
+
<p>Each component provides the method <code>param_name</code> that takes the name of a parameter name and prepends the first 5 characters of the component’s SHA1-hashed path to it.</p>
|
|
147
|
+
</li><li>
|
|
148
|
+
<p>For instance, if <code>param_name(:number)</code> is called on the last <code>Binary</code> component, the output is <code>a9f3d_number</code>.</p>
|
|
149
|
+
</li><li>
|
|
150
|
+
<p>If the same method is called on the first <code>Binary</code> component, the output is <code>f6e86_number</code>.</p>
|
|
151
|
+
</li></ul>
|
|
152
|
+
|
|
153
|
+
<p>In short, <code>param_name</code> should be used to prefix every parameter that is used in a component that could potentially be nested. It is good practice to apply it to all components. <code>param_name</code> has two important properties:</p>
|
|
154
|
+
<ul><li>
|
|
155
|
+
<p>From the param name alone, it is not possible to determine to which component the parameter belongs. However:</p>
|
|
156
|
+
</li><li>
|
|
157
|
+
<p><code>param_name</code> is consistent across reloads of the same URL (given that the components are still the same) and thus each component will be able to identify its own parameters and react to them.</p>
|
|
158
|
+
</li></ul>
|
|
159
|
+
|
|
160
|
+
<p>With that in mind, let’s adjust our <code>Binary</code> component. In this example, we will assume that we have implemented yet another component called <code>NumberChooser</code> that provides a number input with a Stimulus controller attached. That controller is given the parameter as a String value, such that the it can set the appropriate HTTP GET param and trigger a full page reload to the <code>BinaryComparator</code> component.</p>
|
|
161
|
+
|
|
162
|
+
<p>Further, we can drop the custom initializer from the <code>Binary</code> component, as the number to display is exclusively coming from the HTTP GET param. The resulting code looks something like:</p>
|
|
163
|
+
|
|
164
|
+
<pre class="code ruby"><code class="ruby"><span class='comment'># app/components/numbers/binary_comparator.rb
|
|
165
|
+
</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'>Nestings</span><span class='op'>::</span><span class='const'>BinaryComparator</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/Component.html" title="Compony::Component (class)">Component</a></span></span>
|
|
166
|
+
<span class='id identifier rubyid_setup'>setup</span> <span class='kw'>do</span>
|
|
167
|
+
<span class='comment'># standalone and other configs are omitted in this example.
|
|
168
|
+
</span> <span class='id identifier rubyid_content'>content</span> <span class='kw'>do</span>
|
|
169
|
+
<span class='int'>3</span><span class='period'>.</span><span class='id identifier rubyid_times'>times</span> <span class='kw'>do</span>
|
|
170
|
+
<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='const'><span class='object_link'><a href="Components.html" title="Components (module)">Components</a></span></span><span class='op'>::</span><span class='const'>Nestings</span><span class='op'>::</span><span class='const'>Binary</span><span class='rparen'>)</span>
|
|
171
|
+
<span class='kw'>end</span>
|
|
172
|
+
<span class='kw'>end</span>
|
|
173
|
+
<span class='kw'>end</span>
|
|
174
|
+
<span class='kw'>end</span>
|
|
175
|
+
|
|
176
|
+
<span class='comment'># app/components/numbers/binary.rb
|
|
177
|
+
</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'>Nestings</span><span class='op'>::</span><span class='const'>Binary</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/Component.html" title="Compony::Component (class)">Component</a></span></span>
|
|
178
|
+
<span class='id identifier rubyid_setup'>setup</span> <span class='kw'>do</span>
|
|
179
|
+
<span class='comment'># standalone and other configs are omitted in this example.
|
|
180
|
+
</span> <span class='id identifier rubyid_content'>content</span> <span class='kw'>do</span>
|
|
181
|
+
<span class='comment'># This is where we use param_name to retrieve the parameter for this component, regardless whether it's standalone or used as a sub-comp.
|
|
182
|
+
</span> <span class='ivar'>@number</span> <span class='op'>||=</span> <span class='id identifier rubyid_params'>params</span><span class='lbracket'>[</span><span class='id identifier rubyid_param_name'>param_name</span><span class='lparen'>(</span><span class='symbol'>:number</span><span class='rparen'>)</span><span class='rbracket'>]</span><span class='period'>.</span><span class='id identifier rubyid_presence'>presence</span><span class='op'>&.</span><span class='id identifier rubyid_to_i'>to_i</span> <span class='op'>||</span> <span class='int'>0</span>
|
|
183
|
+
<span class='comment'># Display the number as binary
|
|
184
|
+
</span> <span class='id identifier rubyid_para'>para</span> <span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>The number </span><span class='embexpr_beg'>#{</span><span class='ivar'>@number</span><span class='embexpr_end'>}</span><span class='tstring_content'> has the binary form </span><span class='embexpr_beg'>#{</span><span class='ivar'>@number</span><span class='period'>.</span><span class='id identifier rubyid_to_s'>to_s</span><span class='lparen'>(</span><span class='int'>2</span><span class='rparen'>)</span><span class='embexpr_end'>}</span><span class='tstring_content'>.</span><span class='tstring_end'>"</span></span>
|
|
185
|
+
<span class='comment'># Display the number input that will reload the page to adjust to the user input. We give it the param_name such that it can set params accordingly.
|
|
186
|
+
</span> <span class='id identifier rubyid_concat'>concat</span> <span class='id identifier rubyid_sub_comp'>sub_comp</span><span class='lparen'>(</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'>Nestings</span><span class='op'>::</span><span class='const'>NumberChooser</span><span class='comma'>,</span> <span class='label'>param_name:</span> <span class='id identifier rubyid_param_name'>param_name</span><span class='lparen'>(</span><span class='symbol'>:number</span><span class='rparen'>)</span><span class='rparen'>)</span>
|
|
187
|
+
<span class='kw'>end</span>
|
|
188
|
+
<span class='kw'>end</span>
|
|
189
|
+
<span class='kw'>end</span>
|
|
190
|
+
</code></pre>
|
|
191
|
+
|
|
192
|
+
<p>The result for the URL <code>path/to/binary_comparator?a9f3d_number=2&e70b4_number=4&a9f3d_number=8</code> is something like this:</p>
|
|
193
|
+
|
|
194
|
+
<pre class="code ruby"><code class="ruby">The number 2 has the binary form 10. Enter a number and press ENTER: [2]
|
|
195
|
+
The number 4 has the binary form 100. Enter a number and press ENTER: [4]
|
|
196
|
+
The number 8 has the binary form 1000. Enter a number and press ENTER: [8]
|
|
197
|
+
</code></pre>
|
|
198
|
+
|
|
199
|
+
<p>Note that this example is completely stateless, as all the info is encoded in the URL.</p>
|
|
200
|
+
|
|
201
|
+
<h2 id="label-Rendering+List+as+sub+comp+in+Show">Rendering <code>List</code> as sub comp in <code>Show</code></h2>
|
|
202
|
+
|
|
203
|
+
<p>A pattern often used is the following:</p>
|
|
204
|
+
|
|
205
|
+
<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'><</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>
|
|
206
|
+
<span class='id identifier rubyid_setup'>setup</span> <span class='kw'>do</span>
|
|
207
|
+
<span class='id identifier rubyid_content'>content</span> <span class='symbol'>:quotes</span> <span class='kw'>do</span>
|
|
208
|
+
<span class='id identifier rubyid_h1'>h1</span> <span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>Quotes of </span><span class='embexpr_beg'>#{</span><span class='ivar'>@data</span><span class='period'>.</span><span class='id identifier rubyid_label'>label</span><span class='embexpr_end'>}</span><span class='tstring_end'>"</span></span>
|
|
209
|
+
<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='period'>.</span><span class='id identifier rubyid_accessible_by'>accessible_by</span><span class='lparen'>(</span><span class='id identifier rubyid_current_ability'>current_ability</span><span class='rparen'>)</span><span class='comma'>,</span> <span class='label'>turbo_frame:</span> <span class='symbol'>:"user_</span><span class='embexpr_beg'>#{</span><span class='ivar'>@data</span><span class='period'>.</span><span class='id identifier rubyid_id'>id</span><span class='embexpr_end'>}</span><span class='tstring_content'>_quotes</span><span class='tstring_end'>"</span></span><span class='rparen'>)</span>
|
|
210
|
+
<span class='kw'>end</span>
|
|
211
|
+
<span class='kw'>end</span>
|
|
212
|
+
<span class='kw'>end</span>
|
|
213
|
+
</code></pre>
|
|
214
|
+
|
|
215
|
+
<p>Again, there is a lot going on here:</p>
|
|
216
|
+
<ul><li>
|
|
217
|
+
<p>Since the component inherits from the <a href="/doc/guide/pre_built_components/show_md.html">pre-built Show component</a>, it automatically displays all fields of user in its <code>main</code> content block.</p>
|
|
218
|
+
</li><li>
|
|
219
|
+
<p>We add a second content block <code>:quotes</code> with a title displaying “Quotes of John Deer” and tell compony to render the appropriate list as a sub comp, giving it all quotes of John Deer that are accessible by the user currently logged in (this is a cancancan feature).</p>
|
|
220
|
+
</li><li>
|
|
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
|
+
</li><li>
|
|
223
|
+
<p>We also give <code>:"user_#{@data.id}_quotes"</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>
|
|
224
|
+
</li></ul>
|
|
225
|
+
|
|
226
|
+
<p><a href="/README_md.html#guide--documentation">Guide index</a></p>
|
|
227
|
+
</div></div>
|
|
228
|
+
|
|
229
|
+
<div id="footer">
|
|
230
|
+
Generated on Mon May 18 13:55:33 2026 by
|
|
231
|
+
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
|
232
|
+
0.9.34 (ruby-3.3.5).
|
|
233
|
+
</div>
|
|
234
|
+
|
|
235
|
+
</div>
|
|
236
|
+
</body>
|
|
237
|
+
</html>
|
data/doc/file.new.html
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
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: new
|
|
8
|
+
|
|
9
|
+
— 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 = "new";
|
|
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> »
|
|
40
|
+
<span class="title">File: new</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'><ul><li>
|
|
61
|
+
<p><a href="/README_md.html#guide--documentation">Back to the guide</a></p>
|
|
62
|
+
</li><li>
|
|
63
|
+
<p><a href="/doc/guide/pre_built_components_md.html">List of pre-built components</a></p>
|
|
64
|
+
</li></ul>
|
|
65
|
+
|
|
66
|
+
<h1 id="label-Pre-built+components-3A+New">Pre-built components: New</h1>
|
|
67
|
+
|
|
68
|
+
<p>This component is the Compony equivalent to a typical Rails controller’s <code>new</code> and <code>create</code> actions.</p>
|
|
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>
|
|
71
|
+
<ul><li>
|
|
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
|
+
</li><li>
|
|
74
|
+
<p>POST (equivalent to a <code>create</code> action in a controller) will attempt to save the resource. If that fails, the form is rendered again with a HTTP 422 code (“unprocessable entity”). If the creation succeeds, a flash is shown and the user is redirected:</p>
|
|
75
|
+
</li><li>
|
|
76
|
+
<p>if present: the data’s Show component</p>
|
|
77
|
+
</li><li>
|
|
78
|
+
<p>otherwise, if the resource is owned by another resource class: the owner’s Show component</p>
|
|
79
|
+
</li><li>
|
|
80
|
+
<p>otherwise, the data’s Index component</p>
|
|
81
|
+
</li></ul>
|
|
82
|
+
|
|
83
|
+
<p>Authorization checks for <code>create</code> even in GET. The reason is that it makes no sense to present an empty form to a user who cannot create a new record. This also causes any <a href="/doc/guide/intents_md.html">intents</a> to New components to be hidden to users lacking the permission.</p>
|
|
84
|
+
|
|
85
|
+
<p>This component follows the <a href="/doc/guide/resourceful_md.html#complete-resourceful-lifecycle">resourceful lifecycle</a>. <code>load_data</code> is set to create a new record and <code>store_data</code> attempts to create it. Parameters are validated in <code>assign_attributes</code> using a Schemacop schema that is generated from the form. The schema corresponds to Rail’s typical strong parameter structure for forms. For example, a user’s New component would look for a parameter <code>user</code> holding a hash of attributes (e.g. <code>user[first_name]=Tom</code>).</p>
|
|
86
|
+
|
|
87
|
+
<p>In case you overwrite <code>store_data</code>, make sure to set <code>@create_succeeded</code> to true if storing was successful (and to set it to false otherwise).</p>
|
|
88
|
+
|
|
89
|
+
<p>The following DSL calls are implemented to allow for convenient overrides of default logic:</p>
|
|
90
|
+
<ul><li>
|
|
91
|
+
<p>The block <code>on_create_failed_respond</code> is run if <code>@create_succeeded</code> is not true. By default, it logs all error messages with level <code>warn</code> and renders the component again through HTTP 422, causing Turbo to correctly display the page. Error messages are displayed by the form inputs.</p>
|
|
92
|
+
</li><li>
|
|
93
|
+
<p>The block <code>on_created</code> is evaluated between successful record creation and responding. By default, it is not implemented and doing so is optional. This would be a suitable location for hooks that update state after a resource was created (like an <code>after_create</code> hook, but only executed if a record was created by this component). Do not redirect or render here, use the next blocks instead.</p>
|
|
94
|
+
</li><li>
|
|
95
|
+
<p>The block given in <code>on_created_respond</code> is evaluated after successful creation and by default shows a flash, then redirects. Overwrite this block if you need to completely customize all logic that happens after creation. If this block is overwritten, <code>on_created_redirect_path</code> will not be called.</p>
|
|
96
|
+
</li><li>
|
|
97
|
+
<p><code>on_created_redirect_path</code> is evaluated as the second step of <code>on_created_respond</code> and redirects to the resource’s Show, its owner’s Show, or its own Index component as described above. Overwrite this block in order to redirect ot another component instead, while keeping the default flash provided by <code>on_created_respond</code>.</p>
|
|
98
|
+
</li></ul>
|
|
99
|
+
</div></div>
|
|
100
|
+
|
|
101
|
+
<div id="footer">
|
|
102
|
+
Generated on Mon May 18 13:55:34 2026 by
|
|
103
|
+
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
|
104
|
+
0.9.34 (ruby-3.3.5).
|
|
105
|
+
</div>
|
|
106
|
+
|
|
107
|
+
</div>
|
|
108
|
+
</body>
|
|
109
|
+
</html>
|
|
@@ -0,0 +1,98 @@
|
|
|
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: ownership
|
|
8
|
+
|
|
9
|
+
— 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 = "ownership";
|
|
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> »
|
|
40
|
+
<span class="title">File: ownership</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-Ownership">Ownership</h1>
|
|
64
|
+
|
|
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
|
+
<ul><li>
|
|
67
|
+
<p>typically NOT owned: visitors and vouchers: while a voucher can <code>belong_to</code> a visitor, the voucher can be managed on it’s own. Vouchers can have their own index page which makes it possible to search for a given voucher code across all vouchers.</p>
|
|
68
|
+
</li><li>
|
|
69
|
+
<p>typically owned: users and their permissions: a permission only makes sense with respect to its associated user and having a list of all permissions across the system would rarely be a use case. In this case, we consider the <code>Permission</code> model to be conceptually <strong>owned by</strong> the <code>User</code> model.</p>
|
|
70
|
+
</li></ul>
|
|
71
|
+
|
|
72
|
+
<p>In Compony, if a model class is owned by another, it means that:</p>
|
|
73
|
+
<ul><li>
|
|
74
|
+
<p>The owned model has a non-optional <code>belongs_to</code> relation ship to its owner.</p>
|
|
75
|
+
</li><li>
|
|
76
|
+
<p>The owned model class has no Index component.</p>
|
|
77
|
+
</li><li>
|
|
78
|
+
<p><a href="/doc/guide/pre_built_components_md.html">Pre-built components</a> offer <a href="/doc/guide/intents_md.html#exposed-intents">exposed intents</a> to the owner model and redirect to its Show component instead of to the current object’s Index component.</p>
|
|
79
|
+
</li></ul>
|
|
80
|
+
|
|
81
|
+
<p>To mark a model as owned by another, write the following code <strong>in the model</strong>:</p>
|
|
82
|
+
|
|
83
|
+
<pre class="code ruby"><code class="ruby"><span class='comment'># app/models/permission.rb
|
|
84
|
+
</span><span class='id identifier rubyid_owned_by'>owned_by</span> <span class='symbol'>:user</span>
|
|
85
|
+
</code></pre>
|
|
86
|
+
|
|
87
|
+
<p><a href="/README_md.html#guide--documentation">Guide index</a></p>
|
|
88
|
+
</div></div>
|
|
89
|
+
|
|
90
|
+
<div id="footer">
|
|
91
|
+
Generated on Mon May 18 13:55:33 2026 by
|
|
92
|
+
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
|
93
|
+
0.9.34 (ruby-3.3.5).
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
</div>
|
|
97
|
+
</body>
|
|
98
|
+
</html>
|