anchormodel 0.1.3 → 0.1.4
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/CHANGELOG.md +5 -0
- data/Gemfile.lock +26 -24
- data/VERSION +1 -0
- data/anchormodel.gemspec +15 -15
- data/doc/Anchormodel/Attribute.html +3 -3
- data/doc/Anchormodel/ModelMixin.html +19 -284
- data/doc/Anchormodel/Version.html +4 -60
- data/doc/Anchormodel.html +5 -5
- data/doc/_index.html +25 -5
- data/doc/class_list.html +1 -1
- data/doc/file.README.html +14 -6
- data/doc/frames.html +10 -5
- data/doc/index.html +14 -6
- data/doc/method_list.html +43 -19
- data/doc/top-level-namespace.html +4 -4
- 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/util.rb +93 -0
- data/lib/anchormodel/version.rb +1 -7
- data/lib/anchormodel.rb +2 -1
- data/pkg/anchormodel-0.1.3.gem +0 -0
- data/test/active_record_model/user_test.rb +19 -0
- metadata +12 -9
@@ -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,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,8 @@ 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'
|
Binary file
|
@@ -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.4
|
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-23 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,6 +165,7 @@ 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
|
@@ -187,14 +188,16 @@ files:
|
|
187
188
|
- doc/method_list.html
|
188
189
|
- doc/top-level-namespace.html
|
189
190
|
- lib/anchormodel.rb
|
190
|
-
- lib/anchormodel/
|
191
|
+
- lib/anchormodel/active_model_type_value_single.rb
|
191
192
|
- lib/anchormodel/attribute.rb
|
192
193
|
- lib/anchormodel/model_mixin.rb
|
194
|
+
- lib/anchormodel/util.rb
|
193
195
|
- lib/anchormodel/version.rb
|
194
196
|
- lib/generators/anchormodel/USAGE
|
195
197
|
- lib/generators/anchormodel/anchormodel_generator.rb
|
196
198
|
- lib/generators/anchormodel/templates/anchormodel.rb.erb
|
197
199
|
- logo.svg
|
200
|
+
- pkg/anchormodel-0.1.3.gem
|
198
201
|
- test/active_record_model/user_test.rb
|
199
202
|
- test/dummy/.gitignore
|
200
203
|
- test/dummy/Rakefile
|
@@ -233,7 +236,7 @@ homepage: https://github.com/kalsan/anchormodel
|
|
233
236
|
licenses:
|
234
237
|
- LGPL-3.0-or-later
|
235
238
|
metadata: {}
|
236
|
-
post_install_message:
|
239
|
+
post_install_message:
|
237
240
|
rdoc_options: []
|
238
241
|
require_paths:
|
239
242
|
- lib
|
@@ -248,8 +251,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
248
251
|
- !ruby/object:Gem::Version
|
249
252
|
version: '0'
|
250
253
|
requirements: []
|
251
|
-
rubygems_version: 3.
|
252
|
-
signing_key:
|
254
|
+
rubygems_version: 3.5.6
|
255
|
+
signing_key:
|
253
256
|
specification_version: 4
|
254
257
|
summary: Bringing object-oriented programming to Rails enums
|
255
258
|
test_files: []
|