anchormodel 0.1.3 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +2 -1
- data/CHANGELOG.md +10 -0
- data/Gemfile.lock +26 -24
- data/README.md +36 -1
- data/VERSION +1 -0
- data/anchormodel.gemspec +15 -15
- data/doc/Anchormodel/ActiveModelTypeValueSingle.html +615 -0
- data/doc/Anchormodel/Attribute.html +2 -2
- data/doc/Anchormodel/ModelMixin.html +18 -283
- data/doc/Anchormodel/Util.html +496 -0
- data/doc/Anchormodel/Version.html +3 -59
- data/doc/Anchormodel.html +5 -5
- data/doc/AnchormodelGenerator.html +201 -0
- data/doc/_index.html +70 -3
- data/doc/class_list.html +1 -1
- data/doc/file.README.html +45 -6
- data/doc/index.html +45 -6
- data/doc/method_list.html +50 -18
- data/doc/top-level-namespace.html +3 -3
- data/lib/anchormodel/{active_model_type_value.rb → active_model_type_value_single.rb} +22 -9
- data/lib/anchormodel/model_mixin.rb +4 -82
- data/lib/anchormodel/simple_form_inputs/anchormodel_input.rb +21 -0
- data/lib/anchormodel/simple_form_inputs/anchormodel_radio_buttons_input.rb +23 -0
- data/lib/anchormodel/simple_form_inputs/helpers/anchormodel_inputs_common.rb +49 -0
- data/lib/anchormodel/util.rb +93 -0
- data/lib/anchormodel/version.rb +1 -7
- data/lib/anchormodel.rb +5 -1
- data/test/active_record_model/user_test.rb +19 -0
- metadata +17 -9
data/doc/method_list.html
CHANGED
@@ -54,23 +54,23 @@
|
|
54
54
|
|
55
55
|
<li class="even ">
|
56
56
|
<div class="item">
|
57
|
-
<span class='object_link'><a href="
|
58
|
-
<small>
|
57
|
+
<span class='object_link'><a href="AnchormodelGenerator.html#add_anchormodel-instance_method" title="AnchormodelGenerator#add_anchormodel (method)">#add_anchormodel</a></span>
|
58
|
+
<small>AnchormodelGenerator</small>
|
59
59
|
</div>
|
60
60
|
</li>
|
61
61
|
|
62
62
|
|
63
63
|
<li class="odd ">
|
64
64
|
<div class="item">
|
65
|
-
<span class='object_link'><a href="Anchormodel
|
66
|
-
<small>Anchormodel
|
65
|
+
<span class='object_link'><a href="Anchormodel.html#all-class_method" title="Anchormodel.all (method)">all</a></span>
|
66
|
+
<small>Anchormodel</small>
|
67
67
|
</div>
|
68
68
|
</li>
|
69
69
|
|
70
70
|
|
71
71
|
<li class="even ">
|
72
72
|
<div class="item">
|
73
|
-
<span class='object_link'><a href="Anchormodel/Attribute.html#
|
73
|
+
<span class='object_link'><a href="Anchormodel/Attribute.html#anchormodel_class-instance_method" title="Anchormodel::Attribute#anchormodel_class (method)">#anchormodel_class</a></span>
|
74
74
|
<small>Anchormodel::Attribute</small>
|
75
75
|
</div>
|
76
76
|
</li>
|
@@ -78,32 +78,32 @@
|
|
78
78
|
|
79
79
|
<li class="odd ">
|
80
80
|
<div class="item">
|
81
|
-
<span class='object_link'><a href="Anchormodel/
|
82
|
-
<small>Anchormodel::
|
81
|
+
<span class='object_link'><a href="Anchormodel/Attribute.html#attribute_name-instance_method" title="Anchormodel::Attribute#attribute_name (method)">#attribute_name</a></span>
|
82
|
+
<small>Anchormodel::Attribute</small>
|
83
83
|
</div>
|
84
84
|
</li>
|
85
85
|
|
86
86
|
|
87
87
|
<li class="even ">
|
88
88
|
<div class="item">
|
89
|
-
<span class='object_link'><a href="Anchormodel/
|
90
|
-
<small>Anchormodel::
|
89
|
+
<span class='object_link'><a href="Anchormodel/ModelMixin.html#belongs_to_anchormodel-class_method" title="Anchormodel::ModelMixin.belongs_to_anchormodel (method)">belongs_to_anchormodel</a></span>
|
90
|
+
<small>Anchormodel::ModelMixin</small>
|
91
91
|
</div>
|
92
92
|
</li>
|
93
93
|
|
94
94
|
|
95
95
|
<li class="odd ">
|
96
96
|
<div class="item">
|
97
|
-
<span class='object_link'><a href="Anchormodel/
|
98
|
-
<small>Anchormodel::
|
97
|
+
<span class='object_link'><a href="Anchormodel/ActiveModelTypeValueSingle.html#cast-instance_method" title="Anchormodel::ActiveModelTypeValueSingle#cast (method)">#cast</a></span>
|
98
|
+
<small>Anchormodel::ActiveModelTypeValueSingle</small>
|
99
99
|
</div>
|
100
100
|
</li>
|
101
101
|
|
102
102
|
|
103
103
|
<li class="even ">
|
104
104
|
<div class="item">
|
105
|
-
<span class='object_link'><a href="Anchormodel/
|
106
|
-
<small>Anchormodel::
|
105
|
+
<span class='object_link'><a href="Anchormodel/ActiveModelTypeValueSingle.html#changed_in_place%3F-instance_method" title="Anchormodel::ActiveModelTypeValueSingle#changed_in_place? (method)">#changed_in_place?</a></span>
|
106
|
+
<small>Anchormodel::ActiveModelTypeValueSingle</small>
|
107
107
|
</div>
|
108
108
|
</li>
|
109
109
|
|
@@ -142,13 +142,21 @@
|
|
142
142
|
|
143
143
|
<li class="odd ">
|
144
144
|
<div class="item">
|
145
|
-
<span class='object_link'><a href="Anchormodel/
|
146
|
-
<small>Anchormodel::
|
145
|
+
<span class='object_link'><a href="Anchormodel/ActiveModelTypeValueSingle.html#initialize-instance_method" title="Anchormodel::ActiveModelTypeValueSingle#initialize (method)">#initialize</a></span>
|
146
|
+
<small>Anchormodel::ActiveModelTypeValueSingle</small>
|
147
147
|
</div>
|
148
148
|
</li>
|
149
149
|
|
150
150
|
|
151
151
|
<li class="even ">
|
152
|
+
<div class="item">
|
153
|
+
<span class='object_link'><a href="Anchormodel/SimpleFormInputs/Helpers/AnchormodelInputsCommon.html#input-instance_method" title="Anchormodel::SimpleFormInputs::Helpers::AnchormodelInputsCommon#input (method)">#input</a></span>
|
154
|
+
<small>Anchormodel::SimpleFormInputs::Helpers::AnchormodelInputsCommon</small>
|
155
|
+
</div>
|
156
|
+
</li>
|
157
|
+
|
158
|
+
|
159
|
+
<li class="odd ">
|
152
160
|
<div class="item">
|
153
161
|
<span class='object_link'><a href="Anchormodel.html#inspect-instance_method" title="Anchormodel#inspect (method)">#inspect</a></span>
|
154
162
|
<small>Anchormodel</small>
|
@@ -156,6 +164,14 @@
|
|
156
164
|
</li>
|
157
165
|
|
158
166
|
|
167
|
+
<li class="even ">
|
168
|
+
<div class="item">
|
169
|
+
<span class='object_link'><a href="Anchormodel/Util.html#install_methods_in_model-class_method" title="Anchormodel::Util.install_methods_in_model (method)">install_methods_in_model</a></span>
|
170
|
+
<small>Anchormodel::Util</small>
|
171
|
+
</div>
|
172
|
+
</li>
|
173
|
+
|
174
|
+
|
159
175
|
<li class="odd ">
|
160
176
|
<div class="item">
|
161
177
|
<span class='object_link'><a href="Anchormodel.html#key-instance_method" title="Anchormodel#key (method)">#key</a></span>
|
@@ -182,13 +198,21 @@
|
|
182
198
|
|
183
199
|
<li class="even ">
|
184
200
|
<div class="item">
|
185
|
-
<span class='object_link'><a href="Anchormodel/
|
186
|
-
<small>Anchormodel::
|
201
|
+
<span class='object_link'><a href="Anchormodel/ActiveModelTypeValueSingle.html#serializable%3F-instance_method" title="Anchormodel::ActiveModelTypeValueSingle#serializable? (method)">#serializable?</a></span>
|
202
|
+
<small>Anchormodel::ActiveModelTypeValueSingle</small>
|
187
203
|
</div>
|
188
204
|
</li>
|
189
205
|
|
190
206
|
|
191
207
|
<li class="odd ">
|
208
|
+
<div class="item">
|
209
|
+
<span class='object_link'><a href="Anchormodel/ActiveModelTypeValueSingle.html#serialize-instance_method" title="Anchormodel::ActiveModelTypeValueSingle#serialize (method)">#serialize</a></span>
|
210
|
+
<small>Anchormodel::ActiveModelTypeValueSingle</small>
|
211
|
+
</div>
|
212
|
+
</li>
|
213
|
+
|
214
|
+
|
215
|
+
<li class="even ">
|
192
216
|
<div class="item">
|
193
217
|
<span class='object_link'><a href="Anchormodel.html#setup!-class_method" title="Anchormodel.setup! (method)">setup!</a></span>
|
194
218
|
<small>Anchormodel</small>
|
@@ -196,7 +220,7 @@
|
|
196
220
|
</li>
|
197
221
|
|
198
222
|
|
199
|
-
<li class="
|
223
|
+
<li class="odd ">
|
200
224
|
<div class="item">
|
201
225
|
<span class='object_link'><a href="Anchormodel.html#to_s-instance_method" title="Anchormodel#to_s (method)">#to_s</a></span>
|
202
226
|
<small>Anchormodel</small>
|
@@ -204,6 +228,14 @@
|
|
204
228
|
</li>
|
205
229
|
|
206
230
|
|
231
|
+
<li class="even ">
|
232
|
+
<div class="item">
|
233
|
+
<span class='object_link'><a href="Anchormodel/ActiveModelTypeValueSingle.html#type-instance_method" title="Anchormodel::ActiveModelTypeValueSingle#type (method)">#type</a></span>
|
234
|
+
<small>Anchormodel::ActiveModelTypeValueSingle</small>
|
235
|
+
</div>
|
236
|
+
</li>
|
237
|
+
|
238
|
+
|
207
239
|
|
208
240
|
</ul>
|
209
241
|
</div>
|
@@ -84,7 +84,7 @@
|
|
84
84
|
|
85
85
|
|
86
86
|
|
87
|
-
<strong class="classes">Classes:</strong> <span class='object_link'><a href="Anchormodel.html" title="Anchormodel (class)">Anchormodel</a></span>
|
87
|
+
<strong class="classes">Classes:</strong> <span class='object_link'><a href="Anchormodel.html" title="Anchormodel (class)">Anchormodel</a></span>, <span class='object_link'><a href="AnchormodelGenerator.html" title="AnchormodelGenerator (class)">AnchormodelGenerator</a></span>, <span class='object_link'><a href="AnchormodelInput.html" title="AnchormodelInput (class)">AnchormodelInput</a></span>, <span class='object_link'><a href="AnchormodelRadioButtonsInput.html" title="AnchormodelRadioButtonsInput (class)">AnchormodelRadioButtonsInput</a></span>
|
88
88
|
|
89
89
|
|
90
90
|
</p>
|
@@ -100,9 +100,9 @@
|
|
100
100
|
</div>
|
101
101
|
|
102
102
|
<div id="footer">
|
103
|
-
Generated on Wed
|
103
|
+
Generated on Wed Apr 24 17:01:46 2024 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.2.2).
|
106
106
|
</div>
|
107
107
|
|
108
108
|
</div>
|
@@ -1,15 +1,22 @@
|
|
1
1
|
# @see https://www.rubydoc.info/docs/rails/ActiveModel/Type/Value
|
2
|
-
class Anchormodel::
|
2
|
+
class Anchormodel::ActiveModelTypeValueSingle < ActiveModel::Type::Value
|
3
3
|
def initialize(attribute)
|
4
4
|
super()
|
5
5
|
@attribute = attribute
|
6
6
|
end
|
7
7
|
|
8
|
+
def type
|
9
|
+
:anchormodel
|
10
|
+
end
|
11
|
+
|
12
|
+
# This converts an Anchormodel instance to string for DB
|
8
13
|
def cast(value)
|
9
|
-
|
14
|
+
value = value.presence
|
15
|
+
return value if value.is_a?(@attribute.anchormodel_class)
|
16
|
+
return @attribute.anchormodel_class.find(value)
|
10
17
|
end
|
11
18
|
|
12
|
-
#
|
19
|
+
# This converts DB or input to an Anchormodel instance
|
13
20
|
def serialize(value)
|
14
21
|
value = value.presence
|
15
22
|
return case value
|
@@ -27,13 +34,19 @@ class Anchormodel::ActiveModelTypeValue < ActiveModel::Type::Value
|
|
27
34
|
end
|
28
35
|
end
|
29
36
|
|
30
|
-
def
|
31
|
-
|
32
|
-
|
33
|
-
|
37
|
+
def serializable?(value)
|
38
|
+
return case value
|
39
|
+
when Symbol, String
|
40
|
+
@attribute.anchormodel_class.valid_keys.exclude?(value.to_sym)
|
41
|
+
when nil, @attribute.anchormodel_class
|
42
|
+
true
|
43
|
+
else
|
44
|
+
false
|
45
|
+
end
|
34
46
|
end
|
35
47
|
|
36
|
-
def
|
37
|
-
|
48
|
+
def changed_in_place?(raw_old_value, value)
|
49
|
+
old_value = deserialize(raw_old_value)
|
50
|
+
old_value != value
|
38
51
|
end
|
39
52
|
end
|
@@ -9,88 +9,10 @@ module Anchormodel::ModelMixin
|
|
9
9
|
|
10
10
|
class_methods do
|
11
11
|
# Creates an attribute linking to an Anchormodel. The attribute should be
|
12
|
-
# present in the DB and the column should be named the same as `attribute_name
|
13
|
-
# @
|
14
|
-
|
15
|
-
|
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
|
23
|
-
attribute_name = attribute_name.to_sym
|
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
|
32
|
-
|
33
|
-
# Register attribute
|
34
|
-
self.anchormodel_attributes = anchormodel_attributes.merge({ attribute_name => attribute }).freeze
|
35
|
-
|
36
|
-
# Add presence validation if required
|
37
|
-
unless optional
|
38
|
-
validates attribute_name, presence: true
|
39
|
-
end
|
40
|
-
|
41
|
-
# Make casting work
|
42
|
-
# Define serializer/deserializer
|
43
|
-
active_model_type_value = Anchormodel::ActiveModelTypeValue.new(attribute)
|
44
|
-
|
45
|
-
# Overwrite reader to force building anchors at every retrieval
|
46
|
-
define_method(attribute_name.to_s) do
|
47
|
-
active_model_type_value.deserialize(read_attribute(attribute_name))
|
48
|
-
end
|
49
|
-
|
50
|
-
# Override writer to fail early when an invalid target value is specified
|
51
|
-
define_method("#{attribute_name}=") do |new_value|
|
52
|
-
write_attribute(attribute_name, active_model_type_value.serialize(new_value))
|
53
|
-
end
|
54
|
-
|
55
|
-
# Supply serializer and deserializer
|
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
|
12
|
+
# present in the DB and the column should be of type String and named the same as `attribute_name`.
|
13
|
+
# @see Anchormodel::Util#install_methods_in_model Parameters
|
14
|
+
def belongs_to_anchormodel(*args, **kwargs)
|
15
|
+
Anchormodel::Util.install_methods_in_model(self, *args, **kwargs)
|
94
16
|
end
|
95
17
|
end
|
96
18
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
unless defined? SimpleForm
|
2
|
+
begin
|
3
|
+
require 'simple_form'
|
4
|
+
rescue LoadError
|
5
|
+
nil
|
6
|
+
end
|
7
|
+
|
8
|
+
end
|
9
|
+
if defined? SimpleForm
|
10
|
+
class AnchormodelInput < SimpleForm::Inputs::CollectionSelectInput
|
11
|
+
include Anchormodel::SimpleFormInputs::Helpers::AnchormodelInputsCommon
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def sf_selection_key
|
16
|
+
:selected
|
17
|
+
end
|
18
|
+
|
19
|
+
def before_render_input; end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
unless defined? SimpleForm
|
2
|
+
begin
|
3
|
+
require 'simple_form'
|
4
|
+
rescue LoadError
|
5
|
+
nil
|
6
|
+
end
|
7
|
+
|
8
|
+
end
|
9
|
+
if defined? SimpleForm
|
10
|
+
class AnchormodelRadioButtonsInput < SimpleForm::Inputs::CollectionRadioButtonsInput
|
11
|
+
include Anchormodel::SimpleFormInputs::Helpers::AnchormodelInputsCommon
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def sf_selection_key
|
16
|
+
:checked
|
17
|
+
end
|
18
|
+
|
19
|
+
def before_render_input
|
20
|
+
@input_type = :radio_buttons
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
class Anchormodel
|
2
|
+
module SimpleFormInputs
|
3
|
+
module Helpers
|
4
|
+
module AnchormodelInputsCommon
|
5
|
+
def input(wrapper_options = nil)
|
6
|
+
unless object.respond_to?(:anchormodel_attributes)
|
7
|
+
fail("The form field object does not appear to respond to `anchormodel_attributes`, \
|
8
|
+
did you `include Anchormodel::ModelMixin` in your `application_record.rb`? Affected object: #{object.inspect}")
|
9
|
+
end
|
10
|
+
|
11
|
+
am_attr = object.anchormodel_attributes[@attribute_name]
|
12
|
+
unless am_attr
|
13
|
+
fail("#{@attribute_name.inspect} does not look like an Anchormodel attribute, is `belongs_to_anchormodel` called in your model? \
|
14
|
+
Affected object: #{object.inspect}")
|
15
|
+
end
|
16
|
+
am_class = am_attr.anchormodel_class
|
17
|
+
|
18
|
+
# Attempt to read selected key from html input options "value", as the caller might not know that this is a select.
|
19
|
+
selected_key = input_options[:value]
|
20
|
+
if selected_key.blank? && object
|
21
|
+
# No selected key override present and a model is present, use the model to find out what to select.
|
22
|
+
selected_am = object.send(@attribute_name)
|
23
|
+
selected_key = selected_am&.key || am_class.all.first
|
24
|
+
end
|
25
|
+
|
26
|
+
options.deep_merge!(
|
27
|
+
label_method: :first,
|
28
|
+
value_method: :second,
|
29
|
+
sf_selection_key => selected_key.to_s,
|
30
|
+
include_blank: am_attr.optional
|
31
|
+
)
|
32
|
+
|
33
|
+
@collection = collect(am_class.all)
|
34
|
+
|
35
|
+
before_render_input
|
36
|
+
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
# Takes an array of objects implementing the methods `label` and `key` and returns an array suitable for simple_form select fields.
|
43
|
+
def collect(flat_array)
|
44
|
+
return flat_array.map { |entry| [entry.label, entry.key] }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
# @api description
|
2
|
+
# A swiss army knife for common functionality
|
3
|
+
module Anchormodel::Util
|
4
|
+
# Installs an anchormodel attribute in a model class
|
5
|
+
# @param model_class [ActiveRecord::Base] Internal only. The model class that the attribute should be installed to.
|
6
|
+
# @param attribute_name [String,Symbol] The name and database column of the attribute
|
7
|
+
# @param anchormodel_class [Class] Class of the Anchormodel (omit if attribute `:foo_bar` holds a `FooBar`)
|
8
|
+
# @param optional [Boolean] If false, a presence validation is added to the model.
|
9
|
+
# @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
|
10
|
+
# @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
|
11
|
+
# @param model_scopes [Boolean] If true, the model is given an ActiveRecord::Enum style scope `MyModel.mykey` for each key in the anchormodel
|
12
|
+
# @param model_methods [Boolean, NilClass] If non-nil, this mass-assigns and overrides `model_readers`, `model_writers` and `model_scopes`
|
13
|
+
def self.install_methods_in_model(model_class, attribute_name, anchormodel_class = nil,
|
14
|
+
optional: false,
|
15
|
+
model_readers: true,
|
16
|
+
model_writers: true,
|
17
|
+
model_scopes: true,
|
18
|
+
model_methods: nil)
|
19
|
+
|
20
|
+
anchormodel_class ||= attribute_name.to_s.classify.constantize
|
21
|
+
attribute_name = attribute_name.to_sym
|
22
|
+
attribute = Anchormodel::Attribute.new(self, attribute_name, anchormodel_class, optional)
|
23
|
+
|
24
|
+
# Mass configurations if model_methods was specfied
|
25
|
+
unless model_methods.nil?
|
26
|
+
model_readers = model_methods
|
27
|
+
model_writers = model_methods
|
28
|
+
model_scopes = model_methods
|
29
|
+
end
|
30
|
+
|
31
|
+
# Register attribute
|
32
|
+
model_class.anchormodel_attributes = model_class.anchormodel_attributes.merge({ attribute_name => attribute }).freeze
|
33
|
+
|
34
|
+
# Add presence validation if required
|
35
|
+
unless optional
|
36
|
+
model_class.validates attribute_name, presence: true
|
37
|
+
end
|
38
|
+
|
39
|
+
# Make casting work
|
40
|
+
# Define serializer/deserializer
|
41
|
+
active_model_type_value = Anchormodel::ActiveModelTypeValueSingle.new(attribute)
|
42
|
+
|
43
|
+
# Overwrite reader to force building anchors at every retrieval
|
44
|
+
model_class.define_method(attribute_name.to_s) do
|
45
|
+
active_model_type_value.deserialize(read_attribute_before_type_cast(attribute_name))
|
46
|
+
end
|
47
|
+
|
48
|
+
# Override writer to fail early when an invalid target value is specified
|
49
|
+
model_class.define_method("#{attribute_name}=") do |new_value|
|
50
|
+
write_attribute(attribute_name, active_model_type_value.serialize(new_value))
|
51
|
+
end
|
52
|
+
|
53
|
+
# Supply serializer and deserializer
|
54
|
+
model_class.attribute attribute_name, active_model_type_value
|
55
|
+
|
56
|
+
# Create ActiveRecord::Enum style reader directly in the model if asked to do so
|
57
|
+
# 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)
|
58
|
+
if model_readers
|
59
|
+
anchormodel_class.all.each do |entry|
|
60
|
+
if model_class.respond_to?(:"#{entry.key}?")
|
61
|
+
fail("Anchormodel reader #{entry.key}? already defined for #{self}, add `model_readers: false` to `belongs_to_anchormodel :#{attribute_name}`.")
|
62
|
+
end
|
63
|
+
model_class.define_method(:"#{entry.key}?") do
|
64
|
+
public_send(attribute_name.to_s) == entry
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Create ActiveRecord::Enum style writer directly in the model if asked to do so
|
70
|
+
# 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)
|
71
|
+
if model_writers
|
72
|
+
anchormodel_class.all.each do |entry|
|
73
|
+
if model_class.respond_to?(:"#{entry.key}!")
|
74
|
+
fail("Anchormodel writer #{entry.key}! already defined for #{self}, add `model_writers: false` to `belongs_to_anchormodel :#{attribute_name}`.")
|
75
|
+
end
|
76
|
+
model_class.define_method(:"#{entry.key}!") do
|
77
|
+
public_send(:"#{attribute_name}=", entry)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Create ActiveRecord::Enum style scope directly in the model class if asked to do so
|
83
|
+
# 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)
|
84
|
+
if model_scopes
|
85
|
+
anchormodel_class.all.each do |entry|
|
86
|
+
if model_class.respond_to?(entry.key)
|
87
|
+
fail("Anchormodel scope #{entry.key} already defined for #{self}, add `model_scopes: false` to `belongs_to_anchormodel :#{attribute_name}`.")
|
88
|
+
end
|
89
|
+
model_class.scope(entry.key, -> { where(attribute_name => entry.key) })
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
data/lib/anchormodel/version.rb
CHANGED
data/lib/anchormodel.rb
CHANGED
@@ -77,7 +77,11 @@ class Anchormodel
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
-
require 'anchormodel/
|
80
|
+
require 'anchormodel/util'
|
81
|
+
require 'anchormodel/active_model_type_value_single'
|
81
82
|
require 'anchormodel/attribute'
|
82
83
|
require 'anchormodel/model_mixin'
|
83
84
|
require 'anchormodel/version'
|
85
|
+
require 'anchormodel/simple_form_inputs/helpers/anchormodel_inputs_common'
|
86
|
+
require 'anchormodel/simple_form_inputs/anchormodel_input'
|
87
|
+
require 'anchormodel/simple_form_inputs/anchormodel_radio_buttons_input'
|
@@ -118,4 +118,23 @@ class UserTest < Minitest::Test
|
|
118
118
|
u.secondary_role = ''
|
119
119
|
assert_nil u.secondary_role
|
120
120
|
end
|
121
|
+
|
122
|
+
# Attempting to create a model with an invalid constant name should fail
|
123
|
+
def test_invalid_key_update
|
124
|
+
assert_raises(RuntimeError) { User.create!(role: :admin, locale: :de, preferred_locale: :invalid) }
|
125
|
+
end
|
126
|
+
|
127
|
+
# Attempting to assign an invalid constant name to a model should fail
|
128
|
+
def test_invalid_key_assignment
|
129
|
+
assert_raises(RuntimeError) { User.new(role: :invalid) }
|
130
|
+
end
|
131
|
+
|
132
|
+
# An invalid constant name into the DB should raise when reading
|
133
|
+
def test_invalid_db_read
|
134
|
+
sql = <<~SQL.squish
|
135
|
+
INSERT INTO users (role, locale, preferred_locale, created_at, updated_at) VALUES ('invalid', 'de', 'de', 'now', 'now')
|
136
|
+
SQL
|
137
|
+
ActiveRecord::Base.connection.execute(sql)
|
138
|
+
assert_raises(RuntimeError) { User.first.role }
|
139
|
+
end
|
121
140
|
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.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sandro Kalbermatter
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-04-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -150,8 +150,8 @@ dependencies:
|
|
150
150
|
- - "~>"
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: 1.6.0
|
153
|
-
description:
|
154
|
-
email:
|
153
|
+
description:
|
154
|
+
email:
|
155
155
|
executables: []
|
156
156
|
extensions: []
|
157
157
|
extra_rdoc_files: []
|
@@ -165,13 +165,17 @@ files:
|
|
165
165
|
- LICENSE
|
166
166
|
- README.md
|
167
167
|
- Rakefile
|
168
|
+
- VERSION
|
168
169
|
- anchormodel.gemspec
|
169
170
|
- bin/rails
|
170
171
|
- doc/Anchormodel.html
|
171
172
|
- doc/Anchormodel/ActiveModelTypeValue.html
|
173
|
+
- doc/Anchormodel/ActiveModelTypeValueSingle.html
|
172
174
|
- doc/Anchormodel/Attribute.html
|
173
175
|
- doc/Anchormodel/ModelMixin.html
|
176
|
+
- doc/Anchormodel/Util.html
|
174
177
|
- doc/Anchormodel/Version.html
|
178
|
+
- doc/AnchormodelGenerator.html
|
175
179
|
- doc/_index.html
|
176
180
|
- doc/class_list.html
|
177
181
|
- doc/css/common.css
|
@@ -187,9 +191,13 @@ files:
|
|
187
191
|
- doc/method_list.html
|
188
192
|
- doc/top-level-namespace.html
|
189
193
|
- lib/anchormodel.rb
|
190
|
-
- lib/anchormodel/
|
194
|
+
- lib/anchormodel/active_model_type_value_single.rb
|
191
195
|
- lib/anchormodel/attribute.rb
|
192
196
|
- lib/anchormodel/model_mixin.rb
|
197
|
+
- lib/anchormodel/simple_form_inputs/anchormodel_input.rb
|
198
|
+
- lib/anchormodel/simple_form_inputs/anchormodel_radio_buttons_input.rb
|
199
|
+
- lib/anchormodel/simple_form_inputs/helpers/anchormodel_inputs_common.rb
|
200
|
+
- lib/anchormodel/util.rb
|
193
201
|
- lib/anchormodel/version.rb
|
194
202
|
- lib/generators/anchormodel/USAGE
|
195
203
|
- lib/generators/anchormodel/anchormodel_generator.rb
|
@@ -233,7 +241,7 @@ homepage: https://github.com/kalsan/anchormodel
|
|
233
241
|
licenses:
|
234
242
|
- LGPL-3.0-or-later
|
235
243
|
metadata: {}
|
236
|
-
post_install_message:
|
244
|
+
post_install_message:
|
237
245
|
rdoc_options: []
|
238
246
|
require_paths:
|
239
247
|
- lib
|
@@ -248,8 +256,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
248
256
|
- !ruby/object:Gem::Version
|
249
257
|
version: '0'
|
250
258
|
requirements: []
|
251
|
-
rubygems_version: 3.
|
252
|
-
signing_key:
|
259
|
+
rubygems_version: 3.5.9
|
260
|
+
signing_key:
|
253
261
|
specification_version: 4
|
254
262
|
summary: Bringing object-oriented programming to Rails enums
|
255
263
|
test_files: []
|