active_module 0.1.7 → 0.2.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 +4 -4
- data/README.md +83 -22
- data/lib/active_module/base.rb +32 -6
- data/lib/active_module/version.rb +1 -1
- metadata +6 -6
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 743570de05b5e510d10408fe3b95eaab2789e4432311538bda978bf52d893355
|
|
4
|
+
data.tar.gz: 72057e516212b80260b45fe9b2bd5c97530fc5f6ceaab42c0578d48581bf3b50
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7c4ee2bc7ae24fb246f9744f161088222af6c02d85c99d62e39f196257b90d24abcea21de0217ea578d8755acfd61bf4d078d9f7290f3cc0cc28110bca893cd9
|
|
7
|
+
data.tar.gz: '02582a7cbe08a616e35828e0537bacf41714cd4cfae0d5272188fb9feb8aa970c44670391b1cef6926781ebe53f28ce966a0eeda46d32e2149be22370cf19daf'
|
data/README.md
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
# ActiveModule
|
|
2
1
|
|
|
3
|
-
|
|
2
|
+
# active_module
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
#### *Modules and Classes as first-class active record values!*
|
|
4
6
|
|
|
5
7
|
ActiveModel/ActiveRecord implementation of the Module attribute type.
|
|
6
8
|
|
|
7
|
-
- Allows storing a reference to a `Module` or `Class`
|
|
8
|
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
9
|
+
- Allows storing a reference to a `Module` or `Class` in a `:string` database field
|
|
10
|
+
- Automatically casts strings and symbols into modules when creating and querying objects
|
|
11
|
+
- Symbols or strings refer to the modules using unqualified names
|
|
12
|
+
- It is safe and efficient
|
|
11
13
|
|
|
12
14
|
This is a very generic mechanism that enables many possible utilizations, for instance:
|
|
13
|
-
-
|
|
14
|
-
- Rapid prototyping static domain objects
|
|
15
|
-
- Static configuration management
|
|
16
|
-
- Rich Java/C#-like enums
|
|
15
|
+
- **Composition-based polymorphism (Strategy design pattern)**
|
|
16
|
+
- **Rapid prototyping static domain objects**
|
|
17
|
+
- **Static configuration management**
|
|
18
|
+
- **Rich Java/C#-like enums**
|
|
17
19
|
|
|
18
|
-
You can find examples of these in [Usage -> Examples
|
|
20
|
+
You can find examples of these in [Usage -> Examples](#Examples).
|
|
19
21
|
|
|
20
22
|
## TL;DR
|
|
21
23
|
|
|
@@ -36,13 +38,24 @@ object.module_field = "Module"
|
|
|
36
38
|
object.module_field #=> Nested::Module:Module
|
|
37
39
|
```
|
|
38
40
|
|
|
39
|
-
|
|
41
|
+
Query them like this:
|
|
42
|
+
```ruby
|
|
43
|
+
MyARObject.where(module_field: Nested::Module)
|
|
44
|
+
MyARObject.where(module_field: :Module)
|
|
45
|
+
MyARObject.where(module_field: "Module")
|
|
46
|
+
object.module_field #=> Nested::Module:Module
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
And compare them like this:
|
|
50
|
+
|
|
40
51
|
```ruby
|
|
52
|
+
object.module_field == Nested::Module
|
|
53
|
+
|
|
41
54
|
module MyNameSpace
|
|
42
55
|
using ActiveModule::Comparison
|
|
43
56
|
|
|
44
|
-
object.module_field =~ :
|
|
45
|
-
object.module_field =~ "
|
|
57
|
+
object.module_field =~ :Module
|
|
58
|
+
object.module_field =~ "Module"
|
|
46
59
|
end
|
|
47
60
|
```
|
|
48
61
|
|
|
@@ -51,7 +64,7 @@ end
|
|
|
51
64
|
Add to your gemfile - and if you are using rails - that's all you need:
|
|
52
65
|
|
|
53
66
|
```ruby
|
|
54
|
-
gem 'active_module', "~>0.
|
|
67
|
+
gem 'active_module', "~> 0.2"
|
|
55
68
|
```
|
|
56
69
|
|
|
57
70
|
If you are not using rails, just issue this command after loading active record
|
|
@@ -73,11 +86,11 @@ ActiveRecord::Type.register(:active_module, ActiveModule::Base)
|
|
|
73
86
|
Add a string field to the table you want to hold a module attribute in your migrations
|
|
74
87
|
```ruby
|
|
75
88
|
create_table :my_ar_objects do |t|
|
|
76
|
-
t.string :module_field
|
|
89
|
+
t.string :module_field, index: true
|
|
77
90
|
end
|
|
78
91
|
```
|
|
79
92
|
|
|
80
|
-
Now given this module
|
|
93
|
+
Now given this random module hierarchy:
|
|
81
94
|
```ruby
|
|
82
95
|
class MyARObject < ActiveRecord::Base
|
|
83
96
|
module MyModule1; end
|
|
@@ -95,6 +108,16 @@ class MyARObject < ActiveRecord::Base
|
|
|
95
108
|
possible_modules: [MyModule1, MyModule2, MyClass, MyClass::MyModule1]
|
|
96
109
|
end
|
|
97
110
|
```
|
|
111
|
+
|
|
112
|
+
Optionally, you can specify how to map your modules into the database
|
|
113
|
+
(the default is the module's fully qualified name):
|
|
114
|
+
```ruby
|
|
115
|
+
attribute :module_field,
|
|
116
|
+
:active_module,
|
|
117
|
+
possible_modules: [MyModule1, MyModule2, MyClass, MyClass::MyModule1]
|
|
118
|
+
mapping: {MyModule1 => "this is the db representation of module1"}
|
|
119
|
+
```
|
|
120
|
+
|
|
98
121
|
And this is it! Easy!<br>
|
|
99
122
|
|
|
100
123
|
### Assigning and querying module attributes
|
|
@@ -169,23 +192,28 @@ my_ar_object.module_field == MyClass::MyModule1
|
|
|
169
192
|
|
|
170
193
|
## Examples
|
|
171
194
|
|
|
172
|
-
###
|
|
195
|
+
### Composition-based polymorphism (Strategy design pattern)
|
|
173
196
|
|
|
174
197
|
[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),
|
|
175
198
|
and multiple-polymorphism (by composing an object of multiple strategies).
|
|
176
199
|
|
|
177
|
-
If you want to use classes this will do:
|
|
200
|
+
If you want to use classes this will do:
|
|
178
201
|
```ruby
|
|
179
202
|
class MyARObject < ActiveRecord::Base
|
|
180
|
-
attribute :
|
|
203
|
+
attribute :strategy_class, :active_module, possible_modules: StrategySuperclass.subclasses
|
|
204
|
+
|
|
205
|
+
def strategy
|
|
206
|
+
@strategy ||= strategy_class.new(some_args_from_the_instance)
|
|
207
|
+
end
|
|
181
208
|
|
|
182
209
|
def run_strategy!(args)
|
|
183
|
-
strategy.
|
|
210
|
+
strategy.call(args)
|
|
184
211
|
end
|
|
185
212
|
end
|
|
186
213
|
```
|
|
187
214
|
|
|
188
|
-
But if you are not in the mood to define a class hierarchy for it
|
|
215
|
+
But if you are not in the mood to define a class hierarchy for it (or if you are performance-savy),
|
|
216
|
+
you may use modules instead:
|
|
189
217
|
|
|
190
218
|
```ruby
|
|
191
219
|
class MyARObject < ActiveRecord::Base
|
|
@@ -214,6 +242,39 @@ MyARObject.create!(module_field: :Strategy1).run_strategy! #=> "strategy1 called
|
|
|
214
242
|
MyARObject.create!(module_field: :Strategy2).run_strategy! #=> "strategy2 called"
|
|
215
243
|
```
|
|
216
244
|
|
|
245
|
+
You can later easily promote these modules to classes if you need instance variables:
|
|
246
|
+
|
|
247
|
+
```ruby
|
|
248
|
+
class MyARObject < ActiveRecord::Base
|
|
249
|
+
class Strategy1
|
|
250
|
+
def self.call
|
|
251
|
+
self.new.call
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def call
|
|
255
|
+
"strategy1 called"
|
|
256
|
+
end
|
|
257
|
+
end
|
|
258
|
+
|
|
259
|
+
module Strategy2
|
|
260
|
+
def self.call
|
|
261
|
+
"strategy2 called"
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
attribute :strategy,
|
|
266
|
+
:active_module,
|
|
267
|
+
possible_modules: [Strategy1, Strategy2]
|
|
268
|
+
|
|
269
|
+
def run_strategy!(some_args)
|
|
270
|
+
strategy.call(some_args, other_args)
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
MyARObject.create!(module_field: :Strategy1).run_strategy! #=> "strategy1 called"
|
|
275
|
+
MyARObject.create!(module_field: :Strategy2).run_strategy! #=> "strategy2 called"
|
|
276
|
+
```
|
|
277
|
+
|
|
217
278
|
|
|
218
279
|
### Rapid prototyping static domain objects
|
|
219
280
|
|
data/lib/active_module/base.rb
CHANGED
|
@@ -5,11 +5,35 @@ require_relative "modules_index"
|
|
|
5
5
|
|
|
6
6
|
module ActiveModule
|
|
7
7
|
class Base < ActiveModel::Type::Value
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
attr_reader :possible_modules, :mapping
|
|
9
|
+
|
|
10
|
+
def initialize(possible_modules_or_mapping = nil,
|
|
11
|
+
possible_modules: [],
|
|
12
|
+
mapping: {})
|
|
13
|
+
@possible_modules =
|
|
14
|
+
if possible_modules_or_mapping.is_a?(Array)
|
|
15
|
+
possible_modules_or_mapping + possible_modules
|
|
16
|
+
else
|
|
17
|
+
possible_modules
|
|
18
|
+
end
|
|
19
|
+
mapping_arg = if possible_modules_or_mapping.is_a?(Hash)
|
|
20
|
+
possible_modules_or_mapping.merge(mapping)
|
|
21
|
+
else
|
|
22
|
+
mapping
|
|
23
|
+
end
|
|
24
|
+
@mapping =
|
|
25
|
+
@possible_modules.each_with_object(mapping_arg.dup) do |mod, result|
|
|
26
|
+
result[mod] ||= mod.name
|
|
27
|
+
end
|
|
10
28
|
super()
|
|
11
29
|
end
|
|
12
30
|
|
|
31
|
+
def ==(other)
|
|
32
|
+
other.is_a?(Base) &&
|
|
33
|
+
possible_modules == other.possible_modules &&
|
|
34
|
+
mapping == other.mapping
|
|
35
|
+
end
|
|
36
|
+
|
|
13
37
|
def type
|
|
14
38
|
:active_module
|
|
15
39
|
end
|
|
@@ -38,13 +62,11 @@ module ActiveModule
|
|
|
38
62
|
end
|
|
39
63
|
|
|
40
64
|
def serialize(module_instance)
|
|
41
|
-
|
|
65
|
+
mapping[cast(module_instance)]
|
|
42
66
|
end
|
|
43
67
|
|
|
44
68
|
def deserialize(str)
|
|
45
|
-
str
|
|
46
|
-
rescue NameError
|
|
47
|
-
nil
|
|
69
|
+
from_db[str]
|
|
48
70
|
end
|
|
49
71
|
|
|
50
72
|
private
|
|
@@ -78,5 +100,9 @@ module ActiveModule
|
|
|
78
100
|
def modules_index
|
|
79
101
|
@modules_index ||= ModulesIndex.new(@possible_modules)
|
|
80
102
|
end
|
|
103
|
+
|
|
104
|
+
def from_db
|
|
105
|
+
@from_db ||= mapping.invert
|
|
106
|
+
end
|
|
81
107
|
end
|
|
82
108
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: active_module
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Pedro Rolo
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2024-12-
|
|
11
|
+
date: 2024-12-22 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
@@ -19,7 +19,7 @@ dependencies:
|
|
|
19
19
|
version: '9'
|
|
20
20
|
- - ">="
|
|
21
21
|
- !ruby/object:Gem::Version
|
|
22
|
-
version: '7'
|
|
22
|
+
version: '7.1'
|
|
23
23
|
type: :runtime
|
|
24
24
|
prerelease: false
|
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -29,7 +29,7 @@ dependencies:
|
|
|
29
29
|
version: '9'
|
|
30
30
|
- - ">="
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '7'
|
|
32
|
+
version: '7.1'
|
|
33
33
|
- !ruby/object:Gem::Dependency
|
|
34
34
|
name: bundler
|
|
35
35
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -44,7 +44,7 @@ dependencies:
|
|
|
44
44
|
- - ">="
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
46
|
version: 1.15.0
|
|
47
|
-
description:
|
|
47
|
+
description: ActiveModel/ActiveRecord module attribute type implementation
|
|
48
48
|
email:
|
|
49
49
|
- pedrorolo@gmail.com
|
|
50
50
|
executables: []
|
|
@@ -89,5 +89,5 @@ requirements: []
|
|
|
89
89
|
rubygems_version: 3.5.7
|
|
90
90
|
signing_key:
|
|
91
91
|
specification_version: 4
|
|
92
|
-
summary:
|
|
92
|
+
summary: Modules and Classes as first-class active record values
|
|
93
93
|
test_files: []
|