anchormodel 0.1.3 → 0.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.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: []
|