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.
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="Anchormodel.html#all-class_method" title="Anchormodel.all (method)">all</a></span>
58
- <small>Anchormodel</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/Attribute.html#anchormodel_class-instance_method" title="Anchormodel::Attribute#anchormodel_class (method)">#anchormodel_class</a></span>
66
- <small>Anchormodel::Attribute</small>
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#attribute_name-instance_method" title="Anchormodel::Attribute#attribute_name (method)">#attribute_name</a></span>
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/ModelMixin.html#belongs_to_anchormodel-class_method" title="Anchormodel::ModelMixin.belongs_to_anchormodel (method)">belongs_to_anchormodel</a></span>
82
- <small>Anchormodel::ModelMixin</small>
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/ActiveModelTypeValue.html#cast-instance_method" title="Anchormodel::ActiveModelTypeValue#cast (method)">#cast</a></span>
90
- <small>Anchormodel::ActiveModelTypeValue</small>
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/ActiveModelTypeValue.html#changed%3F-instance_method" title="Anchormodel::ActiveModelTypeValue#changed? (method)">#changed?</a></span>
98
- <small>Anchormodel::ActiveModelTypeValue</small>
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/ActiveModelTypeValue.html#deserialize-instance_method" title="Anchormodel::ActiveModelTypeValue#deserialize (method)">#deserialize</a></span>
106
- <small>Anchormodel::ActiveModelTypeValue</small>
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/ActiveModelTypeValue.html#initialize-instance_method" title="Anchormodel::ActiveModelTypeValue#initialize (method)">#initialize</a></span>
146
- <small>Anchormodel::ActiveModelTypeValue</small>
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/ActiveModelTypeValue.html#serialize-instance_method" title="Anchormodel::ActiveModelTypeValue#serialize (method)">#serialize</a></span>
186
- <small>Anchormodel::ActiveModelTypeValue</small>
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="even ">
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 Jan 25 12:36:39 2023 by
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.1.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::ActiveModelTypeValue < ActiveModel::Type::Value
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
- serialize value
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
- # Implementing this instead of cast to force key validation in any case
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 deserialize(value)
31
- value = value.presence
32
- return value if value.is_a?(@attribute.anchormodel_class)
33
- return @attribute.anchormodel_class.find(value)
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 changed?(old_value, new_value, _new_value_before_type_cast)
37
- return deserialize(old_value) != deserialize(new_value)
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
- # @param attribute_name [String,Symbol] The name and database column of the attribute
14
- # @param anchormodel_class [Class] Class of the Anchormodel (omit if attribute `:foo_bar` holds a `FooBar`)
15
- # @param optional [Boolean] If true, a presence validation is added to the model.
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
@@ -1,11 +1,5 @@
1
1
  class Anchormodel
2
2
  module Version
3
- MAJOR = 0
4
- MINOR = 1
5
- PATCH = 3
6
-
7
- EDGE = false
8
-
9
- LABEL = [MAJOR, MINOR, PATCH, EDGE ? 'edge' : nil].compact.join('.')
3
+ LABEL = (Pathname.new(__FILE__).dirname.dirname.dirname / 'VERSION').read
10
4
  end
11
5
  end
data/lib/anchormodel.rb CHANGED
@@ -77,7 +77,11 @@ class Anchormodel
77
77
  end
78
78
  end
79
79
 
80
- require 'anchormodel/active_model_type_value'
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.3
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: 2023-05-19 00:00:00.000000000 Z
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/active_model_type_value.rb
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.4.13
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: []