anchormodel 0.0.1

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