anchormodel 0.0.1

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.
@@ -0,0 +1,203 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
5
+ <meta charset="utf-8" />
6
+
7
+ <link rel="stylesheet" href="css/full_list.css" type="text/css" media="screen" />
8
+
9
+ <link rel="stylesheet" href="css/common.css" type="text/css" media="screen" />
10
+
11
+
12
+
13
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
14
+
15
+ <script type="text/javascript" charset="utf-8" src="js/full_list.js"></script>
16
+
17
+
18
+ <title>Method List</title>
19
+ <base id="base_target" target="_parent" />
20
+ </head>
21
+ <body>
22
+ <div id="content">
23
+ <div class="fixed_header">
24
+ <h1 id="full_list_header">Method List</h1>
25
+ <div id="full_list_nav">
26
+
27
+ <span><a target="_self" href="class_list.html">
28
+ Classes
29
+ </a></span>
30
+
31
+ <span><a target="_self" href="method_list.html">
32
+ Methods
33
+ </a></span>
34
+
35
+ <span><a target="_self" href="file_list.html">
36
+ Files
37
+ </a></span>
38
+
39
+ </div>
40
+
41
+ <div id="search">Search: <input type="text" /></div>
42
+ </div>
43
+
44
+ <ul id="full_list" class="method">
45
+
46
+
47
+ <li class="odd ">
48
+ <div class="item">
49
+ <span class='object_link'><a href="Anchormodel.html#==-instance_method" title="Anchormodel#== (method)">#==</a></span>
50
+ <small>Anchormodel</small>
51
+ </div>
52
+ </li>
53
+
54
+
55
+ <li class="even ">
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>
59
+ </div>
60
+ </li>
61
+
62
+
63
+ <li class="odd ">
64
+ <div class="item">
65
+ <span class='object_link'><a href="Anchormodel/Attribute.html#anchor_class-instance_method" title="Anchormodel::Attribute#anchor_class (method)">#anchor_class</a></span>
66
+ <small>Anchormodel::Attribute</small>
67
+ </div>
68
+ </li>
69
+
70
+
71
+ <li class="even ">
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>
74
+ <small>Anchormodel::Attribute</small>
75
+ </div>
76
+ </li>
77
+
78
+
79
+ <li class="odd ">
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>
83
+ </div>
84
+ </li>
85
+
86
+
87
+ <li class="even ">
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>
91
+ </div>
92
+ </li>
93
+
94
+
95
+ <li class="odd ">
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>
99
+ </div>
100
+ </li>
101
+
102
+
103
+ <li class="even ">
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>
107
+ </div>
108
+ </li>
109
+
110
+
111
+ <li class="odd ">
112
+ <div class="item">
113
+ <span class='object_link'><a href="Anchormodel.html#find-class_method" title="Anchormodel.find (method)">find</a></span>
114
+ <small>Anchormodel</small>
115
+ </div>
116
+ </li>
117
+
118
+
119
+ <li class="even ">
120
+ <div class="item">
121
+ <span class='object_link'><a href="Anchormodel.html#index-instance_method" title="Anchormodel#index (method)">#index</a></span>
122
+ <small>Anchormodel</small>
123
+ </div>
124
+ </li>
125
+
126
+
127
+ <li class="odd ">
128
+ <div class="item">
129
+ <span class='object_link'><a href="Anchormodel.html#initialize-instance_method" title="Anchormodel#initialize (method)">#initialize</a></span>
130
+ <small>Anchormodel</small>
131
+ </div>
132
+ </li>
133
+
134
+
135
+ <li class="even ">
136
+ <div class="item">
137
+ <span class='object_link'><a href="Anchormodel/Attribute.html#initialize-instance_method" title="Anchormodel::Attribute#initialize (method)">#initialize</a></span>
138
+ <small>Anchormodel::Attribute</small>
139
+ </div>
140
+ </li>
141
+
142
+
143
+ <li class="odd ">
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>
147
+ </div>
148
+ </li>
149
+
150
+
151
+ <li class="even ">
152
+ <div class="item">
153
+ <span class='object_link'><a href="Anchormodel.html#inspect-instance_method" title="Anchormodel#inspect (method)">#inspect</a></span>
154
+ <small>Anchormodel</small>
155
+ </div>
156
+ </li>
157
+
158
+
159
+ <li class="odd ">
160
+ <div class="item">
161
+ <span class='object_link'><a href="Anchormodel.html#key-instance_method" title="Anchormodel#key (method)">#key</a></span>
162
+ <small>Anchormodel</small>
163
+ </div>
164
+ </li>
165
+
166
+
167
+ <li class="even ">
168
+ <div class="item">
169
+ <span class='object_link'><a href="Anchormodel.html#label-instance_method" title="Anchormodel#label (method)">#label</a></span>
170
+ <small>Anchormodel</small>
171
+ </div>
172
+ </li>
173
+
174
+
175
+ <li class="odd ">
176
+ <div class="item">
177
+ <span class='object_link'><a href="Anchormodel/Attribute.html#optional-instance_method" title="Anchormodel::Attribute#optional (method)">#optional</a></span>
178
+ <small>Anchormodel::Attribute</small>
179
+ </div>
180
+ </li>
181
+
182
+
183
+ <li class="even ">
184
+ <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>
187
+ </div>
188
+ </li>
189
+
190
+
191
+ <li class="odd ">
192
+ <div class="item">
193
+ <span class='object_link'><a href="Anchormodel.html#to_s-instance_method" title="Anchormodel#to_s (method)">#to_s</a></span>
194
+ <small>Anchormodel</small>
195
+ </div>
196
+ </li>
197
+
198
+
199
+
200
+ </ul>
201
+ </div>
202
+ </body>
203
+ </html>
@@ -0,0 +1,110 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>
7
+ Top Level Namespace
8
+
9
+ &mdash; Documentation by YARD 0.9.28
10
+
11
+ </title>
12
+
13
+ <link rel="stylesheet" href="css/style.css" type="text/css" />
14
+
15
+ <link rel="stylesheet" href="css/common.css" type="text/css" />
16
+
17
+ <script type="text/javascript">
18
+ pathId = "";
19
+ relpath = '';
20
+ </script>
21
+
22
+
23
+ <script type="text/javascript" charset="utf-8" src="js/jquery.js"></script>
24
+
25
+ <script type="text/javascript" charset="utf-8" src="js/app.js"></script>
26
+
27
+
28
+ </head>
29
+ <body>
30
+ <div class="nav_wrap">
31
+ <iframe id="nav" src="class_list.html?1"></iframe>
32
+ <div id="resizer"></div>
33
+ </div>
34
+
35
+ <div id="main" tabindex="-1">
36
+ <div id="header">
37
+ <div id="menu">
38
+
39
+ <a href="_index.html">Index</a> &raquo;
40
+
41
+
42
+ <span class="title">Top Level Namespace</span>
43
+
44
+ </div>
45
+
46
+ <div id="search">
47
+
48
+ <a class="full_list_link" id="class_list_link"
49
+ href="class_list.html">
50
+
51
+ <svg width="24" height="24">
52
+ <rect x="0" y="4" width="24" height="4" rx="1" ry="1"></rect>
53
+ <rect x="0" y="12" width="24" height="4" rx="1" ry="1"></rect>
54
+ <rect x="0" y="20" width="24" height="4" rx="1" ry="1"></rect>
55
+ </svg>
56
+ </a>
57
+
58
+ </div>
59
+ <div class="clear"></div>
60
+ </div>
61
+
62
+ <div id="content"><h1>Top Level Namespace
63
+
64
+
65
+
66
+ </h1>
67
+ <div class="box_info">
68
+
69
+
70
+
71
+
72
+
73
+
74
+
75
+
76
+
77
+
78
+
79
+ </div>
80
+
81
+ <h2>Defined Under Namespace</h2>
82
+ <p class="children">
83
+
84
+
85
+
86
+
87
+ <strong class="classes">Classes:</strong> <span class='object_link'><a href="Anchormodel.html" title="Anchormodel (class)">Anchormodel</a></span>
88
+
89
+
90
+ </p>
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+
99
+
100
+ </div>
101
+
102
+ <div id="footer">
103
+ Generated on Sat Dec 17 12:56:00 2022 by
104
+ <a href="https://yardoc.org" title="Yay! A Ruby Documentation Tool" target="_parent">yard</a>
105
+ 0.9.28 (ruby-3.0.4).
106
+ </div>
107
+
108
+ </div>
109
+ </body>
110
+ </html>
@@ -0,0 +1,37 @@
1
+ # @see https://www.rubydoc.info/docs/rails/ActiveModel/Type/Value
2
+ class Anchormodel::ActiveModelTypeValue < ActiveModel::Type::Value
3
+ def initialize(attribute)
4
+ super()
5
+ @attribute = attribute
6
+ end
7
+
8
+ def cast(value)
9
+ serialize value
10
+ end
11
+
12
+ # Implementing this instead of cast to force key validation in any case
13
+ def serialize(value)
14
+ return case value
15
+ when Symbol, String
16
+ unless @attribute.anchor_class.valid_keys.include?(value.to_sym)
17
+ fail("Attempt to set #{@attribute.attribute_name} to unsupported key #{value.inspect}.")
18
+ end
19
+ value.to_s
20
+ when @attribute.anchor_class
21
+ value.key.to_s
22
+ when nil
23
+ nil
24
+ else
25
+ fail "Attempt to set #{@attribute.attribute_name} to unsupported type #{value.class}"
26
+ end
27
+ end
28
+
29
+ def deserialize(value)
30
+ return value if value.is_a?(@attribute.anchor_class)
31
+ return @attribute.anchor_class.find(value)
32
+ end
33
+
34
+ def changed?(old_value, new_value, _new_value_before_type_cast)
35
+ return deserialize(old_value) != deserialize(new_value)
36
+ end
37
+ end
@@ -0,0 +1,24 @@
1
+ # @api description
2
+ # This class holds all information related to a Rails model pointing to an Anchormodel.
3
+ # It is instanciated when {Anchormodel::ModelMixin#belongs_to_anchormodel} is used.
4
+ class Anchormodel::Attribute
5
+ attr_reader :attribute_name
6
+ attr_reader :optional
7
+
8
+ # @param model_class [ActiveRecord::Base] The Rails model where {Anchormodel::ModelMixin#belongs_to_anchormodel} is used
9
+ # @param attribute_name [String,Symbol] The name and database column of the attribute
10
+ # @param anchor_class_name [String] Name of the Anchormodel class (omit if attribute `:foo_bar` holds an `Anchormodels::FooBar`)
11
+ # @param optional [Boolean] If true, a presence validation is added to the model.
12
+ def initialize(model_class, attribute_name, anchor_class_name = nil, optional = false)
13
+ @model_class = model_class
14
+ @attribute_name = attribute_name.to_sym
15
+ @anchor_class_name = anchor_class_name || "Anchormodels::#{attribute_name.to_s.classify}"
16
+ @optional = optional
17
+ end
18
+
19
+ # Getter for the Anchormodel class based on the name passed to the initializer.
20
+ # We are loading the anchor class lazily, because the model mixin instanciates this statically -> avoid premature anchor class loading
21
+ def anchor_class
22
+ @anchor_class ||= @anchor_class_name.constantize
23
+ end
24
+ end
@@ -0,0 +1,46 @@
1
+ # @api description
2
+ # All Rails models making use of #belongs_to_anchormodel must include this mixin. Typically, it is included in `application_record.rb`.
3
+ module Anchormodel::ModelMixin
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ class_attribute :anchormodel_attributes, default: {}.freeze
8
+ end
9
+
10
+ class_methods do
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 anchor_class_name [String] Name of the Anchormodel class (omit if attribute `:foo_bar` holds an `Anchormodels::FooBar`)
15
+ # @param optional [Boolean] If true, a presence validation is added to the model.
16
+ def belongs_to_anchormodel(attribute_name, anchor_class_name = nil, optional: false)
17
+ attribute_name = attribute_name.to_sym
18
+ attribute = Anchormodel::Attribute.new(self, attribute_name, anchor_class_name, optional)
19
+
20
+ # Register attribute
21
+ self.anchormodel_attributes = anchormodel_attributes.merge({ attribute_name => attribute }).freeze
22
+
23
+ # Add presence validation if required
24
+ unless optional
25
+ validates attribute_name, presence: true
26
+ end
27
+
28
+ # Make casting work
29
+ # Define serializer/deserializer
30
+ active_model_type_value = Anchormodel::ActiveModelTypeValue.new(attribute)
31
+
32
+ # Overwrite reader to force building anchors at every retrieval
33
+ define_method(attribute_name.to_s) do
34
+ active_model_type_value.deserialize(read_attribute(attribute_name))
35
+ end
36
+
37
+ # Override writer to fail early when an invalid target value is specified
38
+ define_method("#{attribute_name}=") do |new_value|
39
+ write_attribute(attribute_name, active_model_type_value.serialize(new_value))
40
+ end
41
+
42
+ # Supply serializer and deserializer
43
+ attribute attribute_name, active_model_type_value
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,11 @@
1
+ class Anchormodel
2
+ module Version
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ PATCH = 1
6
+
7
+ EDGE = false
8
+
9
+ LABEL = [MAJOR, MINOR, PATCH, EDGE ? 'edge' : nil].compact.join('.')
10
+ end
11
+ end
@@ -0,0 +1,65 @@
1
+ # @api description
2
+ # Inherit from this class and place your Anchormodel under `app/anchormodels/your_anchor_model.rb`. Use it like `Anchormodels::YourAnchorModel`.
3
+ # Refer to the README for usage.
4
+ class Anchormodel
5
+ attr_reader :key
6
+ attr_reader :index
7
+
8
+ class_attribute :entries_list, default: [] # For ordering
9
+ class_attribute :entries_hash, default: {} # For quick access
10
+ class_attribute :valid_keys, default: Set.new
11
+
12
+ # Returns all possible values this Anchormodel can take.
13
+ def self.all
14
+ entries_list
15
+ end
16
+
17
+ # Retrieves a particular value given the key. Fails if not found.
18
+ # @param key [String,Symbol] The key of the value that should be retrieved.
19
+ def self.find(key)
20
+ return nil if key.nil?
21
+ return entries_hash[key.to_sym] || fail("Retreived undefined anchor model key #{key.inspect} for #{inspect}.")
22
+ end
23
+
24
+ # Call this initializer directly in your Anchormodel class. To set `@foo=:bar` for anchor `:ter`, use `new(:ter, foo: :bar)`
25
+ # @param key [String,Symbol] The key under which the entry should be stored.
26
+ # @param attributes All named arguments to Anchormodel are made available as instance attributes.
27
+ def initialize(key, **attributes)
28
+ @key = key.to_sym
29
+ @index = entries_list.count
30
+
31
+ # Save attributes as instance variables
32
+ attributes.each do |k, v|
33
+ instance_variable_set(:"@#{k}", v)
34
+ end
35
+
36
+ # Register self
37
+ entries_list << self
38
+ entries_hash[key] = self
39
+
40
+ # Register valid keys
41
+ valid_keys << key
42
+ end
43
+
44
+ def ==(other)
45
+ self.class == other.class && key == other.key
46
+ end
47
+
48
+ # Returns a Rails label that is compatible with the [Rails FastGettext](https://github.com/grosser/gettext_i18n_rails/) gem.
49
+ def label
50
+ I18n.t("#{self.class.name.demodulize}|#{key.to_s.humanize}")
51
+ end
52
+
53
+ def inspect
54
+ "#<#{self.class.name}<#{key}>:#{hash}>"
55
+ end
56
+
57
+ def to_s
58
+ inspect
59
+ end
60
+ end
61
+
62
+ require 'anchormodel/active_model_type_value'
63
+ require 'anchormodel/attribute'
64
+ require 'anchormodel/model_mixin'
65
+ require 'anchormodel/version'
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: anchormodel
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Sandro Kalbermatter
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-12-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '7.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '7.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: yard
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.9.28
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 0.9.28
41
+ - !ruby/object:Gem::Dependency
42
+ name: yard-activesupport-concern
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description:
56
+ email:
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - ".gitignore"
62
+ - ".ruby-version"
63
+ - ".yardopts"
64
+ - LICENSE
65
+ - README.md
66
+ - Rakefile
67
+ - anchormodel.gemspec
68
+ - doc/Anchormodel.html
69
+ - doc/Anchormodel/ActiveModelTypeValue.html
70
+ - doc/Anchormodel/Attribute.html
71
+ - doc/Anchormodel/ModelMixin.html
72
+ - doc/Anchormodel/Version.html
73
+ - doc/_index.html
74
+ - doc/class_list.html
75
+ - doc/css/common.css
76
+ - doc/css/full_list.css
77
+ - doc/css/style.css
78
+ - doc/file.README.html
79
+ - doc/file_list.html
80
+ - doc/frames.html
81
+ - doc/index.html
82
+ - doc/js/app.js
83
+ - doc/js/full_list.js
84
+ - doc/js/jquery.js
85
+ - doc/method_list.html
86
+ - doc/top-level-namespace.html
87
+ - lib/anchormodel.rb
88
+ - lib/anchormodel/active_model_type_value.rb
89
+ - lib/anchormodel/attribute.rb
90
+ - lib/anchormodel/model_mixin.rb
91
+ - lib/anchormodel/version.rb
92
+ homepage:
93
+ licenses: []
94
+ metadata: {}
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: 3.0.0
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubygems_version: 3.2.33
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: Bringing object-oriented programming to Rails enums
114
+ test_files: []