anchormodel 0.0.2 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.ruby-version +1 -1
- data/CHANGELOG.md +11 -0
- data/Gemfile +3 -0
- data/Gemfile.lock +206 -0
- data/README.md +98 -9
- data/Rakefile +23 -3
- data/anchormodel.gemspec +26 -10
- data/bin/rails +7 -0
- data/doc/Anchormodel/ActiveModelTypeValue.html +14 -10
- data/doc/Anchormodel/Attribute.html +75 -71
- data/doc/Anchormodel/ModelMixin.html +190 -21
- data/doc/Anchormodel/Version.html +4 -4
- data/doc/Anchormodel.html +151 -53
- data/doc/_index.html +2 -2
- data/doc/file.README.html +118 -47
- data/doc/index.html +118 -47
- data/doc/method_list.html +9 -1
- data/doc/top-level-namespace.html +2 -2
- data/lib/anchormodel/active_model_type_value.rb +4 -4
- data/lib/anchormodel/attribute.rb +4 -9
- data/lib/anchormodel/model_mixin.rb +53 -3
- data/lib/anchormodel/version.rb +2 -2
- data/lib/anchormodel.rb +7 -2
- data/logo.svg +98 -0
- data/test/active_record_model/user_test.rb +115 -0
- data/test/dummy/.gitignore +29 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/anchormodels/locale.rb +6 -0
- data/test/dummy/app/anchormodels/role.rb +14 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/models/application_record.rb +5 -0
- data/test/dummy/app/models/concerns/.keep +0 -0
- data/test/dummy/app/models/user.rb +6 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +33 -0
- data/test/dummy/config/application.rb +37 -0
- data/test/dummy/config/boot.rb +3 -0
- data/test/dummy/config/credentials.yml.enc +1 -0
- data/test/dummy/config/database.yml +8 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/test.rb +50 -0
- data/test/dummy/config/initializers/content_security_policy.rb +25 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +8 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/permissions_policy.rb +11 -0
- data/test/dummy/config/locales/en.yml +33 -0
- data/test/dummy/config/puma.rb +43 -0
- data/test/dummy/config/routes.rb +6 -0
- data/test/dummy/config.ru +6 -0
- data/test/dummy/db/migrate/20230107173151_create_users.rb +12 -0
- data/test/dummy/db/schema.rb +23 -0
- data/test/dummy/db/seeds.rb +7 -0
- data/test/dummy/lib/tasks/.keep +0 -0
- data/test/dummy/log/.keep +0 -0
- data/test/dummy/tmp/.keep +0 -0
- data/test/dummy/tmp/pids/.keep +0 -0
- data/test/test_helper.rb +21 -0
- metadata +149 -13
- data/anchormodel-0.0.1.gem +0 -0
data/doc/index.html
CHANGED
@@ -57,63 +57,59 @@
|
|
57
57
|
<div class="clear"></div>
|
58
58
|
</div>
|
59
59
|
|
60
|
-
<div id="content"><div id='filecontents'
|
60
|
+
<div id="content"><div id='filecontents'>
|
61
|
+
<p><img src=“logo.svg” height=250 alt=“Anchormodel logo”/></p>
|
61
62
|
|
62
|
-
<
|
63
|
-
Enums</a>. In
|
64
|
-
contrast to regular Enums, Anchormodels can hold application logic, making them
|
65
|
-
ideal for tying code to database objects.</p>
|
63
|
+
<h1 id="label-Introducing+Anchormodel">Introducing Anchormodel</h1>
|
66
64
|
|
67
|
-
<
|
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
|
+
|
67
|
+
<h1 id="label-Use+case">Use case</h1>
|
68
68
|
|
69
69
|
<p>Typically, a Rails application consists of three kinds of state:</p>
|
70
|
+
<ul><li>
|
71
|
+
<p>The code, which we can consider static within a given version. Code can reference to other code, e.g. <code>node.parent = other_node</code>.</p>
|
72
|
+
</li><li>
|
73
|
+
<p>The database contents, which can fluctuate within the bounds of the DB schema. Data can reference to other data, ideally via foreign keys.</p>
|
74
|
+
</li><li>
|
75
|
+
<p>A mix of the two, where code needs to be specifically tailored for some kind of data. A prominent example of such a mix would for instance be user roles: roles must be hardcoded in the code because security logic is tied to them. However, as users are assigned to roles in the database, roles also need to be persisted in the database. This is where Anchormodel comes into play.</p>
|
76
|
+
</li></ul>
|
70
77
|
|
71
|
-
<
|
72
|
-
<li>The code, which we can consider static within a given version. Code can
|
73
|
-
reference to other code, e.g. <code>node.parent = other_node</code>.</li>
|
74
|
-
<li>The database contents, which can fluctuate within the bounds of the DB schema.
|
75
|
-
Data can reference to other data, ideally via foreign keys.</li>
|
76
|
-
<li>A mix of the two, where code needs to be specifically tailored for some kind
|
77
|
-
of data. A prominent example of such a mix would for instance be user roles:
|
78
|
-
roles must be hardcoded in the code because security logic is tied to them.
|
79
|
-
However, as users are assigned to roles in the database, roles also need to be
|
80
|
-
persisted in the database. This is where Anchormodel comes into play.</li>
|
81
|
-
</ul>
|
78
|
+
<h2 id="label-Alternatives+coviering+the+same+use+case">Alternatives coviering the same use case</h2>
|
82
79
|
|
83
|
-
<
|
80
|
+
<h3 id="label-Enum">Enum</h3>
|
84
81
|
|
85
|
-
<
|
82
|
+
<p>In Rails and other frameworks, the third point in the listing above is typically achieved via Enums. Enums map either Integers or Strings to constants and therefore provide a link between the DB and the application code.</p>
|
86
83
|
|
87
|
-
<p>
|
88
|
-
achieved via Enums. Enums map either Integers or Strings to constants and
|
89
|
-
therefore provide a link between the DB and the application code.</p>
|
84
|
+
<p>However, at least in Rails, Enums provide very limited customization options. They are basic values that can be used in if-statements. Anchormodels however are regular classes and can easily be extended.</p>
|
90
85
|
|
91
|
-
<
|
92
|
-
They are basic values that can be used in if-statements. Anchormodels however
|
93
|
-
are regular classes and can easily be extended.</p>
|
86
|
+
<h3 id="label-ActiveEnum">ActiveEnum</h3>
|
94
87
|
|
95
|
-
<
|
88
|
+
<p>The gem (<a href="https://github.com/adzap/active_enum">ActiveEnum)</a> allows to create Enum-like classes that can be extended. However it only supports Integer keys. I find this unsatisfactory, as debugging with tools like <code>psql</code> or <code>mysql</code> is made unnecessarily hard when you only see numbers. Keys for enums should be meaningful, making you immediately understand what they stand for.</p>
|
96
89
|
|
97
|
-
<p>
|
98
|
-
Enum-like classes that can be extended. However it only supports Integer keys. I
|
99
|
-
find this unsatisfactory, as debugging with tools like <code>psql</code> or <code>mysql</code> is made
|
100
|
-
unnecessarily hard when you only see numbers. Keys for enums should be
|
101
|
-
meaningful, making you immediately understand what they stand for.</p>
|
90
|
+
<p>This is why Anchormodel is strictly relying on String keys corresponding to the entries of an Anchormodel.</p>
|
102
91
|
|
103
|
-
<
|
104
|
-
|
92
|
+
<h1 id="label-Installation">Installation</h1>
|
93
|
+
<ol><li>
|
94
|
+
<p>Add gem to Gemfile: <code>gem 'anchormodel'</code></p>
|
95
|
+
</li><li>
|
96
|
+
<p>In <code>application_record.rb</code>, add in the class body: <code>include Anchormodel::ModelMixin</code></p>
|
97
|
+
</li></ol>
|
105
98
|
|
106
|
-
<h1 id="example">
|
99
|
+
<h1 id="label-Basic+example">Basic example</h1>
|
107
100
|
|
108
101
|
<p><code>app/anchormodels/role.rb</code>:</p>
|
109
102
|
|
110
|
-
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>
|
103
|
+
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>Role</span> <span class='op'><</span> <span class='const'><span class='object_link'><a href="Anchormodel.html" title="Anchormodel (class)">Anchormodel</a></span></span>
|
104
|
+
<span class='comment'># Make <, > etc. based on <=> operator whic hwe will define below
|
105
|
+
</span> <span class='id identifier rubyid_include'>include</span> <span class='const'>Comparable</span>
|
106
|
+
|
111
107
|
<span class='comment'># Expose the attribute privilege_level
|
112
108
|
</span> <span class='id identifier rubyid_attr_reader'>attr_reader</span> <span class='symbol'>:privilege_level</span>
|
113
109
|
|
114
|
-
<span class='comment'>#
|
110
|
+
<span class='comment'># Define <=> to make user roles comparable based on the privilege level
|
115
111
|
</span> <span class='kw'>def</span> <span class='op'><=></span><span class='lparen'>(</span><span class='id identifier rubyid_other'>other</span><span class='rparen'>)</span>
|
116
|
-
<span class='
|
112
|
+
<span class='ivar'>@privilege_level</span> <span class='op'><=></span> <span class='id identifier rubyid_other'>other</span><span class='period'>.</span><span class='id identifier rubyid_privilege_level'>privilege_level</span>
|
117
113
|
<span class='kw'>end</span>
|
118
114
|
|
119
115
|
<span class='comment'># Declare all available roles
|
@@ -127,17 +123,21 @@ entries of an Anchormodel.</p>
|
|
127
123
|
|
128
124
|
<pre class="code ruby"><code class="ruby"><span class='comment'># The DB table `users` must have a String column `users.role`
|
129
125
|
</span><span class='kw'>class</span> <span class='const'>User</span> <span class='op'><</span> <span class='const'>ApplicationRecord</span>
|
130
|
-
<span class='
|
126
|
+
<span class='comment'># If `users.role` has an `NOT NULL` constraint, use:
|
127
|
+
</span> <span class='id identifier rubyid_belongs_to_anchormodel'>belongs_to_anchormodel</span> <span class='symbol'>:role</span>
|
128
|
+
|
129
|
+
<span class='comment'># If `users.role` can be `NULL`, use the following instead:
|
130
|
+
</span> <span class='id identifier rubyid_belongs_to_anchormodel'>belongs_to_anchormodel</span> <span class='symbol'>:role</span><span class='comma'>,</span> <span class='label'>optional:</span> <span class='kw'>true</span>
|
131
131
|
<span class='kw'>end</span>
|
132
132
|
</code></pre>
|
133
133
|
|
134
134
|
<p>You may now use the following methods:</p>
|
135
135
|
|
136
136
|
<pre class="code ruby"><code class="ruby"><span class='comment'># Retrieve all user roles:
|
137
|
-
</span><span class='const'>
|
137
|
+
</span><span class='const'>Role</span><span class='period'>.</span><span class='id identifier rubyid_all'>all</span>
|
138
138
|
|
139
139
|
<span class='comment'># Retrieve a specific role from the String and find its privilege level
|
140
|
-
</span><span class='const'>
|
140
|
+
</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>
|
141
141
|
|
142
142
|
<span class='comment'># Implement a Rails helper that makes sure users can only edit other users that have a lower privilege level than themselves
|
143
143
|
</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>
|
@@ -146,20 +146,91 @@ entries of an Anchormodel.</p>
|
|
146
146
|
|
147
147
|
<span class='comment'># Pretty print a user's role, e.g. using the Rails FastGettext gem:
|
148
148
|
</span><span class='id identifier rubyid_puts'>puts</span><span class='lparen'>(</span><span class='tstring'><span class='tstring_beg'>"</span><span class='tstring_content'>User </span><span class='embexpr_beg'>#{</span><span class='ivar'>@user</span><span class='period'>.</span><span class='id identifier rubyid_name'>name</span><span class='embexpr_end'>}</span><span class='tstring_content'> has role </span><span class='embexpr_beg'>#{</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_label'>label</span><span class='embexpr_end'>}</span><span class='tstring_end'>"</span></span><span class='rparen'>)</span>
|
149
|
+
|
150
|
+
<span class='comment'># Check whether @user has role admin
|
151
|
+
</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)
|
152
|
+
</span></code></pre>
|
153
|
+
|
154
|
+
<h1 id="label-Rails+Enum+style+model+methods">Rails Enum style model methods</h1>
|
155
|
+
|
156
|
+
<p>By default, Anchormodel adds three kinds of methods for each key to the model:</p>
|
157
|
+
<ul><li>
|
158
|
+
<p>a reader (getter)</p>
|
159
|
+
</li><li>
|
160
|
+
<p>a writer (setter)</p>
|
161
|
+
</li><li>
|
162
|
+
<p>a Rails scope</p>
|
163
|
+
</li></ul>
|
164
|
+
|
165
|
+
<p>For instance:</p>
|
166
|
+
|
167
|
+
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>User</span> <span class='op'><</span> <span class='const'>ApplicationRecord</span>
|
168
|
+
<span class='id identifier rubyid_belongs_to_anchormodel'>belongs_to_anchormodel</span> <span class='symbol'>:role</span> <span class='comment'># where Role has keys :guest, :manager and :admin
|
169
|
+
</span> <span class='id identifier rubyid_belongs_to_anchormodel'>belongs_to_anchormodel</span> <span class='symbol'>:shape</span> <span class='comment'># where Shape has keys :circle and :rectangle
|
170
|
+
</span><span class='kw'>end</span>
|
171
|
+
|
172
|
+
<span class='comment'># User now implements the following methods, given that @user is retrieved as follows:
|
173
|
+
</span><span class='ivar'>@user</span> <span class='op'>=</span> <span class='const'>User</span><span class='period'>.</span><span class='id identifier rubyid_first'>first</span> <span class='comment'># for example
|
174
|
+
</span>
|
175
|
+
<span class='comment'># Readers
|
176
|
+
</span><span class='ivar'>@user</span><span class='period'>.</span><span class='id identifier rubyid_guest?'>guest?</span> <span class='comment'># same as @user.role.guest?
|
177
|
+
</span><span class='ivar'>@user</span><span class='period'>.</span><span class='id identifier rubyid_manager?'>manager?</span>
|
178
|
+
<span class='ivar'>@user</span><span class='period'>.</span><span class='id identifier rubyid_admin?'>admin?</span>
|
179
|
+
<span class='ivar'>@user</span><span class='period'>.</span><span class='id identifier rubyid_rectangle?'>rectangle?</span> <span class='comment'># same as @user.shape.rectangle?
|
180
|
+
</span><span class='ivar'>@user</span><span class='period'>.</span><span class='id identifier rubyid_circle?'>circle?</span>
|
181
|
+
<span class='comment'># Writers
|
182
|
+
</span><span class='ivar'>@user</span><span class='period'>.</span><span class='id identifier rubyid_guest!'>guest!</span> <span class='comment'># same as @user.role = Role.find(:guest)
|
183
|
+
</span><span class='ivar'>@user</span><span class='period'>.</span><span class='id identifier rubyid_manager!'>manager!</span>
|
184
|
+
<span class='ivar'>@user</span><span class='period'>.</span><span class='id identifier rubyid_admin!'>admin!</span>
|
185
|
+
<span class='ivar'>@user</span><span class='period'>.</span><span class='id identifier rubyid_rectangle!'>rectangle!</span> <span class='comment'># same as @user.shape = Shape.find(:rectangle)
|
186
|
+
</span><span class='ivar'>@user</span><span class='period'>.</span><span class='id identifier rubyid_circle!'>circle!</span>
|
187
|
+
<span class='comment'># Scopes
|
188
|
+
</span><span class='const'>User</span><span class='period'>.</span><span class='id identifier rubyid_guest'>guest</span> <span class='comment'># same as User.where(role: 'guest')
|
189
|
+
</span><span class='const'>User</span><span class='period'>.</span><span class='id identifier rubyid_manager'>manager</span>
|
190
|
+
<span class='const'>User</span><span class='period'>.</span><span class='id identifier rubyid_admin'>admin</span>
|
191
|
+
<span class='const'>User</span><span class='period'>.</span><span class='id identifier rubyid_rectangle'>rectangle</span> <span class='comment'># same as User.where(shape: 'rectangle')
|
192
|
+
</span><span class='const'>User</span><span class='period'>.</span><span class='id identifier rubyid_circle'>circle</span>
|
193
|
+
</code></pre>
|
194
|
+
|
195
|
+
<p>This behavior is similar as the one from Rails Enums. If you want to disable it, use:</p>
|
196
|
+
|
197
|
+
<pre class="code ruby"><code class="ruby"><span class='kw'>class</span> <span class='const'>User</span> <span class='op'><</span> <span class='const'>ApplicationRecord</span>
|
198
|
+
<span class='id identifier rubyid_belongs_to_anchormodel'>belongs_to_anchormodel</span> <span class='symbol'>:role</span><span class='comma'>,</span> <span class='label'>model_readers:</span> <span class='kw'>false</span><span class='comma'>,</span> <span class='label'>model_writers:</span> <span class='kw'>false</span><span class='comma'>,</span> <span class='label'>model_scopes:</span> <span class='kw'>false</span>
|
199
|
+
<span class='comment'># or, equivalent, to disable all at once:
|
200
|
+
</span> <span class='id identifier rubyid_belongs_to_anchormodel'>belongs_to_anchormodel</span> <span class='symbol'>:role</span><span class='comma'>,</span> <span class='label'>model_methods:</span> <span class='kw'>false</span>
|
201
|
+
<span class='kw'>end</span>
|
202
|
+
</code></pre>
|
203
|
+
|
204
|
+
<h1 id="label-Calling+a+column+differently+than+the+Anchormodel">Calling a column differently than the Anchormodel</h1>
|
205
|
+
|
206
|
+
<p>If your column name (and the model’s attribute) is called differently than the Anchormodel, you may give the Anchormodel’s class as the second argument. For example:</p>
|
207
|
+
|
208
|
+
<pre class="code ruby"><code class="ruby"><span class='comment'># app/anchormodels/color.rb
|
209
|
+
</span><span class='kw'>class</span> <span class='const'>Color</span> <span class='op'><</span> <span class='const'><span class='object_link'><a href="Anchormodel.html" title="Anchormodel (class)">Anchormodel</a></span></span>
|
210
|
+
<span class='id identifier rubyid_new'>new</span> <span class='symbol'>:green</span>
|
211
|
+
<span class='id identifier rubyid_new'>new</span> <span class='symbol'>:red</span>
|
212
|
+
<span class='kw'>end</span>
|
213
|
+
|
214
|
+
<span class='comment'># app/models/user.rb
|
215
|
+
</span><span class='kw'>class</span> <span class='const'>User</span> <span class='op'><</span> <span class='const'>ApplicationRecord</span>
|
216
|
+
<span class='id identifier rubyid_belongs_to_anchormodel'>belongs_to_anchormodel</span> <span class='symbol'>:favorite_color</span><span class='comma'>,</span> <span class='const'>Color</span>
|
217
|
+
<span class='kw'>end</span>
|
149
218
|
</code></pre>
|
150
219
|
|
151
|
-
<
|
220
|
+
<h2 id="label-Having+multiple+attributes+to+the+same+Anchormodel">Having multiple attributes to the same Anchormodel</h2>
|
152
221
|
|
153
|
-
<
|
154
|
-
|
155
|
-
<
|
156
|
-
</
|
222
|
+
<p>If you want to have multiple attributes in the same model pointing to the same Anchormodel, you need to disable <code>model_methods</code> for at least one of them (otherwise the model methods will clash in your model class):</p>
|
223
|
+
|
224
|
+
<pre class="code ruby"><code class="ruby"><span class='comment'># app/models/user.rb
|
225
|
+
</span> <span class='id identifier rubyid_belongs_to_anchormodel'>belongs_to_anchormodel</span> <span class='symbol'>:role</span>
|
226
|
+
<span class='id identifier rubyid_belongs_to_anchormodel'>belongs_to_anchormodel</span> <span class='symbol'>:secondary_role</span><span class='comma'>,</span> <span class='const'>Role</span><span class='comma'>,</span> <span class='label'>model_methods:</span> <span class='kw'>false</span>
|
227
|
+
</code></pre>
|
157
228
|
</div></div>
|
158
229
|
|
159
230
|
<div id="footer">
|
160
|
-
Generated on
|
231
|
+
Generated on Tue Jan 24 20:27:58 2023 by
|
161
232
|
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
162
|
-
0.9.28 (ruby-3.
|
233
|
+
0.9.28 (ruby-3.1.3).
|
163
234
|
</div>
|
164
235
|
|
165
236
|
</div>
|
data/doc/method_list.html
CHANGED
@@ -62,7 +62,7 @@
|
|
62
62
|
|
63
63
|
<li class="odd ">
|
64
64
|
<div class="item">
|
65
|
-
<span class='object_link'><a href="Anchormodel/Attribute.html#
|
65
|
+
<span class='object_link'><a href="Anchormodel/Attribute.html#anchormodel_class-instance_method" title="Anchormodel::Attribute#anchormodel_class (method)">#anchormodel_class</a></span>
|
66
66
|
<small>Anchormodel::Attribute</small>
|
67
67
|
</div>
|
68
68
|
</li>
|
@@ -189,6 +189,14 @@
|
|
189
189
|
|
190
190
|
|
191
191
|
<li class="odd ">
|
192
|
+
<div class="item">
|
193
|
+
<span class='object_link'><a href="Anchormodel.html#setup!-class_method" title="Anchormodel.setup! (method)">setup!</a></span>
|
194
|
+
<small>Anchormodel</small>
|
195
|
+
</div>
|
196
|
+
</li>
|
197
|
+
|
198
|
+
|
199
|
+
<li class="even ">
|
192
200
|
<div class="item">
|
193
201
|
<span class='object_link'><a href="Anchormodel.html#to_s-instance_method" title="Anchormodel#to_s (method)">#to_s</a></span>
|
194
202
|
<small>Anchormodel</small>
|
@@ -100,9 +100,9 @@
|
|
100
100
|
</div>
|
101
101
|
|
102
102
|
<div id="footer">
|
103
|
-
Generated on
|
103
|
+
Generated on Tue Jan 24 20:27:59 2023 by
|
104
104
|
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
105
|
-
0.9.28 (ruby-3.
|
105
|
+
0.9.28 (ruby-3.1.3).
|
106
106
|
</div>
|
107
107
|
|
108
108
|
</div>
|
@@ -13,11 +13,11 @@ class Anchormodel::ActiveModelTypeValue < ActiveModel::Type::Value
|
|
13
13
|
def serialize(value)
|
14
14
|
return case value
|
15
15
|
when Symbol, String
|
16
|
-
unless @attribute.
|
16
|
+
unless @attribute.anchormodel_class.valid_keys.include?(value.to_sym)
|
17
17
|
fail("Attempt to set #{@attribute.attribute_name} to unsupported key #{value.inspect}.")
|
18
18
|
end
|
19
19
|
value.to_s
|
20
|
-
when @attribute.
|
20
|
+
when @attribute.anchormodel_class
|
21
21
|
value.key.to_s
|
22
22
|
when nil
|
23
23
|
nil
|
@@ -27,8 +27,8 @@ class Anchormodel::ActiveModelTypeValue < ActiveModel::Type::Value
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def deserialize(value)
|
30
|
-
return value if value.is_a?(@attribute.
|
31
|
-
return @attribute.
|
30
|
+
return value if value.is_a?(@attribute.anchormodel_class)
|
31
|
+
return @attribute.anchormodel_class.find(value)
|
32
32
|
end
|
33
33
|
|
34
34
|
def changed?(old_value, new_value, _new_value_before_type_cast)
|
@@ -3,22 +3,17 @@
|
|
3
3
|
# It is instanciated when {Anchormodel::ModelMixin#belongs_to_anchormodel} is used.
|
4
4
|
class Anchormodel::Attribute
|
5
5
|
attr_reader :attribute_name
|
6
|
+
attr_reader :anchormodel_class
|
6
7
|
attr_reader :optional
|
7
8
|
|
8
9
|
# @param model_class [ActiveRecord::Base] The Rails model where {Anchormodel::ModelMixin#belongs_to_anchormodel} is used
|
9
10
|
# @param attribute_name [String,Symbol] The name and database column of the attribute
|
10
|
-
# @param
|
11
|
+
# @param anchormodel_class [Class] Class of the Anchormodel (omit if attribute `:foo_bar` holds an `FooBar`)
|
11
12
|
# @param optional [Boolean] If true, a presence validation is added to the model.
|
12
|
-
def initialize(model_class, attribute_name,
|
13
|
+
def initialize(model_class, attribute_name, anchormodel_class = nil, optional = false)
|
13
14
|
@model_class = model_class
|
14
15
|
@attribute_name = attribute_name.to_sym
|
15
|
-
@
|
16
|
+
@anchormodel_class = anchormodel_class
|
16
17
|
@optional = optional
|
17
18
|
end
|
18
|
-
|
19
|
-
# Getter for the Anchormodel class based on the name passed to the initializer.
|
20
|
-
# We are loading the anchor class lazily, because the model mixin instanciates this statically -> avoid premature anchor class loading
|
21
|
-
def anchor_class
|
22
|
-
@anchor_class ||= @anchor_class_name.constantize
|
23
|
-
end
|
24
19
|
end
|
@@ -11,11 +11,24 @@ module Anchormodel::ModelMixin
|
|
11
11
|
# Creates an attribute linking to an Anchormodel. The attribute should be
|
12
12
|
# present in the DB and the column should be named the same as `attribute_name.`
|
13
13
|
# @param attribute_name [String,Symbol] The name and database column of the attribute
|
14
|
-
# @param
|
14
|
+
# @param anchormodel_class [Class] Class of the Anchormodel (omit if attribute `:foo_bar` holds a `FooBar`)
|
15
15
|
# @param optional [Boolean] If true, a presence validation is added to the model.
|
16
|
-
|
16
|
+
# @param model_readers [Boolean] If true, the model is given an ActiveRecord::Enum style method `my_model.my_key?` reader for each key in the anchormodel
|
17
|
+
# @param model_writers [Boolean] If true, the model is given an ActiveRecord::Enum style method `my_model.my_key!` writer for each key in the anchormodel
|
18
|
+
# @param model_scopes [Boolean] If true, the model is given an ActiveRecord::Enum style scope `MyModel.mykey` for each key in the anchormodel
|
19
|
+
# @param model_methods [Boolean, NilClass] If non-nil, this mass-assigns and overrides `model_readers`, `model_writers` and `model_scopes`
|
20
|
+
def belongs_to_anchormodel(attribute_name, anchormodel_class = nil, optional: false, model_readers: true,
|
21
|
+
model_writers: true, model_scopes: true, model_methods: nil)
|
22
|
+
anchormodel_class ||= attribute_name.to_s.classify.constantize
|
17
23
|
attribute_name = attribute_name.to_sym
|
18
|
-
attribute = Anchormodel::Attribute.new(self, attribute_name,
|
24
|
+
attribute = Anchormodel::Attribute.new(self, attribute_name, anchormodel_class, optional)
|
25
|
+
|
26
|
+
# Mass configurations if model_methods was specfied
|
27
|
+
unless model_methods.nil?
|
28
|
+
model_readers = model_methods
|
29
|
+
model_writers = model_methods
|
30
|
+
model_scopes = model_methods
|
31
|
+
end
|
19
32
|
|
20
33
|
# Register attribute
|
21
34
|
self.anchormodel_attributes = anchormodel_attributes.merge({ attribute_name => attribute }).freeze
|
@@ -41,6 +54,43 @@ module Anchormodel::ModelMixin
|
|
41
54
|
|
42
55
|
# Supply serializer and deserializer
|
43
56
|
attribute attribute_name, active_model_type_value
|
57
|
+
|
58
|
+
# Create ActiveRecord::Enum style reader directly in the model if asked to do so
|
59
|
+
# For a model User with anchormodel Role with keys :admin and :guest, this creates user.admin? and user.guest? (returning true iff role is admin/guest)
|
60
|
+
if model_readers
|
61
|
+
anchormodel_class.all.each do |entry|
|
62
|
+
if respond_to?(:"#{entry.key}?")
|
63
|
+
fail("Anchormodel reader #{entry.key}? already defined for #{self}, add `model_readers: false` to `belongs_to_anchormodel :#{attribute_name}`.")
|
64
|
+
end
|
65
|
+
define_method(:"#{entry.key}?") do
|
66
|
+
public_send(attribute_name.to_s) == entry
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Create ActiveRecord::Enum style writer directly in the model if asked to do so
|
72
|
+
# For a model User with anchormodel Role with keys :admin and :guest, this creates user.admin! and user.guest! (setting the role to admin/guest)
|
73
|
+
if model_writers
|
74
|
+
anchormodel_class.all.each do |entry|
|
75
|
+
if respond_to?(:"#{entry.key}!")
|
76
|
+
fail("Anchormodel writer #{entry.key}! already defined for #{self}, add `model_writers: false` to `belongs_to_anchormodel :#{attribute_name}`.")
|
77
|
+
end
|
78
|
+
define_method(:"#{entry.key}!") do
|
79
|
+
public_send(:"#{attribute_name.to_s}=", entry)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Create ActiveRecord::Enum style scope directly in the model class if asked to do so
|
85
|
+
# For a model User with anchormodel Role with keys :admin and :guest, this creates user.admin! and user.guest! (setting the role to admin/guest)
|
86
|
+
if model_scopes
|
87
|
+
anchormodel_class.all.each do |entry|
|
88
|
+
if respond_to?(entry.key)
|
89
|
+
fail("Anchormodel scope #{entry.key} already defined for #{self}, add `model_scopes: false` to `belongs_to_anchormodel :#{attribute_name}`.")
|
90
|
+
end
|
91
|
+
scope(entry.key, ->{where(attribute_name => entry.key)})
|
92
|
+
end
|
93
|
+
end
|
44
94
|
end
|
45
95
|
end
|
46
96
|
end
|
data/lib/anchormodel/version.rb
CHANGED
data/lib/anchormodel.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
# @api description
|
2
|
-
# Inherit from this class and place your Anchormodel under `app/anchormodels/your_anchor_model.rb`. Use it like `
|
2
|
+
# Inherit from this class and place your Anchormodel under `app/anchormodels/your_anchor_model.rb`. Use it like `YourAnchorModel`.
|
3
3
|
# Refer to the README for usage.
|
4
4
|
class Anchormodel
|
5
5
|
attr_reader :key
|
@@ -13,7 +13,7 @@ class Anchormodel
|
|
13
13
|
# When a descendant of Anchormodel is first used, it must overwrite the class_attributes
|
14
14
|
# to prevent cross-class pollution.
|
15
15
|
def self.setup!
|
16
|
-
fail("`setup!` was called twice for Anchormodel subclass #{
|
16
|
+
fail("`setup!` was called twice for Anchormodel subclass #{self}.") if setup_completed
|
17
17
|
self.entries_list = entries_list.dup
|
18
18
|
self.entries_hash = entries_hash.dup
|
19
19
|
self.valid_keys = valid_keys.dup
|
@@ -52,6 +52,11 @@ class Anchormodel
|
|
52
52
|
|
53
53
|
# Register valid keys
|
54
54
|
valid_keys << key
|
55
|
+
|
56
|
+
# Define boolean reader
|
57
|
+
self.class.define_method(:"#{key}?") do
|
58
|
+
@key == key
|
59
|
+
end
|
55
60
|
end
|
56
61
|
|
57
62
|
def ==(other)
|
data/logo.svg
ADDED
@@ -0,0 +1,98 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
2
|
+
<svg
|
3
|
+
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
4
|
+
xmlns:cc="http://creativecommons.org/ns#"
|
5
|
+
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
6
|
+
xmlns:svg="http://www.w3.org/2000/svg"
|
7
|
+
xmlns="http://www.w3.org/2000/svg"
|
8
|
+
xmlns:xlink="http://www.w3.org/1999/xlink"
|
9
|
+
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
10
|
+
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
11
|
+
width="156.75362mm"
|
12
|
+
height="164.14165mm"
|
13
|
+
viewBox="0 0 156.75362 164.14165"
|
14
|
+
version="1.1"
|
15
|
+
id="svg8"
|
16
|
+
inkscape:version="1.0.2 (e86c870879, 2021-01-15)"
|
17
|
+
sodipodi:docname="logo.svg">
|
18
|
+
<defs
|
19
|
+
id="defs2">
|
20
|
+
<linearGradient
|
21
|
+
inkscape:collect="always"
|
22
|
+
id="linearGradient900">
|
23
|
+
<stop
|
24
|
+
style="stop-color:#1b252e;stop-opacity:1"
|
25
|
+
offset="0"
|
26
|
+
id="stop896" />
|
27
|
+
<stop
|
28
|
+
style="stop-color:#551544;stop-opacity:1"
|
29
|
+
offset="1"
|
30
|
+
id="stop898" />
|
31
|
+
</linearGradient>
|
32
|
+
<linearGradient
|
33
|
+
inkscape:collect="always"
|
34
|
+
xlink:href="#linearGradient900"
|
35
|
+
id="linearGradient902"
|
36
|
+
x1="86.611481"
|
37
|
+
y1="46.172035"
|
38
|
+
x2="28.230343"
|
39
|
+
y2="199.08452"
|
40
|
+
gradientUnits="userSpaceOnUse" />
|
41
|
+
</defs>
|
42
|
+
<sodipodi:namedview
|
43
|
+
id="base"
|
44
|
+
pagecolor="#ffffff"
|
45
|
+
bordercolor="#666666"
|
46
|
+
borderopacity="1.0"
|
47
|
+
inkscape:pageopacity="0.0"
|
48
|
+
inkscape:pageshadow="2"
|
49
|
+
inkscape:zoom="0.7"
|
50
|
+
inkscape:cx="265.73964"
|
51
|
+
inkscape:cy="446.69416"
|
52
|
+
inkscape:document-units="mm"
|
53
|
+
inkscape:current-layer="layer1"
|
54
|
+
inkscape:document-rotation="0"
|
55
|
+
showgrid="false"
|
56
|
+
inkscape:window-width="3436"
|
57
|
+
inkscape:window-height="1401"
|
58
|
+
inkscape:window-x="1920"
|
59
|
+
inkscape:window-y="18"
|
60
|
+
inkscape:window-maximized="1"
|
61
|
+
fit-margin-top="0"
|
62
|
+
fit-margin-left="0"
|
63
|
+
fit-margin-right="0"
|
64
|
+
fit-margin-bottom="0" />
|
65
|
+
<metadata
|
66
|
+
id="metadata5">
|
67
|
+
<rdf:RDF>
|
68
|
+
<cc:Work
|
69
|
+
rdf:about="">
|
70
|
+
<dc:format>image/svg+xml</dc:format>
|
71
|
+
<dc:type
|
72
|
+
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
73
|
+
<dc:title></dc:title>
|
74
|
+
</cc:Work>
|
75
|
+
</rdf:RDF>
|
76
|
+
</metadata>
|
77
|
+
<g
|
78
|
+
inkscape:label="Ebene 1"
|
79
|
+
inkscape:groupmode="layer"
|
80
|
+
id="layer1"
|
81
|
+
transform="translate(-17.637819,-39.186618)">
|
82
|
+
<g
|
83
|
+
id="g908">
|
84
|
+
<path
|
85
|
+
id="path892"
|
86
|
+
style="fill:#1b252e;fill-opacity:1;stroke-width:0.15337"
|
87
|
+
d="m 95.252639,194.5457 c -1.41201,-0.23145 -5.03489,-1.03694 -6.05811,-1.34693 -0.67482,-0.20442 -1.45547,-0.42833 -1.73476,-0.49756 -0.2793,-0.0692 -0.5054,-0.17869 -0.50245,-0.24326 0.003,-0.0646 0.33499,-0.39855 0.73786,-0.74216 2.81821,-2.40369 7.0189,-6.93571 9.78801,-10.56003 1.65518,-2.16638 4.418551,-6.26673 5.590951,-8.29601 l 0.91492,-1.58362 0.17975,0.51562 c 0.19829,0.56881 3.1596,6.44921 3.34949,6.65122 0.23843,0.25364 6.5474,-2.86237 9.51285,-4.69841 0.97006,-0.60061 2.14334,-1.35392 2.60728,-1.67403 0.46395,-0.32011 0.97448,-0.66761 1.13452,-0.77224 0.32704,-0.2138 2.00088,-1.48828 3.4727,-2.64414 2.6923,-2.11434 8.57098,-7.66106 11.05332,-10.42915 1.08345,-1.20817 3.53989,-4.20374 4.27607,-5.21457 0.3686,-0.50612 0.70991,-0.95472 0.75846,-0.9969 0.3503,-0.30429 0.94266,-1.27968 0.87857,-1.44669 -0.13018,-0.33925 -1.78342,-1.15491 -3.14808,-1.55317 -2.09639,-0.61181 -3.58423,-0.49323 -4.98895,0.39762 -0.68157,0.43223 -0.76661,0.45123 -1.08534,0.24239 -0.1966,-0.12882 -0.41527,-0.50019 -0.50676,-0.86065 -0.19706,-0.77636 0.1249,-2.05053 0.87227,-3.45196 0.95415,-1.78921 4.53453,-6.34984 7.75721,-9.88103 1.99573,-2.18677 8.71813,-8.76843 10.85401,-10.62675 0.45764,-0.39817 1.1309,-1.00538 1.49612,-1.34935 0.36522,-0.34396 0.7496,-0.67384 0.85418,-0.73306 0.10458,-0.0592 0.48951,-0.38373 0.8554,-0.72114 2.0724,-1.9111 5.20704,-4.52441 5.42698,-4.52441 0.25342,0 0.92126,0.77425 1.25677,1.45701 0.36828,0.74943 1.19233,4.00134 1.59346,6.28816 0.42098,2.39999 0.59505,5.00463 0.60006,8.97888 0.006,4.39041 -0.34888,8.26542 -1.00238,10.95919 -0.12278,0.50612 -0.43073,1.78355 -0.68434,2.83874 -0.79619,3.31272 -2.2497,7.14262 -4.42316,11.65471 -0.7693,1.59704 -2.35022,4.34476 -3.10098,5.38962 l -0.40127,0.55847 -0.42764,-1.24864 c -0.95987,-2.80272 -2.35482,-5.21065 -4.09834,-7.07448 -1.03208,-1.1033 -1.44582,-1.28089 -1.2857,-0.55186 0.0673,0.30648 0.0301,0.4875 -0.11828,0.57514 -0.11909,0.0703 -0.48563,0.54198 -0.81455,1.0481 -0.32892,0.50612 -0.65223,0.98924 -0.71846,1.07359 -0.0662,0.0844 -0.5863,0.84354 -1.15571,1.68707 -2.54545,3.77086 -4.09551,5.91646 -5.59537,7.74517 -0.10378,0.12653 -0.39457,0.50601 -0.64621,0.84328 -1.27223,1.70519 -3.75133,4.52325 -5.67119,6.44656 -2.96589,2.97124 -4.10665,3.96217 -7.13581,6.19861 -0.63441,0.4684 -1.35909,1.00484 -1.61038,1.19211 -4.1958,3.12669 -10.47563,6.40426 -15.94724,8.32321 -2.52792,0.88655 -5.96165,1.85402 -7.66849,2.16061 -0.42177,0.0758 -1.284471,0.24206 -1.917121,0.36957 -1.07882,0.21741 -2.49852,0.27109 -3.37414,0.12755 z m -38.34245,-20.90135 c -0.37959,-0.0587 -1.19903,-0.26582 -1.82097,-0.46029 -1.4476,-0.45264 -2.20148,-1.18893 -4.19844,-4.10042 -0.35869,-0.52297 -1.16979,-1.63497 -1.80244,-2.47113 -0.63265,-0.83616 -1.27152,-1.68509 -1.41972,-1.8865 -0.37675,-0.51208 -3.37187,-5.08288 -5.28024,-8.0581 -0.16684,-0.26012 -0.25187,-0.21114 -1.03747,0.59763 -1.0352,1.06572 -2.41226,3.14361 -3.18142,4.80055 -1.01247,2.18107 -1.35074,2.96208 -1.35074,3.11869 0,0.39455 -0.30952,0.27056 -0.6243,-0.25008 -0.38393,-0.635 -3.05657,-5.56477 -3.05657,-5.63794 0,-0.0253 -0.34499,-0.77007 -0.76666,-1.65512 -3.62493,-7.60865 -5.36989,-15.78016 -5.16469,-24.18585 0.1149,-4.70644 0.52571,-7.94843 1.56449,-12.34627 0.60555,-2.5637 0.94317,-3.10491 2.02771,-3.25038 0.58994,-0.0791 0.6355,-0.0472 3.11765,2.18416 3.69543,3.32209 3.65849,3.28929 4.82122,4.28084 0.58952,0.50274 1.624,1.44045 2.29882,2.0838 0.67483,0.64335 1.60587,1.50882 2.06897,1.92327 1.38935,1.24336 6.59798,6.48742 7.83197,7.88523 2.85563,3.23474 5.41308,6.48762 6.87312,8.74208 1.15489,1.78328 1.34304,3.21188 0.6198,4.7059 l -0.31773,0.65634 -0.3911,-0.4648 c -1.43941,-1.71064 -4.31582,-1.71196 -7.71289,-0.004 -0.66458,0.33422 -1.14374,0.67122 -1.1348,0.79809 0.027,0.38279 3.37081,4.70913 5.4964,7.11143 3.28049,3.70752 7.9538,8.33881 11.18029,11.07975 1.08387,0.92077 2.7743,2.29046 3.0687,2.48646 0.18181,0.12105 0.56345,0.38991 0.84807,0.59747 l 0.5175,0.37739 -1.13098,0.2903 c -1.81773,0.46657 -4.12807,0.88567 -5.75426,1.04382 -1.6988,0.16521 -5.14143,0.16922 -6.18929,0.007 z" />
|
88
|
+
<path
|
89
|
+
style="fill:#1b252e;fill-opacity:1;stroke-width:0.15337"
|
90
|
+
d="m 73.138899,201.72191 -0.56528,-0.0933 1.4855,-0.68276 c 0.81703,-0.37552 2.14116,-1.05816 2.94252,-1.51696 0.80135,-0.45879 1.66406,-0.95169 1.91712,-1.09532 0.25306,-0.14363 1.12505,-0.68768 1.93776,-1.20899 l 1.47765,-0.94784 1.20632,0.59575 c 3.11279,1.53728 11.07339,3.39783 13.69965,3.20187 2.761661,-0.20603 9.355601,-1.83619 11.892461,-2.94002 0.25306,-0.1101 0.8397,-0.35341 1.30365,-0.54067 0.46394,-0.18725 1.41453,-0.64405 2.11243,-1.01508 1.17819,-0.62639 1.31523,-0.66441 1.91712,-0.53179 0.35652,0.0786 1.64896,0.21806 2.87209,0.31 1.22312,0.092 2.91402,0.23168 3.75756,0.3105 0.84353,0.0788 2.60345,0.21832 3.91093,0.31002 1.30747,0.0917 3.13641,0.23564 4.0643,0.31985 0.92788,0.0842 3.20542,0.24923 5.0612,0.36669 1.85577,0.11747 6.68692,0.45761 10.73588,0.75589 4.04896,0.29827 9.39774,0.68404 11.88616,0.85726 2.48842,0.17323 5.35261,0.38212 6.36485,0.46422 1.01224,0.0821 2.80666,0.21473 3.98761,0.29476 1.18095,0.08 3.3032,0.23076 4.71612,0.33496 l 2.56894,0.18947 v 1.10058 1.1006 l -0.94271,0.0985 c -1.19572,0.12499 -99.547791,0.0875 -100.309831,-0.0383 z"
|
91
|
+
id="path890" />
|
92
|
+
<path
|
93
|
+
style="fill:url(#linearGradient902);fill-opacity:1;stroke-width:0.15337"
|
94
|
+
d="m 41.036419,203.29922 c -6.6454,-0.25704 -14.21974,-1.23751 -21.01166,-2.71988 l -2.30054,-0.50212 -0.0432,-1.79323 -0.0432,-1.79323 h 0.37904 c 0.20848,0 1.60825,0.10136 3.11061,0.22524 10.63489,0.87692 22.45948,0.90484 29.34121,0.0693 3.76481,-0.45712 5.8148,-0.86537 9.6623,-1.92425 1.10377,-0.30376 4.02123,-1.29358 4.21767,-1.43096 0.0843,-0.059 0.49845,-0.23296 0.92022,-0.38663 1.13997,-0.41532 4.5733,-2.14947 6.03707,-3.04928 0.70543,-0.43364 1.45985,-0.89494 1.67649,-1.02512 0.21664,-0.13016 0.42066,-0.317 0.45339,-0.41517 0.0335,-0.10047 -0.6994,-0.55901 -1.6765,-1.04892 -1.8084,-0.90672 -4.36159,-2.39156 -5.41686,-3.15024 -0.33742,-0.24258 -1.13111,-0.81303 -1.76376,-1.26766 -0.63265,-0.45464 -1.25654,-0.92032 -1.38643,-1.03485 -0.12989,-0.11452 -0.65371,-0.49821 -1.16405,-0.85262 -0.77073,-0.53524 -1.2576,-0.72503 -2.87437,-1.12049 -2.86906,-0.70177 -4.85117,-1.66258 -8.5316,-4.13561 -0.92776,-0.62339 -0.79088,-0.78248 0.26097,-0.30332 4.46726,2.03499 8.71337,2.87048 13.77217,2.70988 4.10395,-0.13028 7.45721,-0.94969 11.11931,-2.71714 2.09711,-1.01213 4.1177,-2.38394 6.12915,-4.16117 3.41928,-3.02112 5.88138,-6.87339 6.75097,-10.56273 0.27091,-1.14937 0.35977,-2.0885 0.42086,-4.44773 0.0735,-2.83832 0.0532,-3.13138 -0.39818,-5.75136 -0.50005,-2.90249 -0.94333,-4.79646 -1.7208,-7.3525 -0.95328,-3.13399 -1.86555,-6.01802 -2.2103,-6.98758 -0.74485,-2.09477 -2.2714,-7.21923 -2.74129,-9.20218 -0.62085,-2.62001 -0.90852,-4.85573 -0.90778,-7.05501 7.7e-4,-2.21786 0.0982,-2.96973 0.68586,-5.29126 0.39846,-1.57416 0.5011,-1.85373 1.31563,-3.58354 0.67742,-1.43865 2.0959,-3.66606 3.95402,-6.20893 1.25993,-1.72424 1.56117,-2.64487 1.30591,-3.99108 -0.17761,-0.93673 -0.65101,-2.473533 -0.91673,-2.976013 -0.0769,-0.14547 -0.35663,-0.70099 -0.62156,-1.23449 -0.26494,-0.5335 -0.72881,-1.3617 -1.03083,-1.84044 -0.97191,-1.54057 -1.94825,-3.01305 -2.11117,-3.184 -0.0872,-0.0915 -0.29662,-0.35868 -0.46533,-0.59369 -1.09629,-1.52715 -2.45246,-3.15178 -2.59373,-3.10716 -0.0918,0.029 -0.55825,0.84197 -1.03654,1.80661 -0.47829,0.96464 -0.94415,1.83725 -1.03525,1.93913 -0.0911,0.10188 -0.16563,0.22329 -0.16563,0.26979 0,0.12574 -1.57103,2.69246 -2.7724,4.52949 -1.44789,2.21397 -1.72104,2.75714 -2.43907,4.850033 -0.66067,1.9257 -0.81545,2.30074 -1.60621,3.89205 -1.03911,2.09106 -1.69309,3.09169 -4.07081,6.22862 -0.63224,0.8341 -1.18813,1.55106 -1.23532,1.59324 -0.21602,0.19306 -3.42321,4.39195 -4.84529,6.34349 -2.07985,2.85422 -1.88564,2.61561 -3.22535,3.96265 -1.37051,1.37802 -2.12769,1.81163 -5.01634,2.8727 -1.17075,0.43004 -2.58559,1.01805 -3.14408,1.30668 -0.9721,0.50238 -2.11381,1.00482 -2.28331,1.00482 -0.0441,0 -0.0377,-0.45438 0.0142,-1.00974 0.10865,-1.16347 0.414,-1.73937 1.4081,-2.65569 0.75852,-0.69918 0.80168,-0.8047 0.44893,-1.09746 -0.19524,-0.16204 -0.57385,-0.16458 -2.02246,-0.0136 -1.89867,0.19791 -2.35014,0.37554 -3.15661,1.24198 -0.23912,0.2569 -0.56594,0.4671 -0.72625,0.4671 -0.28385,0 -0.28353,-0.0131 0.0124,-0.49846 0.24797,-0.40669 0.30401,-0.77371 0.30448,-1.9938 l 5.8e-4,-1.49537 0.65183,-0.009 c 0.3585,-0.005 0.9969,-0.0737 1.41867,-0.15267 1.01064,-0.18915 3.29519,-0.58008 4.44772,-0.76109 0.50612,-0.0795 1.2653,-0.14672 1.68707,-0.14942 1.03433,-0.007 2.2882,-0.35796 2.97428,-0.83341 2.15787,-1.49541 3.65872,-3.97932 7.09614,-11.74417 2.19522,-4.95879 2.98763,-6.36418 4.70631,-8.346903 1.52839,-1.76319 2.14727,-3.3015 3.39833,-8.44709 0.34866,-1.43401 0.79363,-3.19393 0.98883,-3.91093 0.1952,-0.71701 0.57889,-2.13184 0.85263,-3.14408 0.90123,-3.33253 1.95174,-5.89998 2.85166,-6.96947 0.22999,-0.27333 1.21767,-1.03368 2.19484,-1.68967 1.62375,-1.09003 4.00077,-3.04778 4.13072,-3.40211 0.0294,-0.0802 0.27661,-0.47739 0.54931,-0.88262 0.7353,-1.092652 0.622173,-2.172714 0.65428,-3.700167 0.135499,-1.114491 0.04248,-1.98501 0.04367,-2.882257 -0.003,-1.098313 -6.1e-4,-1.09281 -1.46118,-3.198177 -1.1186,-1.612407 -1.4619,-2.337314 -1.97395,-4.168128 -0.27794,-0.993778 -0.60222,-2.009572 -0.72062,-2.257321 -0.24391,-0.51039 -0.84841,-0.693314 -0.99851,-0.302154 -0.10313,0.268734 0.13857,2.701444 0.3662,3.685946 0.32225,1.393673 1.42421,3.530014 3.00588,5.827421 0.23227,0.337383 0.31705,0.704178 0.31905,1.380328 0.005,1.553322 -0.49519,4.517177 -0.9494,5.630359 -0.38865,0.95248 -0.4709,1.04784 -1.11098,1.2881 -1.00238,0.37624 -2.38129,1.15619 -3.47422,1.96511 -2.1279,1.57496 -3.1697,3.36476 -4.87586,8.37664 -0.84438,2.48041 -1.2728,3.16177 -2.68653,4.27269 -1.82955,1.43767 -3.8035,2.14778 -5.99871,2.15798 -2.13629,0.01 -3.67499,-0.46323 -5.39381,-1.65858 -2.71034,-1.88491 -4.14318,-5.54123 -3.50238,-8.93734 0.43441,-2.30231 1.9869,-4.12525 4.23872,-4.97713 0.92571,-0.3502 1.23232,-0.39351 2.3671,-0.33435 2.65672,0.1385 4.66571,1.70352 5.21081,4.05927 0.39962,1.72706 -0.55389,3.80536 -2.02346,4.41038 -0.77856,0.32053 -1.08281,0.29571 -1.47355,-0.12021 -0.27559,-0.29335 -0.33388,-0.53961 -0.33582,-1.41867 -10e-4,-0.58504 -0.064,-1.16103 -0.13925,-1.27997 -0.0795,-0.12555 -0.48059,-0.24387 -0.95622,-0.28206 -0.67523,-0.0542 -0.91122,-0.003 -1.342,0.29295 -0.88662,0.60853 -1.14833,1.09576 -1.21414,2.26046 -0.072,1.27419 0.24674,2.10751 1.20281,3.14472 0.98007,1.06325 2.09255,1.53941 3.60142,1.54148 1.75379,0.002 2.76495,-0.42961 4.07913,-1.74274 1.03331,-1.0325 1.34866,-1.58329 1.98544,-3.46778 0.407,-1.20445 0.42441,-1.80436 0.3093,-10.65627 -0.0606,-4.661608 -0.0594,-4.684396 0.36585,-6.592133 0.28751,-1.289968 0.79721,-2.831455 1.5627,-4.726147 1.17206,-2.900981 1.61924,-4.392044 2.27888,-7.598724 0.97966,-4.762375 1.57007,-6.945514 2.22608,-8.231402 0.51784,-1.015058 1.52018,-1.995741 2.5296,-2.474959 0.7469,-0.354584 1.12243,-0.435623 1.99381,-0.430258 0.59047,0.0036 1.34232,0.102385 1.67077,0.219442 0.57999,0.2067 0.637209,-0.0049 1.211549,-0.466417 0.32525,-0.261381 1.673725,-0.588873 1.726835,-0.535763 0.0531,0.05311 -1.088622,0.397568 -1.310402,0.49862 -0.22178,0.10105 -0.461412,0.380558 -0.461412,0.446628 0,0.06607 0.29332,-0.0093 0.65182,-0.167561 0.3585,-0.158228 1.33077,-0.443393 2.1606,-0.6337 1.81498,-0.416234 2.60115,-0.312694 4.35945,0.574145 1.03611,0.522586 1.2825,0.686003 2.11109,1.400215 0.43396,0.374058 2.92248,3.285079 3.83456,4.485588 0.41656,0.548297 0.9862,1.259647 1.26586,1.580777 0.62539,0.718121 1.54268,2.122096 1.92324,2.943632 0.1563,0.337413 0.54704,1.5452 0.86831,2.683971 0.562141,1.992564 0.676201,2.779197 1.010581,6.969582 0.0535,0.670019 0.20039,1.375683 0.32649,1.56814 0.1261,0.192458 0.58483,0.526634 1.01939,0.742614 0.43456,0.21598 0.95066,0.526382 1.14689,0.68978 0.47426,0.600469 -0.14673,0.833732 -0.40181,1.041679 -0.25508,0.207946 -0.58883,0.395371 -0.90814,0.499476 -0.31932,0.104106 -0.61508,0.23235 -0.65726,0.28499 -0.0422,0.05264 -0.5598,0.260238 -1.15027,0.461329 -0.590481,0.201089 -1.071266,0.363914 -1.226961,0.477834 -0.155695,0.11392 -0.94054,0.28929 -2.14718,0.370661 -0.80124,0.05403 -1.40429,-0.01422 -2.37723,-0.269023 -1.61172,-0.422106 -1.6866,-0.469764 -1.68903,-1.074984 -0.002,-0.451312 0.39004,-2.486038 0.58161,-3.020001 0.0454,-0.12653 0.0702,-0.285825 0.0552,-0.35399 -0.015,-0.06816 0.0735,-0.344229 0.19671,-0.613479 0.12322,-0.269248 0.29976,-0.782864 0.39231,-1.141366 0.0925,-0.358501 0.131394,-0.412415 0.31573,-0.651821 0.184336,-0.239406 0.289784,-1.126088 0.43993,-1.610383 0.36651,-1.169012 0.5758,-2.99071 0.3436,-2.99071 -0.0916,0 -0.43265,0.138718 -0.75793,0.308262 -0.5429,0.282977 -0.60753,0.386765 -0.78792,1.265301 -0.10808,0.526371 -0.24376,1.312196 -0.30151,1.74628 -0.0577,0.434083 -0.18192,0.882689 -0.27592,0.996904 -0.094,0.114213 -0.62604,1.052204 -1.18229,2.084422 0,0 -0.841451,1.186734 -1.01137,1.876762 -0.107127,0.435035 0.0153,1.344004 0.0153,1.344004 0.0182,1.600573 0.0806,1.845164 0.65351,2.56016 0.623124,0.55109 1.352525,0.794081 2.0373,1.155699 0.6579,0.328575 0.84199,0.348892 2.41114,0.266112 1.16041,-0.06122 1.254704,-0.122688 1.85666,-0.297303 0.642834,-0.186473 0.91697,-0.470992 1.84486,-0.792819 3.899401,-1.35246 7.483391,-3.374538 10.735891,-6.057173 1.55617,-1.283521 1.75239,-1.518013 1.75865,-2.101667 0.005,-0.490661 -0.64786,-1.213611 -2.24614,-2.486319 -0.35129,-0.279733 -0.83392,-0.678505 -1.07252,-0.886162 -0.23859,-0.207655 -1.00807,-0.806978 -1.70996,-1.33183 -0.70189,-0.524852 -1.80751,-1.368375 -2.45693,-1.874495 -0.64942,-0.50612 -1.59351,-1.227819 -2.09797,-1.603774 -2.535681,-1.889723 -4.859941,-4.123792 -6.676631,-6.417541 l -0.64385,-0.812935 0.44097,0.100144 c 0.91942,0.208796 4.838831,1.622861 8.262841,2.981114 1.0966,0.435002 2.61496,1.020191 3.37414,1.300421 0.75918,0.28023 1.75992,0.678509 2.22386,0.885065 0.46394,0.206555 1.08509,0.467641 1.38033,0.580191 1.11913,0.426633 4.58414,2.172877 6.51299,3.282316 1.57117,0.903706 2.77182,1.751643 3.62712,2.561578 0.59559,0.563993 0.74914,0.810951 0.74914,1.20482 0,0.757136 -0.42232,2.222 -0.99019,3.434607 -0.53562,1.143729 -2.56065,4.792558 -2.83717,5.112184 -0.0881,0.101882 -0.16217,0.239915 -0.16452,0.30674 -0.60418,1.029727 -2.00307,2.263117 -2.41131,2.783035 -0.40824,0.519918 -1.69849,1.716624 -4.33269,3.496545 -0.46395,0.31349 -0.87804,0.61584 -0.92022,0.67189 -0.0422,0.056 -0.42177,0.29253 -0.84354,0.52551 -1.19968,0.66271 -5.75136,4.20502 -5.75136,4.47596 0,0.0505 0.408,0.51482 0.90667,1.03183 1.61495,1.67435 2.00088,2.15258 2.42948,3.0105 0.37135,0.74334 0.42141,1.01756 0.42141,2.30861 0,1.39567 -0.0285,1.52282 -0.60244,2.68392 -0.46595,0.94269 -0.87338,1.48048 -1.79826,2.37361 l -1.19582,1.15477 -0.17356,2.25314 c -0.26642,3.45867 0.19766,7.08283 1.15125,8.990473 0.78726,1.57491 0.94599,1.83667 1.91876,3.16427 0.55627,0.75918 1.12942,1.55286 1.27366,1.76375 0.36382,0.53192 1.76725,2.4972 1.91666,2.68397 0.21959,0.27449 1.52957,2.4397 1.80811,2.98854 0.75105,1.47985 1.62724,4.77847 2.01715,7.59397 0.2087,1.50697 0.27278,2.91131 0.26247,5.75137 -0.0231,6.37299 -0.79293,11.84404 -2.88083,20.47486 -0.26096,1.07875 -2.07571,6.98039 -2.52312,8.20529 -0.16946,0.46394 -0.44182,1.18861 -0.60523,1.61038 -0.16342,0.42177 -0.59173,1.56054 -0.95182,2.5306 -0.805,2.16866 -1.04508,2.70476 -2.85029,6.36485 -1.53141,3.10495 -2.3526,4.65426 -3.03431,5.72469 -0.23241,0.36492 -0.46494,0.77902 -0.51674,0.92022 -0.19674,0.53623 -4.705301,7.32677 -5.109191,7.69516 -0.0462,0.0422 -0.24994,0.31824 -0.45267,0.61348 -0.40722,0.59306 -2.93585,3.64605 -3.6203,4.37104 -0.23891,0.25306 -0.73922,0.80519 -1.11181,1.22696 -1.88944,2.1388 -4.64263,4.78285 -6.69399,6.42863 -0.59047,0.47373 -1.24613,1.00166 -1.45701,1.17318 -2.71644,2.2094 -9.36833,5.9623 -12.33487,6.95917 -0.41551,0.13962 -1.07116,0.38234 -1.45702,0.53937 -1.07847,0.43893 -3.00962,1.0293 -4.5358,1.38662 -0.75918,0.17776 -1.62242,0.38632 -1.9183,0.4635 -1.81895,0.47439 -5.74384,1.07173 -9.58443,1.45871 -2.176,0.21926 -8.51635,0.38314 -11.04263,0.28543 z"
|
95
|
+
id="path847" />
|
96
|
+
</g>
|
97
|
+
</g>
|
98
|
+
</svg>
|