anchormodel 0.3.1 → 0.4.0

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/CHANGELOG.md +14 -0
  4. data/EXAMPLES.md +408 -0
  5. data/Gemfile.lock +1 -1
  6. data/README.md +43 -1
  7. data/VERSION +1 -1
  8. data/anchormodel.gemspec +3 -3
  9. data/bin/test +9 -0
  10. data/doc/Anchormodel/ActiveModelTypeValueMulti.html +204 -42
  11. data/doc/Anchormodel/ActiveModelTypeValueSingle.html +243 -54
  12. data/doc/Anchormodel/Attribute.html +60 -37
  13. data/doc/Anchormodel/ModelMixin.html +166 -16
  14. data/doc/Anchormodel/SimpleFormInputs/Helpers/AnchormodelInputsCommon.html +82 -21
  15. data/doc/Anchormodel/SimpleFormInputs/Helpers.html +2 -11
  16. data/doc/Anchormodel/SimpleFormInputs.html +2 -11
  17. data/doc/Anchormodel/Util.html +497 -38
  18. data/doc/Anchormodel/Version.html +2 -20
  19. data/doc/Anchormodel.html +536 -129
  20. data/doc/AnchormodelCheckBoxesInput.html +22 -1
  21. data/doc/AnchormodelGenerator.html +64 -13
  22. data/doc/AnchormodelInput.html +24 -1
  23. data/doc/AnchormodelRadioButtonsInput.html +22 -1
  24. data/doc/_index.html +16 -1
  25. data/doc/class_list.html +1 -1
  26. data/doc/file.README.html +40 -2
  27. data/doc/index.html +40 -2
  28. data/doc/method_list.html +57 -17
  29. data/doc/top-level-namespace.html +1 -1
  30. data/lib/anchormodel/active_model_type_value_multi.rb +31 -5
  31. data/lib/anchormodel/active_model_type_value_single.rb +34 -6
  32. data/lib/anchormodel/attribute.rb +20 -8
  33. data/lib/anchormodel/model_mixin.rb +43 -8
  34. data/lib/anchormodel/simple_form_inputs/anchormodel_check_boxes_input.rb +7 -0
  35. data/lib/anchormodel/simple_form_inputs/anchormodel_input.rb +9 -0
  36. data/lib/anchormodel/simple_form_inputs/anchormodel_radio_buttons_input.rb +7 -0
  37. data/lib/anchormodel/simple_form_inputs/helpers/anchormodel_inputs_common.rb +14 -0
  38. data/lib/anchormodel/util.rb +102 -17
  39. data/lib/anchormodel.rb +109 -15
  40. data/lib/generators/anchormodel/anchormodel_generator.rb +8 -0
  41. data/test/active_record_model/user_test.rb +221 -10
  42. data/test/dummy/app/anchormodels/animal.rb +1 -0
  43. metadata +3 -1
@@ -106,8 +106,29 @@
106
106
 
107
107
  </div>
108
108
 
109
+ <h2>Overview</h2><div class="docstring">
110
+ <div class="discussion">
111
+
112
+ <p>SimpleForm input for a collection-valued anchormodel attribute (<code>belongs_to_anchormodels</code>), rendered as check boxes (one per anchormodel key).</p>
113
+
114
+
115
+ </div>
116
+ </div>
117
+ <div class="tags">
118
+
119
+ <div class="examples">
120
+ <p class="tag_title">Examples:</p>
121
+
122
+
123
+ <pre class="example code"><code>&lt;%= simple_form_for user do |f| %&gt;
124
+ &lt;%= f.input :animals, as: :anchormodel_check_boxes %&gt;
125
+ &lt;% end %&gt;</code></pre>
126
+
127
+ </div>
109
128
 
110
129
 
130
+ </div>
131
+
111
132
 
112
133
 
113
134
 
@@ -130,7 +151,7 @@
130
151
  </div>
131
152
 
132
153
  <div id="footer">
133
- Generated on Wed May 13 11:46:10 2026 by
154
+ Generated on Wed May 13 15:48:25 2026 by
134
155
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
135
156
  0.9.28 (ruby-3.3.5).
136
157
  </div>
@@ -101,8 +101,28 @@
101
101
 
102
102
  </div>
103
103
 
104
+ <h2>Overview</h2><div class="docstring">
105
+ <div class="discussion">
106
+
107
+ <p>Rails generator for scaffolding a new anchormodel.</p>
108
+
109
+
110
+ </div>
111
+ </div>
112
+ <div class="tags">
113
+
114
+ <div class="examples">
115
+ <p class="tag_title">Examples:</p>
116
+
117
+
118
+ <pre class="example code"><code><span class='id identifier rubyid_rails'>rails</span> <span class='id identifier rubyid_generate'>generate</span> <span class='id identifier rubyid_anchormodel'>anchormodel</span> <span class='const'>Role</span>
119
+ <span class='comment'># → creates app/anchormodels/role.rb</span></code></pre>
120
+
121
+ </div>
104
122
 
105
123
 
124
+ </div>
125
+
106
126
 
107
127
 
108
128
 
@@ -119,7 +139,7 @@
119
139
  <li class="public ">
120
140
  <span class="summary_signature">
121
141
 
122
- <a href="#add_anchormodel-instance_method" title="#add_anchormodel (instance method)">#<strong>add_anchormodel</strong> &#x21d2; Object </a>
142
+ <a href="#add_anchormodel-instance_method" title="#add_anchormodel (instance method)">#<strong>add_anchormodel</strong> &#x21d2; void </a>
123
143
 
124
144
 
125
145
 
@@ -133,7 +153,9 @@
133
153
 
134
154
 
135
155
 
136
- <span class="summary_desc"><div class='inline'></div></span>
156
+ <span class="summary_desc"><div class='inline'>
157
+ <p>Writes the new anchormodel file from the ERB template.</p>
158
+ </div></span>
137
159
 
138
160
  </li>
139
161
 
@@ -151,28 +173,57 @@
151
173
  <div class="method_details first">
152
174
  <h3 class="signature first" id="add_anchormodel-instance_method">
153
175
 
154
- #<strong>add_anchormodel</strong> &#x21d2; <tt>Object</tt>
176
+ #<strong>add_anchormodel</strong> &#x21d2; <tt>void</tt>
155
177
 
156
178
 
157
179
 
158
180
 
159
181
 
160
- </h3><table class="source_code">
182
+ </h3><div class="docstring">
183
+ <div class="discussion">
184
+ <p class="note returns_void">This method returns an undefined value.</p>
185
+ <p>Writes the new anchormodel file from the ERB template.</p>
186
+
187
+
188
+ </div>
189
+ </div>
190
+ <div class="tags">
191
+
192
+ <p class="tag_title">Raises:</p>
193
+ <ul class="raise">
194
+
195
+ <li>
196
+
197
+
198
+ <span class='type'>(<tt>RuntimeError</tt>)</span>
199
+
200
+
201
+
202
+ &mdash;
203
+ <div class='inline'>
204
+ <p>if NAME is blank.</p>
205
+ </div>
206
+
207
+ </li>
208
+
209
+ </ul>
210
+
211
+ </div><table class="source_code">
161
212
  <tr>
162
213
  <td>
163
214
  <pre class="lines">
164
215
 
165
216
 
166
- 4
167
- 5
168
- 6
169
- 7
170
- 8
171
- 9
172
- 10</pre>
217
+ 12
218
+ 13
219
+ 14
220
+ 15
221
+ 16
222
+ 17
223
+ 18</pre>
173
224
  </td>
174
225
  <td>
175
- <pre class="code"><span class="info file"># File 'lib/generators/anchormodel/anchormodel_generator.rb', line 4</span>
226
+ <pre class="code"><span class="info file"># File 'lib/generators/anchormodel/anchormodel_generator.rb', line 12</span>
176
227
 
177
228
  <span class='kw'>def</span> <span class='id identifier rubyid_add_anchormodel'>add_anchormodel</span>
178
229
  <span class='id identifier rubyid_fail'>fail</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>NAME must be present.</span><span class='tstring_end'>&#39;</span></span><span class='rparen'>)</span> <span class='kw'>if</span> <span class='id identifier rubyid_name'>name</span><span class='period'>.</span><span class='id identifier rubyid_blank?'>blank?</span>
@@ -191,7 +242,7 @@
191
242
  </div>
192
243
 
193
244
  <div id="footer">
194
- Generated on Wed May 13 11:46:10 2026 by
245
+ Generated on Wed May 13 15:48:25 2026 by
195
246
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
196
247
  0.9.28 (ruby-3.3.5).
197
248
  </div>
@@ -106,7 +106,30 @@
106
106
 
107
107
  </div>
108
108
 
109
+ <h2>Overview</h2><div class="docstring">
110
+ <div class="discussion">
111
+
112
+ <p>SimpleForm input for an anchormodel attribute. Renders a <code>&lt;select&gt;</code> collection whose options are the entries of the bound anchormodel.</p>
113
+
114
+ <p>Auto-detected by SimpleForm because the attribute’s AR type is <code>:anchormodel</code>.</p>
115
+
109
116
 
117
+ </div>
118
+ </div>
119
+ <div class="tags">
120
+
121
+ <div class="examples">
122
+ <p class="tag_title">Examples:</p>
123
+
124
+
125
+ <pre class="example code"><code>&lt;%= simple_form_for user do |f| %&gt;
126
+ &lt;%= f.input :role %&gt;
127
+ &lt;% end %&gt;</code></pre>
128
+
129
+ </div>
130
+
131
+
132
+ </div>
110
133
 
111
134
 
112
135
 
@@ -130,7 +153,7 @@
130
153
  </div>
131
154
 
132
155
  <div id="footer">
133
- Generated on Wed May 13 11:46:10 2026 by
156
+ Generated on Wed May 13 15:48:25 2026 by
134
157
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
135
158
  0.9.28 (ruby-3.3.5).
136
159
  </div>
@@ -106,8 +106,29 @@
106
106
 
107
107
  </div>
108
108
 
109
+ <h2>Overview</h2><div class="docstring">
110
+ <div class="discussion">
111
+
112
+ <p>SimpleForm input for a single-value anchormodel attribute, rendered as radio buttons. Unsuitable for collection attributes — use <span class='object_link'><a href="AnchormodelCheckBoxesInput.html" title="AnchormodelCheckBoxesInput (class)">AnchormodelCheckBoxesInput</a></span> for those.</p>
113
+
114
+
115
+ </div>
116
+ </div>
117
+ <div class="tags">
118
+
119
+ <div class="examples">
120
+ <p class="tag_title">Examples:</p>
121
+
122
+
123
+ <pre class="example code"><code>&lt;%= simple_form_for user do |f| %&gt;
124
+ &lt;%= f.input :role, as: :anchormodel_radio_buttons %&gt;
125
+ &lt;% end %&gt;</code></pre>
126
+
127
+ </div>
109
128
 
110
129
 
130
+ </div>
131
+
111
132
 
112
133
 
113
134
 
@@ -130,7 +151,7 @@
130
151
  </div>
131
152
 
132
153
  <div id="footer">
133
- Generated on Wed May 13 11:46:10 2026 by
154
+ Generated on Wed May 13 15:48:25 2026 by
134
155
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
135
156
  0.9.28 (ruby-3.3.5).
136
157
  </div>
data/doc/_index.html CHANGED
@@ -152,6 +152,21 @@
152
152
  </ul>
153
153
 
154
154
 
155
+ <ul id="alpha_I" class="alpha">
156
+ <li class="letter">I</li>
157
+ <ul>
158
+
159
+ <li>
160
+ <span class='object_link'><a href="Anchormodel/InvalidKey.html" title="Anchormodel::InvalidKey (class)">InvalidKey</a></span>
161
+
162
+ <small>(Anchormodel)</small>
163
+
164
+ </li>
165
+
166
+ </ul>
167
+ </ul>
168
+
169
+
155
170
  <ul id="alpha_M" class="alpha">
156
171
  <li class="letter">M</li>
157
172
  <ul>
@@ -220,7 +235,7 @@
220
235
  </div>
221
236
 
222
237
  <div id="footer">
223
- Generated on Wed May 13 11:46:09 2026 by
238
+ Generated on Wed May 13 15:48:24 2026 by
224
239
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
225
240
  0.9.28 (ruby-3.3.5).
226
241
  </div>
data/doc/class_list.html CHANGED
@@ -43,7 +43,7 @@
43
43
 
44
44
  <ul id="full_list" class="class">
45
45
  <li id="object_" class="odd"><div class="item" style="padding-left:30px"><span class='object_link'><a href="top-level-namespace.html" title="Top Level Namespace (root)">Top Level Namespace</a></span></div></li>
46
- <li id='object_Anchormodel' class='even'><div class='item' style='padding-left:30px'><a class='toggle'></a> <span class='object_link'><a href="Anchormodel.html" title="Anchormodel (class)">Anchormodel</a></span> &lt; Object<small class='search_info'>Top Level Namespace</small></div><ul><li id='object_Anchormodel::ActiveModelTypeValueMulti' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Anchormodel/ActiveModelTypeValueMulti.html" title="Anchormodel::ActiveModelTypeValueMulti (class)">ActiveModelTypeValueMulti</a></span> &lt; ActiveModelTypeValueSingle<small class='search_info'>Anchormodel</small></div></li><li id='object_Anchormodel::ActiveModelTypeValueSingle' class='collapsed even'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Anchormodel/ActiveModelTypeValueSingle.html" title="Anchormodel::ActiveModelTypeValueSingle (class)">ActiveModelTypeValueSingle</a></span> &lt; Value<small class='search_info'>Anchormodel</small></div></li><li id='object_Anchormodel::Attribute' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Anchormodel/Attribute.html" title="Anchormodel::Attribute (class)">Attribute</a></span> &lt; Object<small class='search_info'>Anchormodel</small></div></li><li id='object_Anchormodel::ModelMixin' class='collapsed even'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Anchormodel/ModelMixin.html" title="Anchormodel::ModelMixin (module)">ModelMixin</a></span><small class='search_info'>Anchormodel</small></div></li><li id='object_Anchormodel::SimpleFormInputs' class='collapsed odd'><div class='item' style='padding-left:45px'><a class='toggle'></a> <span class='object_link'><a href="Anchormodel/SimpleFormInputs.html" title="Anchormodel::SimpleFormInputs (module)">SimpleFormInputs</a></span><small class='search_info'>Anchormodel</small></div><ul><li id='object_Anchormodel::SimpleFormInputs::Helpers' class='collapsed'><div class='item' style='padding-left:60px'><a class='toggle'></a> <span class='object_link'><a href="Anchormodel/SimpleFormInputs/Helpers.html" title="Anchormodel::SimpleFormInputs::Helpers (module)">Helpers</a></span><small class='search_info'>Anchormodel::SimpleFormInputs</small></div><ul><li id='object_Anchormodel::SimpleFormInputs::Helpers::AnchormodelInputsCommon' class='collapsed'><div class='item' style='padding-left:75px'><span class='object_link'><a href="Anchormodel/SimpleFormInputs/Helpers/AnchormodelInputsCommon.html" title="Anchormodel::SimpleFormInputs::Helpers::AnchormodelInputsCommon (module)">AnchormodelInputsCommon</a></span><small class='search_info'>Anchormodel::SimpleFormInputs::Helpers</small></div></li></ul></li></ul></li><li id='object_Anchormodel::Util' class='collapsed even'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Anchormodel/Util.html" title="Anchormodel::Util (module)">Util</a></span><small class='search_info'>Anchormodel</small></div></li><li id='object_Anchormodel::Version' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Anchormodel/Version.html" title="Anchormodel::Version (module)">Version</a></span><small class='search_info'>Anchormodel</small></div></li></ul></li><li id='object_AnchormodelCheckBoxesInput' class='even'><div class='item' style='padding-left:30px'><span class='object_link'><a href="AnchormodelCheckBoxesInput.html" title="AnchormodelCheckBoxesInput (class)">AnchormodelCheckBoxesInput</a></span> &lt; CollectionCheckBoxesInput<small class='search_info'>Top Level Namespace</small></div></li><li id='object_AnchormodelGenerator' class='odd'><div class='item' style='padding-left:30px'><span class='object_link'><a href="AnchormodelGenerator.html" title="AnchormodelGenerator (class)">AnchormodelGenerator</a></span> &lt; NamedBase<small class='search_info'>Top Level Namespace</small></div></li><li id='object_AnchormodelInput' class='even'><div class='item' style='padding-left:30px'><span class='object_link'><a href="AnchormodelInput.html" title="AnchormodelInput (class)">AnchormodelInput</a></span> &lt; CollectionSelectInput<small class='search_info'>Top Level Namespace</small></div></li><li id='object_AnchormodelRadioButtonsInput' class='odd'><div class='item' style='padding-left:30px'><span class='object_link'><a href="AnchormodelRadioButtonsInput.html" title="AnchormodelRadioButtonsInput (class)">AnchormodelRadioButtonsInput</a></span> &lt; CollectionRadioButtonsInput<small class='search_info'>Top Level Namespace</small></div></li>
46
+ <li id='object_Anchormodel' class='even'><div class='item' style='padding-left:30px'><a class='toggle'></a> <span class='object_link'><a href="Anchormodel.html" title="Anchormodel (class)">Anchormodel</a></span> &lt; Object<small class='search_info'>Top Level Namespace</small></div><ul><li id='object_Anchormodel::ActiveModelTypeValueMulti' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Anchormodel/ActiveModelTypeValueMulti.html" title="Anchormodel::ActiveModelTypeValueMulti (class)">ActiveModelTypeValueMulti</a></span> &lt; ActiveModelTypeValueSingle<small class='search_info'>Anchormodel</small></div></li><li id='object_Anchormodel::ActiveModelTypeValueSingle' class='collapsed even'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Anchormodel/ActiveModelTypeValueSingle.html" title="Anchormodel::ActiveModelTypeValueSingle (class)">ActiveModelTypeValueSingle</a></span> &lt; Value<small class='search_info'>Anchormodel</small></div></li><li id='object_Anchormodel::Attribute' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Anchormodel/Attribute.html" title="Anchormodel::Attribute (class)">Attribute</a></span> &lt; Object<small class='search_info'>Anchormodel</small></div></li><li id='object_Anchormodel::InvalidKey' class='collapsed even'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Anchormodel/InvalidKey.html" title="Anchormodel::InvalidKey (class)">InvalidKey</a></span> &lt; RuntimeError<small class='search_info'>Anchormodel</small></div></li><li id='object_Anchormodel::ModelMixin' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Anchormodel/ModelMixin.html" title="Anchormodel::ModelMixin (module)">ModelMixin</a></span><small class='search_info'>Anchormodel</small></div></li><li id='object_Anchormodel::SimpleFormInputs' class='collapsed even'><div class='item' style='padding-left:45px'><a class='toggle'></a> <span class='object_link'><a href="Anchormodel/SimpleFormInputs.html" title="Anchormodel::SimpleFormInputs (module)">SimpleFormInputs</a></span><small class='search_info'>Anchormodel</small></div><ul><li id='object_Anchormodel::SimpleFormInputs::Helpers' class='collapsed'><div class='item' style='padding-left:60px'><a class='toggle'></a> <span class='object_link'><a href="Anchormodel/SimpleFormInputs/Helpers.html" title="Anchormodel::SimpleFormInputs::Helpers (module)">Helpers</a></span><small class='search_info'>Anchormodel::SimpleFormInputs</small></div><ul><li id='object_Anchormodel::SimpleFormInputs::Helpers::AnchormodelInputsCommon' class='collapsed'><div class='item' style='padding-left:75px'><span class='object_link'><a href="Anchormodel/SimpleFormInputs/Helpers/AnchormodelInputsCommon.html" title="Anchormodel::SimpleFormInputs::Helpers::AnchormodelInputsCommon (module)">AnchormodelInputsCommon</a></span><small class='search_info'>Anchormodel::SimpleFormInputs::Helpers</small></div></li></ul></li></ul></li><li id='object_Anchormodel::Util' class='collapsed odd'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Anchormodel/Util.html" title="Anchormodel::Util (module)">Util</a></span><small class='search_info'>Anchormodel</small></div></li><li id='object_Anchormodel::Version' class='collapsed even'><div class='item' style='padding-left:45px'><span class='object_link'><a href="Anchormodel/Version.html" title="Anchormodel::Version (module)">Version</a></span><small class='search_info'>Anchormodel</small></div></li></ul></li><li id='object_AnchormodelCheckBoxesInput' class='odd'><div class='item' style='padding-left:30px'><span class='object_link'><a href="AnchormodelCheckBoxesInput.html" title="AnchormodelCheckBoxesInput (class)">AnchormodelCheckBoxesInput</a></span> &lt; CollectionCheckBoxesInput<small class='search_info'>Top Level Namespace</small></div></li><li id='object_AnchormodelGenerator' class='even'><div class='item' style='padding-left:30px'><span class='object_link'><a href="AnchormodelGenerator.html" title="AnchormodelGenerator (class)">AnchormodelGenerator</a></span> &lt; NamedBase<small class='search_info'>Top Level Namespace</small></div></li><li id='object_AnchormodelInput' class='odd'><div class='item' style='padding-left:30px'><span class='object_link'><a href="AnchormodelInput.html" title="AnchormodelInput (class)">AnchormodelInput</a></span> &lt; CollectionSelectInput<small class='search_info'>Top Level Namespace</small></div></li><li id='object_AnchormodelRadioButtonsInput' class='even'><div class='item' style='padding-left:30px'><span class='object_link'><a href="AnchormodelRadioButtonsInput.html" title="AnchormodelRadioButtonsInput (class)">AnchormodelRadioButtonsInput</a></span> &lt; CollectionRadioButtonsInput<small class='search_info'>Top Level Namespace</small></div></li>
47
47
 
48
48
  </ul>
49
49
  </div>
data/doc/file.README.html CHANGED
@@ -64,6 +64,8 @@
64
64
 
65
65
  <p>This gem provides a simple but powerful alternative to <a href="https://api.rubyonrails.org/v7.0/classes/ActiveRecord/Enum.html">Rails Enums</a>. In contrast to regular Enums, Anchormodels can hold application logic, making them ideal for tying code to database objects.</p>
66
66
 
67
+ <p>For a tour of real-world usage patterns — ordered enums with <code>Comparable</code>, state machines, polymorphic class registries, multi-key collection attributes, cross-anchormodel references, and more — see <a href="EXAMPLES_md.html">EXAMPLES.md</a>.</p>
68
+
67
69
  <h1 id="label-Use+case">Use case</h1>
68
70
 
69
71
  <p>Typically, a Rails application consists of three kinds of state:</p>
@@ -144,8 +146,14 @@
144
146
  <pre class="code ruby"><code class="ruby"><span class='comment'># Retrieve all user roles:
145
147
  </span><span class='const'>Role</span><span class='period'>.</span><span class='id identifier rubyid_all'>all</span>
146
148
 
147
- <span class='comment'># Retrieve a specific role from the String and find its privilege level
149
+ <span class='comment'># Retrieve a specific role. `find` accepts a String, a Symbol, or `nil` (returns nil).
148
150
  </span><span class='const'>Role</span><span class='period'>.</span><span class='id identifier rubyid_find'>find</span><span class='lparen'>(</span><span class='symbol'>:guest</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_privilege_level'>privilege_level</span>
151
+ <span class='const'>Role</span><span class='period'>.</span><span class='id identifier rubyid_find'>find</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>guest</span><span class='tstring_end'>&#39;</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_privilege_level'>privilege_level</span> <span class='comment'># equivalent
152
+ </span>
153
+ <span class='comment'># Assignment accepts a String, Symbol, or Anchormodel instance:
154
+ </span><span class='ivar'>@user</span><span class='period'>.</span><span class='id identifier rubyid_role'>role</span> <span class='op'>=</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>admin</span><span class='tstring_end'>&#39;</span></span>
155
+ <span class='ivar'>@user</span><span class='period'>.</span><span class='id identifier rubyid_role'>role</span> <span class='op'>=</span> <span class='symbol'>:admin</span>
156
+ <span class='ivar'>@user</span><span class='period'>.</span><span class='id identifier rubyid_role'>role</span> <span class='op'>=</span> <span class='const'>Role</span><span class='period'>.</span><span class='id identifier rubyid_find'>find</span><span class='lparen'>(</span><span class='symbol'>:admin</span><span class='rparen'>)</span>
149
157
 
150
158
  <span class='comment'># Implement a Rails helper that makes sure users can only edit other users that have a lower privilege level than themselves
151
159
  </span><span class='kw'>def</span> <span class='id identifier rubyid_user_can_edit?'>user_can_edit?</span><span class='lparen'>(</span><span class='id identifier rubyid_this_user'>this_user</span><span class='comma'>,</span> <span class='id identifier rubyid_other_user'>other_user</span><span class='rparen'>)</span>
@@ -159,6 +167,19 @@
159
167
  </span><span class='ivar'>@user</span><span class='period'>.</span><span class='id identifier rubyid_role'>role</span><span class='period'>.</span><span class='id identifier rubyid_admin?'>admin?</span> <span class='comment'># true if and only if the role is admin (false otherwise)
160
168
  </span></code></pre>
161
169
 
170
+ <h2 id="label-Error+handling">Error handling</h2>
171
+
172
+ <p>Anchormodel raises <code>Anchormodel::InvalidKey</code> whenever an unknown key is supplied — from <code>find</code>, from attribute writers, and from the bulk-key scopes shown below:</p>
173
+
174
+ <pre class="code ruby"><code class="ruby"><span class='kw'>begin</span>
175
+ <span class='const'>Role</span><span class='period'>.</span><span class='id identifier rubyid_find'>find</span><span class='lparen'>(</span><span class='symbol'>:does_not_exist</span><span class='rparen'>)</span>
176
+ <span class='kw'>rescue</span> <span class='const'><span class='object_link'><a href="Anchormodel.html" title="Anchormodel (class)">Anchormodel</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Anchormodel/InvalidKey.html" title="Anchormodel::InvalidKey (class)">InvalidKey</a></span></span> <span class='op'>=&gt;</span> <span class='id identifier rubyid_e'>e</span>
177
+ <span class='comment'># e.message: &quot;Retrieved undefined anchor model key :does_not_exist for Role.&quot;
178
+ </span><span class='kw'>end</span>
179
+ </code></pre>
180
+
181
+ <p><code>Anchormodel::InvalidKey</code> inherits from <code>RuntimeError</code>, so existing <code>rescue RuntimeError</code> blocks remain compatible while allowing the narrower <code>rescue Anchormodel::InvalidKey</code>.</p>
182
+
162
183
  <p>Your form could look something like this:</p>
163
184
 
164
185
  <pre class="code ruby"><code class="ruby">&lt;%= form_with(model: user) do |form| %&gt;
@@ -302,6 +323,23 @@
302
323
 
303
324
  <p>Note that no other methods of Set are overwritten at this point - if you use any other methods mutating the underlying Set, your changes will not be applied.</p>
304
325
 
326
+ <h2 id="label-Querying+a+collection+of+Anchormodels">Querying a collection of Anchormodels</h2>
327
+
328
+ <p>Because keys are stored as a CSV string in a single column, the standard Rails idiom <code>Model.where(col: array)</code> does <strong>not</strong> work for <code>belongs_to_anchormodels</code> attributes — it compiles to an <code>IN (...)</code> clause that compares against the full column value (e.g. <code>&quot;cat,dog&quot;</code>) rather than the individual entries.</p>
329
+
330
+ <p>Anchormodel provides two helper scopes for bulk-key queries:</p>
331
+
332
+ <pre class="code ruby"><code class="ruby"><span class='comment'># Users that hold at least one of the given keys
333
+ </span><span class='const'>User</span><span class='period'>.</span><span class='id identifier rubyid_with_any_animals'>with_any_animals</span><span class='lparen'>(</span><span class='symbol'>:cat</span><span class='comma'>,</span> <span class='symbol'>:dog</span><span class='rparen'>)</span>
334
+
335
+ <span class='comment'># Users that hold every given key
336
+ </span><span class='const'>User</span><span class='period'>.</span><span class='id identifier rubyid_with_all_animals'>with_all_animals</span><span class='lparen'>(</span><span class='symbol'>:cat</span><span class='comma'>,</span> <span class='symbol'>:dog</span><span class='rparen'>)</span>
337
+ </code></pre>
338
+
339
+ <p>Both scopes accept <code>String</code>, <code>Symbol</code>, or <code>Anchormodel</code> instances (and nested arrays), and raise <code>Anchormodel::InvalidKey</code> on invalid keys. They are always defined for every <code>belongs_to_anchormodels</code> attribute (independent of the <code>model_scopes</code> setting), named <code>with_any_&lt;attribute_name&gt;</code> / <code>with_all_&lt;attribute_name&gt;</code>.</p>
340
+
341
+ <p>For a single key, the per-key scope generated by <code>model_scopes</code> (e.g. <code>User.cat</code>) is the most concise option.</p>
342
+
305
343
  <h2 id="label-Basic+rails+form+for+a+collection+of+Anchormodels">Basic rails form for a collection of Anchormodels</h2>
306
344
 
307
345
  <pre class="code ruby"><code class="ruby">&lt;%= form_with(model: user) do |form| %&gt;
@@ -339,7 +377,7 @@
339
377
  </div></div>
340
378
 
341
379
  <div id="footer">
342
- Generated on Wed May 13 11:46:10 2026 by
380
+ Generated on Wed May 13 15:48:24 2026 by
343
381
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
344
382
  0.9.28 (ruby-3.3.5).
345
383
  </div>
data/doc/index.html CHANGED
@@ -64,6 +64,8 @@
64
64
 
65
65
  <p>This gem provides a simple but powerful alternative to <a href="https://api.rubyonrails.org/v7.0/classes/ActiveRecord/Enum.html">Rails Enums</a>. In contrast to regular Enums, Anchormodels can hold application logic, making them ideal for tying code to database objects.</p>
66
66
 
67
+ <p>For a tour of real-world usage patterns — ordered enums with <code>Comparable</code>, state machines, polymorphic class registries, multi-key collection attributes, cross-anchormodel references, and more — see <a href="EXAMPLES_md.html">EXAMPLES.md</a>.</p>
68
+
67
69
  <h1 id="label-Use+case">Use case</h1>
68
70
 
69
71
  <p>Typically, a Rails application consists of three kinds of state:</p>
@@ -144,8 +146,14 @@
144
146
  <pre class="code ruby"><code class="ruby"><span class='comment'># Retrieve all user roles:
145
147
  </span><span class='const'>Role</span><span class='period'>.</span><span class='id identifier rubyid_all'>all</span>
146
148
 
147
- <span class='comment'># Retrieve a specific role from the String and find its privilege level
149
+ <span class='comment'># Retrieve a specific role. `find` accepts a String, a Symbol, or `nil` (returns nil).
148
150
  </span><span class='const'>Role</span><span class='period'>.</span><span class='id identifier rubyid_find'>find</span><span class='lparen'>(</span><span class='symbol'>:guest</span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_privilege_level'>privilege_level</span>
151
+ <span class='const'>Role</span><span class='period'>.</span><span class='id identifier rubyid_find'>find</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>guest</span><span class='tstring_end'>&#39;</span></span><span class='rparen'>)</span><span class='period'>.</span><span class='id identifier rubyid_privilege_level'>privilege_level</span> <span class='comment'># equivalent
152
+ </span>
153
+ <span class='comment'># Assignment accepts a String, Symbol, or Anchormodel instance:
154
+ </span><span class='ivar'>@user</span><span class='period'>.</span><span class='id identifier rubyid_role'>role</span> <span class='op'>=</span> <span class='tstring'><span class='tstring_beg'>&#39;</span><span class='tstring_content'>admin</span><span class='tstring_end'>&#39;</span></span>
155
+ <span class='ivar'>@user</span><span class='period'>.</span><span class='id identifier rubyid_role'>role</span> <span class='op'>=</span> <span class='symbol'>:admin</span>
156
+ <span class='ivar'>@user</span><span class='period'>.</span><span class='id identifier rubyid_role'>role</span> <span class='op'>=</span> <span class='const'>Role</span><span class='period'>.</span><span class='id identifier rubyid_find'>find</span><span class='lparen'>(</span><span class='symbol'>:admin</span><span class='rparen'>)</span>
149
157
 
150
158
  <span class='comment'># Implement a Rails helper that makes sure users can only edit other users that have a lower privilege level than themselves
151
159
  </span><span class='kw'>def</span> <span class='id identifier rubyid_user_can_edit?'>user_can_edit?</span><span class='lparen'>(</span><span class='id identifier rubyid_this_user'>this_user</span><span class='comma'>,</span> <span class='id identifier rubyid_other_user'>other_user</span><span class='rparen'>)</span>
@@ -159,6 +167,19 @@
159
167
  </span><span class='ivar'>@user</span><span class='period'>.</span><span class='id identifier rubyid_role'>role</span><span class='period'>.</span><span class='id identifier rubyid_admin?'>admin?</span> <span class='comment'># true if and only if the role is admin (false otherwise)
160
168
  </span></code></pre>
161
169
 
170
+ <h2 id="label-Error+handling">Error handling</h2>
171
+
172
+ <p>Anchormodel raises <code>Anchormodel::InvalidKey</code> whenever an unknown key is supplied — from <code>find</code>, from attribute writers, and from the bulk-key scopes shown below:</p>
173
+
174
+ <pre class="code ruby"><code class="ruby"><span class='kw'>begin</span>
175
+ <span class='const'>Role</span><span class='period'>.</span><span class='id identifier rubyid_find'>find</span><span class='lparen'>(</span><span class='symbol'>:does_not_exist</span><span class='rparen'>)</span>
176
+ <span class='kw'>rescue</span> <span class='const'><span class='object_link'><a href="Anchormodel.html" title="Anchormodel (class)">Anchormodel</a></span></span><span class='op'>::</span><span class='const'><span class='object_link'><a href="Anchormodel/InvalidKey.html" title="Anchormodel::InvalidKey (class)">InvalidKey</a></span></span> <span class='op'>=&gt;</span> <span class='id identifier rubyid_e'>e</span>
177
+ <span class='comment'># e.message: &quot;Retrieved undefined anchor model key :does_not_exist for Role.&quot;
178
+ </span><span class='kw'>end</span>
179
+ </code></pre>
180
+
181
+ <p><code>Anchormodel::InvalidKey</code> inherits from <code>RuntimeError</code>, so existing <code>rescue RuntimeError</code> blocks remain compatible while allowing the narrower <code>rescue Anchormodel::InvalidKey</code>.</p>
182
+
162
183
  <p>Your form could look something like this:</p>
163
184
 
164
185
  <pre class="code ruby"><code class="ruby">&lt;%= form_with(model: user) do |form| %&gt;
@@ -302,6 +323,23 @@
302
323
 
303
324
  <p>Note that no other methods of Set are overwritten at this point - if you use any other methods mutating the underlying Set, your changes will not be applied.</p>
304
325
 
326
+ <h2 id="label-Querying+a+collection+of+Anchormodels">Querying a collection of Anchormodels</h2>
327
+
328
+ <p>Because keys are stored as a CSV string in a single column, the standard Rails idiom <code>Model.where(col: array)</code> does <strong>not</strong> work for <code>belongs_to_anchormodels</code> attributes — it compiles to an <code>IN (...)</code> clause that compares against the full column value (e.g. <code>&quot;cat,dog&quot;</code>) rather than the individual entries.</p>
329
+
330
+ <p>Anchormodel provides two helper scopes for bulk-key queries:</p>
331
+
332
+ <pre class="code ruby"><code class="ruby"><span class='comment'># Users that hold at least one of the given keys
333
+ </span><span class='const'>User</span><span class='period'>.</span><span class='id identifier rubyid_with_any_animals'>with_any_animals</span><span class='lparen'>(</span><span class='symbol'>:cat</span><span class='comma'>,</span> <span class='symbol'>:dog</span><span class='rparen'>)</span>
334
+
335
+ <span class='comment'># Users that hold every given key
336
+ </span><span class='const'>User</span><span class='period'>.</span><span class='id identifier rubyid_with_all_animals'>with_all_animals</span><span class='lparen'>(</span><span class='symbol'>:cat</span><span class='comma'>,</span> <span class='symbol'>:dog</span><span class='rparen'>)</span>
337
+ </code></pre>
338
+
339
+ <p>Both scopes accept <code>String</code>, <code>Symbol</code>, or <code>Anchormodel</code> instances (and nested arrays), and raise <code>Anchormodel::InvalidKey</code> on invalid keys. They are always defined for every <code>belongs_to_anchormodels</code> attribute (independent of the <code>model_scopes</code> setting), named <code>with_any_&lt;attribute_name&gt;</code> / <code>with_all_&lt;attribute_name&gt;</code>.</p>
340
+
341
+ <p>For a single key, the per-key scope generated by <code>model_scopes</code> (e.g. <code>User.cat</code>) is the most concise option.</p>
342
+
305
343
  <h2 id="label-Basic+rails+form+for+a+collection+of+Anchormodels">Basic rails form for a collection of Anchormodels</h2>
306
344
 
307
345
  <pre class="code ruby"><code class="ruby">&lt;%= form_with(model: user) do |form| %&gt;
@@ -339,7 +377,7 @@
339
377
  </div></div>
340
378
 
341
379
  <div id="footer">
342
- Generated on Wed May 13 11:46:10 2026 by
380
+ Generated on Wed May 13 15:48:24 2026 by
343
381
  <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
344
382
  0.9.28 (ruby-3.3.5).
345
383
  </div>