anchormodel 0.1.0 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +10 -0
- data/Gemfile.lock +5 -5
- data/README.md +88 -4
- data/anchormodel.gemspec +4 -4
- data/bin/rails +7 -0
- data/doc/Anchormodel/ActiveModelTypeValue.html +17 -13
- data/doc/Anchormodel/Attribute.html +60 -63
- data/doc/Anchormodel/ModelMixin.html +178 -14
- data/doc/Anchormodel/Version.html +4 -4
- data/doc/Anchormodel.html +28 -18
- data/doc/_index.html +1 -1
- data/doc/file.README.html +91 -9
- data/doc/index.html +91 -9
- data/doc/method_list.html +1 -1
- data/doc/top-level-namespace.html +1 -1
- data/lib/anchormodel/active_model_type_value.rb +6 -4
- data/lib/anchormodel/attribute.rb +4 -9
- data/lib/anchormodel/model_mixin.rb +53 -3
- data/lib/anchormodel/version.rb +1 -1
- data/lib/anchormodel.rb +5 -0
- data/test/active_record_model/user_test.rb +72 -2
- data/test/dummy/app/models/user.rb +3 -1
- data/test/dummy/db/migrate/20230107173151_create_users.rb +2 -0
- data/test/dummy/db/schema.rb +8 -7
- metadata +3 -2
data/doc/index.html
CHANGED
@@ -89,7 +89,14 @@
|
|
89
89
|
|
90
90
|
<p>This is why Anchormodel is strictly relying on String keys corresponding to the entries of an Anchormodel.</p>
|
91
91
|
|
92
|
-
<h1 id="label-
|
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>
|
98
|
+
|
99
|
+
<h1 id="label-Basic+example">Basic example</h1>
|
93
100
|
|
94
101
|
<p><code>app/anchormodels/role.rb</code>:</p>
|
95
102
|
|
@@ -116,7 +123,11 @@
|
|
116
123
|
|
117
124
|
<pre class="code ruby"><code class="ruby"><span class='comment'># The DB table `users` must have a String column `users.role`
|
118
125
|
</span><span class='kw'>class</span> <span class='const'>User</span> <span class='op'><</span> <span class='const'>ApplicationRecord</span>
|
119
|
-
<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>
|
120
131
|
<span class='kw'>end</span>
|
121
132
|
</code></pre>
|
122
133
|
|
@@ -135,18 +146,89 @@
|
|
135
146
|
|
136
147
|
<span class='comment'># Pretty print a user's role, e.g. using the Rails FastGettext gem:
|
137
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>
|
138
|
-
</code></pre>
|
139
149
|
|
140
|
-
<
|
141
|
-
|
142
|
-
|
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>
|
143
159
|
</li><li>
|
144
|
-
<p>
|
145
|
-
</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>
|
218
|
+
</code></pre>
|
219
|
+
|
220
|
+
<h2 id="label-Having+multiple+attributes+to+the+same+Anchormodel">Having multiple attributes to the same Anchormodel</h2>
|
221
|
+
|
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>
|
146
228
|
</div></div>
|
147
229
|
|
148
230
|
<div id="footer">
|
149
|
-
Generated on Wed Jan
|
231
|
+
Generated on Wed Jan 25 12:36:39 2023 by
|
150
232
|
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
151
233
|
0.9.28 (ruby-3.1.3).
|
152
234
|
</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>
|
@@ -100,7 +100,7 @@
|
|
100
100
|
</div>
|
101
101
|
|
102
102
|
<div id="footer">
|
103
|
-
Generated on Wed Jan
|
103
|
+
Generated on Wed Jan 25 12:36:39 2023 by
|
104
104
|
<a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
|
105
105
|
0.9.28 (ruby-3.1.3).
|
106
106
|
</div>
|
@@ -11,13 +11,14 @@ class Anchormodel::ActiveModelTypeValue < ActiveModel::Type::Value
|
|
11
11
|
|
12
12
|
# Implementing this instead of cast to force key validation in any case
|
13
13
|
def serialize(value)
|
14
|
+
value = value.presence
|
14
15
|
return case value
|
15
16
|
when Symbol, String
|
16
|
-
unless @attribute.
|
17
|
+
unless @attribute.anchormodel_class.valid_keys.include?(value.to_sym)
|
17
18
|
fail("Attempt to set #{@attribute.attribute_name} to unsupported key #{value.inspect}.")
|
18
19
|
end
|
19
20
|
value.to_s
|
20
|
-
when @attribute.
|
21
|
+
when @attribute.anchormodel_class
|
21
22
|
value.key.to_s
|
22
23
|
when nil
|
23
24
|
nil
|
@@ -27,8 +28,9 @@ class Anchormodel::ActiveModelTypeValue < ActiveModel::Type::Value
|
|
27
28
|
end
|
28
29
|
|
29
30
|
def deserialize(value)
|
30
|
-
|
31
|
-
return @attribute.
|
31
|
+
value = value.presence
|
32
|
+
return value if value.is_a?(@attribute.anchormodel_class)
|
33
|
+
return @attribute.anchormodel_class.find(value)
|
32
34
|
end
|
33
35
|
|
34
36
|
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}=", 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,6 +1,10 @@
|
|
1
1
|
class UserTest < Minitest::Test
|
2
2
|
def setup; end
|
3
3
|
|
4
|
+
def teardown
|
5
|
+
User.destroy_all
|
6
|
+
end
|
7
|
+
|
4
8
|
def test_retrieval
|
5
9
|
assert_equal Role.find(:guest), Role.find('guest')
|
6
10
|
end
|
@@ -17,7 +21,11 @@ class UserTest < Minitest::Test
|
|
17
21
|
)
|
18
22
|
end
|
19
23
|
|
20
|
-
def
|
24
|
+
def test_missing_key
|
25
|
+
assert_raises { Role.find(:does_not_exist) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_basic_setters_and_getters
|
21
29
|
u = User.create!(role: 'guest', locale: 'de') # String assignment
|
22
30
|
assert_equal Role.find(:guest), u.role
|
23
31
|
assert_equal Locale.find(:de), u.locale
|
@@ -45,7 +53,69 @@ class UserTest < Minitest::Test
|
|
45
53
|
def test_custom_comparison
|
46
54
|
assert_equal(-1, Role.find(:moderator) <=> Role.find(:admin))
|
47
55
|
assert_equal(1, Role.find(:moderator) <=> Role.find(:guest))
|
48
|
-
assert_equal(0, Role.find(:moderator) <=> Role.find(
|
56
|
+
assert_equal(0, Role.find(:moderator) <=> Role.find('moderator'))
|
49
57
|
assert Role.find(:moderator) < Role.find(:admin)
|
50
58
|
end
|
59
|
+
|
60
|
+
def test_presence_validation
|
61
|
+
valentine = User.new
|
62
|
+
assert_raises(ActiveRecord::RecordInvalid) { valentine.save! }
|
63
|
+
end
|
64
|
+
|
65
|
+
def test_alternative_column_name
|
66
|
+
ben = User.create!(
|
67
|
+
role: Role.find(:moderator),
|
68
|
+
secondary_role: Role.find(:admin),
|
69
|
+
locale: Locale.find(:de)
|
70
|
+
)
|
71
|
+
assert_equal(Role.find(:moderator), ben.role)
|
72
|
+
assert_equal(Role.find(:admin), ben.secondary_role)
|
73
|
+
assert_equal(Locale.find(:de), ben.locale)
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_optional_attribute
|
77
|
+
jenny = User.create!(role: :admin, locale: :en)
|
78
|
+
assert_nil jenny.secondary_role
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_model_readers_and_writers
|
82
|
+
pia = User.new
|
83
|
+
pia.admin!
|
84
|
+
assert_equal true, pia.admin?
|
85
|
+
assert_equal false, pia.guest?
|
86
|
+
assert_equal Role.find(:admin), pia.role
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_model_scopes
|
90
|
+
User.create!(role: :admin, locale: :en)
|
91
|
+
User.create!(role: :admin, locale: :en)
|
92
|
+
User.create!(role: :moderator, locale: :en)
|
93
|
+
assert_equal 2, User.admin.count
|
94
|
+
assert_equal 1, User.moderator.count
|
95
|
+
assert_equal 0, User.guest.count
|
96
|
+
end
|
97
|
+
|
98
|
+
def test_model_readers_writers_with_different_class_name
|
99
|
+
pia = User.new(locale: :en)
|
100
|
+
pia.de!
|
101
|
+
assert_equal true, pia.de?
|
102
|
+
assert_equal false, pia.fr?
|
103
|
+
assert_equal Locale.find(:de), pia.preferred_locale
|
104
|
+
assert_equal Locale.find(:en), pia.locale
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_model_scopes_with_different_class_name
|
108
|
+
User.create!(role: :admin, locale: :en, preferred_locale: :de)
|
109
|
+
User.create!(role: :admin, locale: :en, preferred_locale: :de)
|
110
|
+
User.create!(role: :admin, locale: :en, preferred_locale: :fr)
|
111
|
+
assert_equal 2, User.de.count
|
112
|
+
assert_equal 1, User.fr.count
|
113
|
+
assert_equal 0, User.en.count
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_rails_blank_assignment
|
117
|
+
u = User.new(role: :admin, secondary_role: :admin, locale: :en, preferred_locale: :en)
|
118
|
+
u.secondary_role = ''
|
119
|
+
assert_nil u.secondary_role
|
120
|
+
end
|
51
121
|
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
class User < ApplicationRecord
|
2
2
|
belongs_to_anchormodel :role
|
3
|
-
belongs_to_anchormodel :
|
3
|
+
belongs_to_anchormodel :secondary_role, Role, optional: true, model_readers: false, model_writers: false, model_scopes: false
|
4
|
+
belongs_to_anchormodel :locale, model_methods: false
|
5
|
+
belongs_to_anchormodel :preferred_locale, Locale
|
4
6
|
end
|
data/test/dummy/db/schema.rb
CHANGED
@@ -10,12 +10,13 @@
|
|
10
10
|
#
|
11
11
|
# It's strongly recommended that you check this file into your version control system.
|
12
12
|
|
13
|
-
ActiveRecord::Schema[7.0].define(version:
|
14
|
-
create_table
|
15
|
-
t.string
|
16
|
-
t.string
|
17
|
-
t.datetime
|
18
|
-
t.datetime
|
13
|
+
ActiveRecord::Schema[7.0].define(version: 20_230_124_084_241) do
|
14
|
+
create_table 'users', force: :cascade do |t|
|
15
|
+
t.string 'role'
|
16
|
+
t.string 'locale'
|
17
|
+
t.datetime 'created_at', null: false
|
18
|
+
t.datetime 'updated_at', null: false
|
19
|
+
t.string 'secondary_role'
|
20
|
+
t.string 'preferred_locale', default: 'en', null: false
|
19
21
|
end
|
20
|
-
|
21
22
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: anchormodel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sandro Kalbermatter
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-01-
|
11
|
+
date: 2023-01-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -166,6 +166,7 @@ files:
|
|
166
166
|
- README.md
|
167
167
|
- Rakefile
|
168
168
|
- anchormodel.gemspec
|
169
|
+
- bin/rails
|
169
170
|
- doc/Anchormodel.html
|
170
171
|
- doc/Anchormodel/ActiveModelTypeValue.html
|
171
172
|
- doc/Anchormodel/Attribute.html
|