anchormodel 0.1.3 → 0.1.4
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|