active_module 0.1.7 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 191f7c2deb3a07261247a4302436cc734541ab62c3db9dd742974d58320dd763
4
- data.tar.gz: bd20cb4d1aa7b6437facfaefc8213485571497df5e9e607ce19d16b81db246b8
3
+ metadata.gz: 743570de05b5e510d10408fe3b95eaab2789e4432311538bda978bf52d893355
4
+ data.tar.gz: 72057e516212b80260b45fe9b2bd5c97530fc5f6ceaab42c0578d48581bf3b50
5
5
  SHA512:
6
- metadata.gz: bc48c3f11e8f71321ab4abece1b5d58f671c96de7e494a5f2f7a8e55b9946c6e88af9e61970bdd2332eba12ee74dadec13485555dff0f05a4f74209c1327fd96
7
- data.tar.gz: 126c9c4a2d885cadbe472dc23caa5025415f2806411e9eecce37a5b40c28ce990c5956246646292e9780b6f30d6f77705ab681899a7532940dbe0cfe59f2a471
6
+ metadata.gz: 7c4ee2bc7ae24fb246f9744f161088222af6c02d85c99d62e39f196257b90d24abcea21de0217ea578d8755acfd61bf4d078d9f7290f3cc0cc28110bca893cd9
7
+ data.tar.gz: '02582a7cbe08a616e35828e0537bacf41714cd4cfae0d5272188fb9feb8aa970c44670391b1cef6926781ebe53f28ce966a0eeda46d32e2149be22370cf19daf'
data/README.md CHANGED
@@ -1,21 +1,23 @@
1
- # ActiveModule
2
1
 
3
- *"Let's turn modules and classes into first-class active record values!"*
2
+ # active_module
3
+ ![alt text](https://img.shields.io/badge/coverage-100%25-brightgreen)
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` (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.
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
- - Strategy Pattern (composition-based polymorphism)
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 :](#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
- And compare them like this (optional):
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 =~ :Module1
45
- object.module_field =~ "Module1"
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.1"
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 structure:
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
- ### Strategy Pattern (composition-based polymorphism)
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 :strategy, :active_module, possible_modules: StrategySuperclass.subclasses
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.new(some_args).call(other_args)
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, you can use modules instead:
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
 
@@ -5,11 +5,35 @@ require_relative "modules_index"
5
5
 
6
6
  module ActiveModule
7
7
  class Base < ActiveModel::Type::Value
8
- def initialize(possible_modules:)
9
- @possible_modules = possible_modules
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
- module_instance && cast(module_instance).name
65
+ mapping[cast(module_instance)]
42
66
  end
43
67
 
44
68
  def deserialize(str)
45
- str&.constantize
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveModule
4
- VERSION = "0.1.7"
4
+ VERSION = "0.2.0"
5
5
  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.1.7
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-18 00:00:00.000000000 Z
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: Let's turn modules and classes into first-class active record values!
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: ActiveModel/ActiveRecord module attribute type implementation
92
+ summary: Modules and Classes as first-class active record values
93
93
  test_files: []