active_module 0.1.0
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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +27 -0
- data/LICENSE +21 -0
- data/README.md +367 -0
- data/Rakefile +12 -0
- data/lib/active_module/base.rb +82 -0
- data/lib/active_module/invalid_module_value.rb +21 -0
- data/lib/active_module/module_refinement.rb +22 -0
- data/lib/active_module/modules_index.rb +28 -0
- data/lib/active_module/register.rb +12 -0
- data/lib/active_module/version.rb +5 -0
- data/lib/active_module.rb +14 -0
- metadata +90 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f2bfc44bc97bbd3905636170057b010c4e2bd7cdb04bb749dcb0951c4ca7ae21
|
4
|
+
data.tar.gz: 22bec343c43a708452e724d648c7520dd923459d197c426acf796ca983163965
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 930ab5f0f7998773628a5d63fe58108b2cd9aaa430c0c168b49f08152dd965c06cb12439a83eade0a8ba929782bca3522c2e0a3f6506aa8883439e2e1c84962e
|
7
|
+
data.tar.gz: 22aa9eb2fd4d4c9a3d74671eee90f22c91aef2eb0f2b6591729fe15cad177c25d8a0a0ed17ac16b14f28f499d3f206bfe87e644115cf3f0384b3000adc7cd630
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require: rubocop-rspec
|
2
|
+
|
3
|
+
AllCops:
|
4
|
+
TargetRubyVersion: 3.0
|
5
|
+
SuggestExtensions: false
|
6
|
+
NewCops: disable
|
7
|
+
|
8
|
+
Style/StringLiterals:
|
9
|
+
EnforcedStyle: double_quotes
|
10
|
+
|
11
|
+
Style/StringLiteralsInInterpolation:
|
12
|
+
EnforcedStyle: double_quotes
|
13
|
+
|
14
|
+
Layout/LineLength:
|
15
|
+
Max: 80
|
16
|
+
|
17
|
+
Metrics/MethodLength:
|
18
|
+
Max: 20
|
19
|
+
|
20
|
+
Metrics/BlockLength:
|
21
|
+
Max: 100
|
22
|
+
|
23
|
+
Style/Documentation:
|
24
|
+
Enabled: false
|
25
|
+
|
26
|
+
RSpec/ExampleLength:
|
27
|
+
Max: 10
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2024 Pedro Miguel dos Santos Morte Martins Rolo
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,367 @@
|
|
1
|
+
# ActiveModule
|
2
|
+
|
3
|
+
*"Let's make `Modules` and `Classes` first-class values in active record!"*
|
4
|
+
|
5
|
+
ActiveModel/ActiveRecord implementation of the Module attribute type.
|
6
|
+
|
7
|
+
- Allows storing a reference to a `Module` or `Class` (because they are modules) in a `:string`
|
8
|
+
database field in a safe and efficient way.
|
9
|
+
- It automatically casts strings and symbols into modules when creating and querying objects.
|
10
|
+
- Symbols or strings refer to the modules using unqualified names.
|
11
|
+
|
12
|
+
This is a very generic mechanism that enables many possible utilizations, for instance:
|
13
|
+
- Strategy Pattern (composition-based polymorphism)
|
14
|
+
- Rapid prototyping static domain objects
|
15
|
+
- Static configuration management
|
16
|
+
- Rich Java/C#-like enums
|
17
|
+
|
18
|
+
You can find examples of these in [Usage -> Examples :](#Examples)
|
19
|
+
|
20
|
+
## Installation
|
21
|
+
|
22
|
+
Add to your gemfile:
|
23
|
+
|
24
|
+
```ruby
|
25
|
+
gem 'active_module', "~>0.1"
|
26
|
+
```
|
27
|
+
|
28
|
+
Add to a rails initializer, such as `intializers/types.rb`
|
29
|
+
|
30
|
+
```ruby
|
31
|
+
ActiveModule.register!
|
32
|
+
```
|
33
|
+
|
34
|
+
or this, if you prefer to have a better idea of what you are doing:
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
ActiveModel::Type.register(:active_module, ActiveModule::Base)
|
38
|
+
ActiveRecord::Type.register(:active_module, ActiveModule::Base)
|
39
|
+
```
|
40
|
+
|
41
|
+
|
42
|
+
## Usage
|
43
|
+
|
44
|
+
Add a string field to the table you want to hold a module attribute in your migrations
|
45
|
+
```ruby
|
46
|
+
create_table :my_ar_objects do |t|
|
47
|
+
t.string :module_field
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
Now given this module structure:
|
52
|
+
```ruby
|
53
|
+
class MyARObject < ActiveRecord::Base
|
54
|
+
module MyModule1; end
|
55
|
+
module MyModule2; end
|
56
|
+
class MyClass;
|
57
|
+
module Module1; end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
```
|
61
|
+
You can make the field refer to one of these modules/classes like this:
|
62
|
+
```ruby
|
63
|
+
class MyARObject < ActiveRecord::Base
|
64
|
+
attribute :module_field,
|
65
|
+
:active_module,
|
66
|
+
possible_modules: [MyModule1, MyModule2, MyClass, MyClass::Module1]
|
67
|
+
end
|
68
|
+
```
|
69
|
+
And this is it! Easy!<br>
|
70
|
+
|
71
|
+
### Assigning and querying module attributes
|
72
|
+
Now you can use this attribute in many handy ways:
|
73
|
+
<br>
|
74
|
+
<br>
|
75
|
+
For instance, you may refer to it using module literals:
|
76
|
+
```ruby
|
77
|
+
MyARObject.create!(module_field: MyARObject::MyModule1)
|
78
|
+
|
79
|
+
MyARObject.where(module_field: MyARObject::MyModule1)
|
80
|
+
|
81
|
+
my_ar_object.module_field = MyARObject::MyModule1
|
82
|
+
|
83
|
+
my_ar_object.module_field #=> MyARObject::MyModule1:Module
|
84
|
+
|
85
|
+
```
|
86
|
+
But as typing fully qualified module names is not very ergonomic, you may also use symbols instead:
|
87
|
+
|
88
|
+
```ruby
|
89
|
+
MyARObject.create!(module_field: :MyClass)
|
90
|
+
|
91
|
+
MyARObject.where(module_field: :MyClass)
|
92
|
+
|
93
|
+
my_ar_object.module_field = :MyClass
|
94
|
+
|
95
|
+
my_ar_object.module_field #=> MyARObject::MyClass:Class
|
96
|
+
|
97
|
+
```
|
98
|
+
|
99
|
+
However, if there is the need for disambiguation, you can always use strings instead:
|
100
|
+
|
101
|
+
```ruby
|
102
|
+
|
103
|
+
MyARObject.create!(module_field: "MyClass::MyModule1")
|
104
|
+
|
105
|
+
MyARObject.where(module_field: "MyClass::MyModule1")
|
106
|
+
|
107
|
+
my_ar_object.module_field = "MyClass::MyModule1"
|
108
|
+
|
109
|
+
my_ar_object.module_field #=> MyARObject::MyClass::MyModule::Module
|
110
|
+
```
|
111
|
+
|
112
|
+
### Examples
|
113
|
+
|
114
|
+
#### Strategy Pattern (composition-based polymorphism)
|
115
|
+
|
116
|
+
[The Strategy design pattern](https://en.wikipedia.org/wiki/Strategy_pattern) allows composition based polymorphism. This enables runtime polymorphism (by changing the strategy in runtime),
|
117
|
+
and multiple-polymorphism (by composing an object of multiple strategies).
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
class MyARObject < ActiveRecord::Base
|
121
|
+
module Strategy1
|
122
|
+
def self.call
|
123
|
+
"strategy1 called"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
module Strategy2
|
128
|
+
def self.call
|
129
|
+
"strategy2 called"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
attribute :strategy,
|
134
|
+
:active_module,
|
135
|
+
possible_modules: [Strategy1, Strategy2]
|
136
|
+
|
137
|
+
def run_strategy!
|
138
|
+
# here we could pass arguments to the strategy, and if
|
139
|
+
# in this case strategies were classes we could also
|
140
|
+
# instantiate them
|
141
|
+
strategy.call
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
MyARObject.create!(module_field: :Strategy1).run_strategy! #=> "strategy1 called"
|
146
|
+
MyARObject.create!(module_field: :Strategy1).run_strategy! #=> "strategy2 called"
|
147
|
+
```
|
148
|
+
|
149
|
+
|
150
|
+
#### Rapid prototyping static domain objects
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
# Provider domain Object
|
154
|
+
module Provider
|
155
|
+
# As if the domain model class
|
156
|
+
def self.all
|
157
|
+
[Ebay, Amazon]
|
158
|
+
end
|
159
|
+
|
160
|
+
# As if the domain model instances
|
161
|
+
module Ebay
|
162
|
+
def self.do_something!
|
163
|
+
"do something with the ebay provider config"
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
module Amazon
|
168
|
+
def self.do_something!
|
169
|
+
"do something with the amazon provider config"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
class MyARObject < ActiveRecord::Base
|
175
|
+
attribute :provider,
|
176
|
+
:active_module,
|
177
|
+
possible_modules: Provider.all
|
178
|
+
end
|
179
|
+
|
180
|
+
MyARObject.create!(provider: :Ebay).provier.do_something!
|
181
|
+
#=> "do something with the ebay provider config"
|
182
|
+
MyARObject.create!(provider: Provider::Amazon).provider.do_something!
|
183
|
+
#=> "do something with the amazon provider config"
|
184
|
+
```
|
185
|
+
|
186
|
+
What is interesting about this is that we can later easily promote
|
187
|
+
our provider objects into full fledged ActiveRecord objects without
|
188
|
+
big changes to our code:
|
189
|
+
```ruby
|
190
|
+
class Provider < ActiveRecord::Base
|
191
|
+
def do_something!
|
192
|
+
#...
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
class MyARObject < ActiveRecord::Base
|
197
|
+
belongs_to :provider
|
198
|
+
end
|
199
|
+
```
|
200
|
+
|
201
|
+
Just in case you'd like to have shared code amongst the instances in the above example,
|
202
|
+
this is how you could do so:
|
203
|
+
|
204
|
+
```ruby
|
205
|
+
# Provider domain Object
|
206
|
+
module Provider
|
207
|
+
# As if the domain model class
|
208
|
+
module Base
|
209
|
+
def do_something!
|
210
|
+
"do something with #{something_from_an_instance}"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# As if the domain model instances
|
215
|
+
module Ebay
|
216
|
+
include Base
|
217
|
+
extend self
|
218
|
+
|
219
|
+
def something_from_an_instance
|
220
|
+
"the ebay provider config"
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
module Amazon
|
225
|
+
include Base
|
226
|
+
extend self
|
227
|
+
|
228
|
+
def something_from_an_instance
|
229
|
+
"the amazon provider config"
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
# As if the domain model class
|
234
|
+
def self.all
|
235
|
+
[Ebay, Amazon]
|
236
|
+
end
|
237
|
+
end
|
238
|
+
```
|
239
|
+
|
240
|
+
|
241
|
+
#### Static configuration management
|
242
|
+
|
243
|
+
This example is not much different than previous one. It however stresses that the module we
|
244
|
+
refer to might be used as a source of configuration parameters that change the behaviour of
|
245
|
+
the class it belongs to:
|
246
|
+
|
247
|
+
```ruby
|
248
|
+
# Provider domain Object
|
249
|
+
module ProviderConfig
|
250
|
+
module Ebay
|
251
|
+
module_function
|
252
|
+
|
253
|
+
def url= 'www.ebay.com'
|
254
|
+
def number_of_attempts= 5
|
255
|
+
end
|
256
|
+
|
257
|
+
module Amazon
|
258
|
+
module_function
|
259
|
+
|
260
|
+
def url= 'www.amazon.com'
|
261
|
+
def number_of_attempts= 10
|
262
|
+
end
|
263
|
+
|
264
|
+
def self.all
|
265
|
+
[Ebay, Amazon]
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
class MyARObject < ActiveRecord::Base
|
270
|
+
attribute :provider_config,
|
271
|
+
:active_module,
|
272
|
+
possible_modules: ProviderConfig.all
|
273
|
+
|
274
|
+
def load_page!
|
275
|
+
n_attempts = 0
|
276
|
+
result = nil
|
277
|
+
while n_attempts < provider.number_of_attempts
|
278
|
+
result = get_page(provider.url)
|
279
|
+
if(result)
|
280
|
+
return result
|
281
|
+
else
|
282
|
+
n_attempts.inc
|
283
|
+
end
|
284
|
+
end
|
285
|
+
result
|
286
|
+
end
|
287
|
+
end
|
288
|
+
|
289
|
+
MyARObject.create!(provider_config: :Ebay).load_page!
|
290
|
+
```
|
291
|
+
|
292
|
+
#### Rich Java/C#-like enums
|
293
|
+
This example is only to show the possibility.
|
294
|
+
This would probably benefit from using a meta programming abstraction
|
295
|
+
and there are already gems with this kind of functionality such as `enumerizable`
|
296
|
+
|
297
|
+
In a real world project, I guess it would rather make sense to extend `ActiveModule::Base` or even `ActiveModel::Type::Value`. But here it goes for the sake of example.
|
298
|
+
|
299
|
+
Java/C# enums allow defining methods on the enum, which are shared across all enum values:
|
300
|
+
|
301
|
+
```ruby
|
302
|
+
module PipelineStage
|
303
|
+
module Base
|
304
|
+
def external_provider_code
|
305
|
+
@external_provider_code ||= self.name.underscore
|
306
|
+
end
|
307
|
+
|
308
|
+
def database_representation
|
309
|
+
self.name
|
310
|
+
end
|
311
|
+
|
312
|
+
def frontend_representation
|
313
|
+
@frontend_representation ||= self.name.demodulize.upcase
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
module InitialContact
|
318
|
+
extend Base
|
319
|
+
end
|
320
|
+
|
321
|
+
module InNegotiations
|
322
|
+
extend Base
|
323
|
+
end
|
324
|
+
|
325
|
+
module LostDeal
|
326
|
+
extend Base
|
327
|
+
end
|
328
|
+
|
329
|
+
module PaidOut
|
330
|
+
extend Base
|
331
|
+
end
|
332
|
+
|
333
|
+
module_function
|
334
|
+
|
335
|
+
def all
|
336
|
+
[InitialContact, InNegotiations, LostDeal, PaidOut]
|
337
|
+
end
|
338
|
+
|
339
|
+
def cast(stage)
|
340
|
+
self.all.map(&:external_provider_code).find{|code| code == stage} ||
|
341
|
+
self.all.map(&:database_representation).find{|code| code == stage} ||
|
342
|
+
self.all.map(&:frontend_representation).find{|code| code == stage}
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
class MyARObject < ActiveRecord::Base
|
347
|
+
attribute :pipeline_stage,
|
348
|
+
:active_module,
|
349
|
+
possible_modules: PipelineStage.all
|
350
|
+
end
|
351
|
+
|
352
|
+
object = MyARObject.new(pipeline_stage: :InitialStage)
|
353
|
+
object.pipeline_stage&.frontend_representation #=> "INITIAL_STAGE"
|
354
|
+
object.pipeline_stage = :InNegotiations
|
355
|
+
object.pipeline_stage&.database_representation #=> "PipelineStage::InNegotiations"
|
356
|
+
```
|
357
|
+
|
358
|
+
|
359
|
+
## Development
|
360
|
+
|
361
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
362
|
+
|
363
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
364
|
+
|
365
|
+
## Contributing
|
366
|
+
|
367
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/pedrorolo/active_module.
|
data/Rakefile
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_model"
|
4
|
+
require_relative "modules_index"
|
5
|
+
|
6
|
+
module ActiveModule
|
7
|
+
class Base < ActiveModel::Type::Value
|
8
|
+
def initialize(possible_modules:)
|
9
|
+
@possible_modules = possible_modules
|
10
|
+
super()
|
11
|
+
end
|
12
|
+
|
13
|
+
def type
|
14
|
+
:active_module
|
15
|
+
end
|
16
|
+
|
17
|
+
def serializable?(object)
|
18
|
+
possible_module?(object)
|
19
|
+
end
|
20
|
+
|
21
|
+
def cast(value)
|
22
|
+
case value
|
23
|
+
when nil
|
24
|
+
nil
|
25
|
+
when ::Symbol
|
26
|
+
sym_to_module(value)
|
27
|
+
when ::Module
|
28
|
+
if possible_module?(value)
|
29
|
+
value
|
30
|
+
else
|
31
|
+
raise_invalid_module_value_error(value)
|
32
|
+
end
|
33
|
+
when ::String
|
34
|
+
str_to_module(value)
|
35
|
+
else
|
36
|
+
raise_invalid_module_value_error(value)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def serialize(module_instance)
|
41
|
+
module_instance && cast(module_instance).name
|
42
|
+
end
|
43
|
+
|
44
|
+
def deserialize(str)
|
45
|
+
str&.constantize
|
46
|
+
rescue NameError
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def sym_to_module(sym)
|
53
|
+
modules_index[sym] ||
|
54
|
+
raise_invalid_module_value_error(sym)
|
55
|
+
end
|
56
|
+
|
57
|
+
def str_to_module(str)
|
58
|
+
modules_index[str.to_sym] ||
|
59
|
+
raise_invalid_module_value_error(str)
|
60
|
+
end
|
61
|
+
|
62
|
+
def raise_invalid_module_value_error(str)
|
63
|
+
raise InvalidModuleValue.new(
|
64
|
+
str,
|
65
|
+
possible_modules: @possible_modules,
|
66
|
+
possible_symbols: modules_index.keys
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
def possible_modules_set
|
71
|
+
@possible_modules_set ||= Set.new(@possible_modules).freeze
|
72
|
+
end
|
73
|
+
|
74
|
+
def possible_module?(module_instance)
|
75
|
+
possible_modules_set.include?(module_instance)
|
76
|
+
end
|
77
|
+
|
78
|
+
def modules_index
|
79
|
+
@modules_index ||= ModulesIndex.new(@possible_modules)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveModule
|
4
|
+
class InvalidModuleValue < StandardError
|
5
|
+
def initialize(value,
|
6
|
+
possible_modules:,
|
7
|
+
possible_symbols:)
|
8
|
+
super(<<~ERROR_MESSAGE)
|
9
|
+
Invalid active_module value #{value.inspect}:
|
10
|
+
It must be one of these modules:
|
11
|
+
#{possible_modules.inspect}
|
12
|
+
|
13
|
+
Or one of their referring symbols
|
14
|
+
#{possible_symbols.inspect}
|
15
|
+
|
16
|
+
Or corresponding strings:
|
17
|
+
#{possible_symbols.map(&:to_s).inspect}
|
18
|
+
ERROR_MESSAGE
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveModule
|
4
|
+
module ModuleRefinement
|
5
|
+
refine ::Module do
|
6
|
+
def possible_names
|
7
|
+
name_parts = name.split("::")
|
8
|
+
[qualified_name].tap do |possible_names|
|
9
|
+
loop do
|
10
|
+
possible_names << name_parts.join("::").freeze
|
11
|
+
name_parts = name_parts.drop(1)
|
12
|
+
break if name_parts.empty?
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def qualified_name
|
18
|
+
"::#{name}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "module_refinement"
|
4
|
+
|
5
|
+
# Indexes modules by symbols of their qualified and unqualified names.
|
6
|
+
module ActiveModule
|
7
|
+
class ModulesIndex
|
8
|
+
using ModuleRefinement
|
9
|
+
|
10
|
+
delegate :[], to: :index
|
11
|
+
delegate :keys, to: :index
|
12
|
+
|
13
|
+
def initialize(modules)
|
14
|
+
@modules = modules
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def index
|
20
|
+
@index ||=
|
21
|
+
@modules.flat_map do |module_instance|
|
22
|
+
module_instance.possible_names.map do |name|
|
23
|
+
[name.to_sym, module_instance]
|
24
|
+
end
|
25
|
+
end.to_h.freeze
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveModule
|
4
|
+
module Register
|
5
|
+
module_function
|
6
|
+
|
7
|
+
def call(type_symbol = :active_module)
|
8
|
+
ActiveModel::Type.register(type_symbol, ActiveModule::Base)
|
9
|
+
ActiveRecord::Type.register(type_symbol, ActiveModule::Base)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "active_module/version"
|
4
|
+
require_relative "active_module/base"
|
5
|
+
require_relative "active_module/invalid_module_value"
|
6
|
+
require_relative "active_module/register"
|
7
|
+
|
8
|
+
module ActiveModule
|
9
|
+
module_function
|
10
|
+
|
11
|
+
def register!(type_symbol = :active_module)
|
12
|
+
Register.call(type_symbol)
|
13
|
+
end
|
14
|
+
end
|
metadata
ADDED
@@ -0,0 +1,90 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: active_module
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Pedro Rolo
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2024-12-16 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: activerecord
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "<"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '9'
|
20
|
+
- - ">="
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '7'
|
23
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - "<"
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '9'
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '7'
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: bundler
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: 1.15.0
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: 1.15.0
|
47
|
+
description: Module type for ActiveModel and Active Record
|
48
|
+
email:
|
49
|
+
- pedrorolo@gmail.com
|
50
|
+
executables: []
|
51
|
+
extensions: []
|
52
|
+
extra_rdoc_files: []
|
53
|
+
files:
|
54
|
+
- ".rspec"
|
55
|
+
- ".rubocop.yml"
|
56
|
+
- LICENSE
|
57
|
+
- README.md
|
58
|
+
- Rakefile
|
59
|
+
- lib/active_module.rb
|
60
|
+
- lib/active_module/base.rb
|
61
|
+
- lib/active_module/invalid_module_value.rb
|
62
|
+
- lib/active_module/module_refinement.rb
|
63
|
+
- lib/active_module/modules_index.rb
|
64
|
+
- lib/active_module/register.rb
|
65
|
+
- lib/active_module/version.rb
|
66
|
+
homepage: https://github.com/pedrorolo/active_module
|
67
|
+
licenses: []
|
68
|
+
metadata:
|
69
|
+
homepage_uri: https://github.com/pedrorolo/active_module
|
70
|
+
source_code_uri: https://github.com/pedrorolo/active_module
|
71
|
+
post_install_message:
|
72
|
+
rdoc_options: []
|
73
|
+
require_paths:
|
74
|
+
- lib
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: 3.0.0
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
81
|
+
requirements:
|
82
|
+
- - ">="
|
83
|
+
- !ruby/object:Gem::Version
|
84
|
+
version: '0'
|
85
|
+
requirements: []
|
86
|
+
rubygems_version: 3.5.7
|
87
|
+
signing_key:
|
88
|
+
specification_version: 4
|
89
|
+
summary: Module type for ActiveModel and Active Record
|
90
|
+
test_files: []
|