enum_ext 0.8.0 → 1.0.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/CHANGELOG.md +14 -0
- data/Gemfile_rails_6.lock +2 -1
- data/README.md +66 -47
- data/lib/enum_ext/annotated.rb +14 -5
- data/lib/enum_ext/basic_helpers.rb +18 -11
- data/lib/enum_ext/config.rb +20 -0
- data/lib/enum_ext/enum_wrapper.rb +18 -2
- data/lib/enum_ext/humanize_helpers.rb +7 -9
- data/lib/enum_ext/superset_helpers.rb +56 -47
- data/lib/enum_ext/version.rb +1 -1
- data/lib/enum_ext.rb +13 -25
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d9693b1516f925bdb67d488f84e3ac2409d9df9d56643a729c2523910664ce81
|
4
|
+
data.tar.gz: c1a4d7210af6cdfefaaf136f3098a1517e66f4eb86af26488e5d9cb7b76047d6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a46470caf20bfacb1ddc688d1c499a14c7f962acf091bd3cf744b98bbc2f7fe2ca5625d2c911d2942c9923ca998e66a869be9a3c0af64995f6ceacb391ccd634
|
7
|
+
data.tar.gz: 01dedd811498199a184e0297b67bc6c2631138c14a9835d127046b83e8a2759c96a0aa1171e01977a3157a4fa42cbc2c7fb6c5bea2fe03379198d086ba6888ed
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
# 1.0.0.rc
|
2
|
+
* global configuration added
|
3
|
+
|
4
|
+
# 0.9.1
|
5
|
+
* fix https://github.com/alekseyl/enum_ext/issues/53
|
6
|
+
|
7
|
+
# 0.9
|
8
|
+
* supersets works fine and same way with prefixes and suffixes as usual enum does
|
9
|
+
|
10
|
+
# 0.8.1
|
11
|
+
* Fixes issue https://github.com/alekseyl/enum_ext/issues/50
|
12
|
+
* Better describe for supersets.
|
13
|
+
* Multiple readme and comments fixes
|
14
|
+
|
1
15
|
# 0.8.0
|
2
16
|
* Methods annotations added:
|
3
17
|
* Full descriptions: Class.enum.describe(short=true) / Class.enum.describe_short / Class.enum.describe_long / Class.enum.describe_basic
|
data/Gemfile_rails_6.lock
CHANGED
data/README.md
CHANGED
@@ -7,7 +7,7 @@ EnumExt extends rails enum with localization/translation and it's helpers, mass-
|
|
7
7
|
Add this line to your application's Gemfile:
|
8
8
|
|
9
9
|
```ruby
|
10
|
-
gem 'enum_ext', '~> 0.
|
10
|
+
gem 'enum_ext', '~> 0.8'
|
11
11
|
```
|
12
12
|
|
13
13
|
And then execute:
|
@@ -26,12 +26,12 @@ Or install it yourself as:
|
|
26
26
|
|
27
27
|
class SomeModel
|
28
28
|
extend EnumExt
|
29
|
-
|
30
|
-
enum
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
29
|
+
|
30
|
+
enum status: {}, ext: [:enum_i, :enum_mass_assign, enum_supersets: { superset: [:basic_enum] } ]
|
31
|
+
humanize_enum #...
|
32
|
+
translate_enum #...
|
33
|
+
ext_enum_sets # if needed
|
34
|
+
end
|
35
35
|
```
|
36
36
|
|
37
37
|
Let's assume that we have model Request representing some buying requests with enum **status**, and we have model Order with requests,
|
@@ -56,16 +56,16 @@ This is a preferable way now, and old helpers are private starting version 0.6!:
|
|
56
56
|
|
57
57
|
```ruby
|
58
58
|
#Instead of three method calls:
|
59
|
-
enum
|
60
|
-
enum_i :
|
61
|
-
enum_mass_assign :
|
59
|
+
enum status: {}
|
60
|
+
enum_i :status # will raise an private method error starting ver 0.6
|
61
|
+
enum_mass_assign :status # will raise an private method error starting ver 0.6!!!
|
62
62
|
|
63
63
|
#You should go with ext option instead:
|
64
|
-
enum
|
64
|
+
enum status: {}, ext: [:enum_i, :enum_mass_assign]
|
65
65
|
|
66
66
|
# OR in case of standalone enum definition:
|
67
|
-
enum
|
68
|
-
enum_ext :
|
67
|
+
enum status: {} # somewhere where you can't or don't want to reach
|
68
|
+
enum_ext :status, [:enum_i, :enum_mass_assign, enum_supersets: {} ]
|
69
69
|
```
|
70
70
|
Rem: enum_ext could be called multiple times and merge later definitions, though I can't imagine why would you split it to multiple calls.
|
71
71
|
|
@@ -75,10 +75,10 @@ Rem: The only exceptions for the new syntax are full translation/humanization he
|
|
75
75
|
|
76
76
|
```ruby
|
77
77
|
# GOOD:
|
78
|
-
enum_ext :
|
78
|
+
enum_ext :status, [:enum_i, :enum_mass_assign, :translate_enum, enum_supersets: {}]
|
79
79
|
|
80
80
|
# BAD (even if correctly defines internationalization):
|
81
|
-
enum_ext :
|
81
|
+
enum_ext :status, [:enum_i, :enum_mass_assign, :translate_enum, enum_supersets: {}] do
|
82
82
|
I18n.t("scope.#{kind}")
|
83
83
|
end
|
84
84
|
```
|
@@ -87,7 +87,7 @@ Rem: The only exceptions for the new syntax are full translation/humanization he
|
|
87
87
|
|
88
88
|
### Humanization (humanize_enum)
|
89
89
|
|
90
|
-
|
90
|
+
If app doesn't need internationalization, it may use humanize_enum to make enum output user friendly
|
91
91
|
|
92
92
|
```ruby
|
93
93
|
humanize_enum :status, {
|
@@ -103,14 +103,13 @@ Rem: The only exceptions for the new syntax are full translation/humanization he
|
|
103
103
|
```
|
104
104
|
|
105
105
|
This humanize_enum adds to instance:
|
106
|
-
-
|
106
|
+
- t_status
|
107
107
|
|
108
|
-
and to
|
109
|
-
-
|
110
|
-
-
|
111
|
-
|
108
|
+
and to enum object:
|
109
|
+
- statuses.t_options - translated enum values options for select input
|
110
|
+
- statuses.t_options_i - same as above but use int values with translations works for ActiveAdmin filters for instance
|
111
|
+
|
112
112
|
|
113
|
-
|
114
113
|
Example with block:
|
115
114
|
|
116
115
|
```ruby
|
@@ -122,12 +121,12 @@ Rem: The only exceptions for the new syntax are full translation/humanization he
|
|
122
121
|
Example for select:
|
123
122
|
|
124
123
|
```ruby
|
125
|
-
f.select :status, Request.
|
124
|
+
f.select :status, Request.statuses.t_options
|
126
125
|
```
|
127
126
|
|
128
127
|
in Active Admin filters
|
129
128
|
```ruby
|
130
|
-
filter :status, as: :select, label: 'Status', collection: Request.
|
129
|
+
filter :status, as: :select, label: 'Status', collection: Request.statuses.t_options_i
|
131
130
|
```
|
132
131
|
|
133
132
|
**Rem:** select options may break when using lambda() or proc with instance method, but will survive with block
|
@@ -138,7 +137,7 @@ Rem: The only exceptions for the new syntax are full translation/humanization he
|
|
138
137
|
request.paid!
|
139
138
|
request.status # >> paid
|
140
139
|
request.t_status # >> "paid 3 dollars"
|
141
|
-
Request.
|
140
|
+
Request.statuses.localizations # >> { in_cart: -> { I18n.t("request.status.in_cart") }, .... }
|
142
141
|
```
|
143
142
|
|
144
143
|
Could be called multiple times, all humanization definitions will be merged under the hood.
|
@@ -158,6 +157,8 @@ Or it can be done with block either with translate or humanize:
|
|
158
157
|
end
|
159
158
|
```
|
160
159
|
|
160
|
+
All humanization examples also should work same way as expected.
|
161
|
+
|
161
162
|
## Enum extended functionality
|
162
163
|
|
163
164
|
### Enum to_i shortcut ( enum_i )
|
@@ -172,6 +173,8 @@ Defines method enum_name_i shortcut for Model.enum_names[elem.enum_name] or enum
|
|
172
173
|
request.paid_i # 10
|
173
174
|
```
|
174
175
|
|
176
|
+
Rem: whenever underlying enum is not an integer will refuse to define helper and outputs WARNING
|
177
|
+
|
175
178
|
### Enum SuperSets (enum_supersets)
|
176
179
|
|
177
180
|
**Use-case** whenever you need superset of enums to behave like a super enum.
|
@@ -185,16 +188,16 @@ Defines method enum_name_i shortcut for Model.enum_names[elem.enum_name] or enum
|
|
185
188
|
```ruby
|
186
189
|
enum status: [:in_cart, :waiting_for_payment, :paid, :packing, :ready_for_shipment, :on_delivery, :delivered],
|
187
190
|
ext: [enum_supersets: {
|
188
|
-
|
191
|
+
around_delivery: [:ready_for_shipment, :on_delivery], # for shipping department for example
|
189
192
|
in_warehouse: [:packing, :ready_for_shipment], # this scope is just for superposition example below
|
190
|
-
sold: [:paid, :
|
193
|
+
sold: [:paid, :around_delivery, :in_warehouse, :delivered] # also you can define any superposition of already defined supersets or enum values
|
191
194
|
}]
|
192
195
|
|
193
196
|
# supersets will be stored inside enum wrapper object, and can be de-referenced to basic enum values
|
194
197
|
# using wrapper defined methods: "superset_enum_plural", i.e. statuses.sold_statuses -> [:paid, :packing, :ready_for_shipment, :on_delivery, :delivered]
|
195
198
|
# so new supersets could be defined using Array operations against newly defined methods
|
196
199
|
enum_ext :status, enum_supersets: {
|
197
|
-
outside_warehouse: ( statuses.
|
200
|
+
outside_warehouse: ( statuses.around_delivery - statuses.in_warehouse ) #... any other array operations like &, + and so can be used
|
198
201
|
}
|
199
202
|
```
|
200
203
|
|
@@ -202,39 +205,38 @@ it will generate:
|
|
202
205
|
|
203
206
|
```
|
204
207
|
instance:
|
205
|
-
- methods:
|
208
|
+
- methods: around_delivery?, in_warehouse?
|
206
209
|
|
207
210
|
class:
|
208
|
-
- named scopes:
|
209
|
-
- parametrized scopes: with_statuses, without_statuses ( available as a standalone extension now, and will not be included by default in a versionafter 0.5.0)
|
211
|
+
- named scopes: around_delivery, in_warehouse
|
210
212
|
|
211
213
|
enum methods:
|
212
214
|
- Class.statuses.supersets -- will output superset definition hash
|
213
215
|
- Class.statuses.supersets_raw -- will output superset decompositions to basic enum types hash
|
214
216
|
|
215
|
-
- Class.statuses.
|
216
|
-
-
|
217
|
+
- Class.statuses.around_delivery (=[:ready_for_shipment, :on_delivery, :delivered] ), in_warehouse_statuses
|
218
|
+
- around_delivery_statuses_i (= [3,4,5]), in_warehouse_statuses_i (=[3])
|
217
219
|
|
218
|
-
translation helpers ( started with t_... ):
|
219
|
-
- Class.statuses.
|
220
|
-
- Class.statuses.
|
220
|
+
translation helpers grouped for superset ( started with t_... ):
|
221
|
+
- Class.statuses.t_around_delivery_options (= [['translation or humanization', :ready_for_shipment] ...] ) for select inputs purposes
|
222
|
+
- Class.statuses.t_around_delivery_options_i (= [['translation or humanization', 3] ...]) same as above but with integer as value ( for example to use in Active admin filters )
|
221
223
|
```
|
222
224
|
|
223
225
|
```ruby
|
224
226
|
request.on_delivery!
|
225
|
-
request.
|
227
|
+
request.around_delivery? # >> true
|
226
228
|
|
227
|
-
Request.
|
229
|
+
Request.around_delivery.exists?(request) # >> true
|
228
230
|
Request.in_warehouse.exists?(request) # >> false
|
229
231
|
|
230
|
-
Request.
|
232
|
+
Request.statuses.around_delivery # >> ["ready_for_shipment", "on_delivery", "delivered"]
|
231
233
|
```
|
232
234
|
Rem:
|
233
235
|
supersets creation could be called multiple times defining a superposition of already defined sets ( considering previous example ):
|
234
236
|
|
235
237
|
```ruby
|
236
238
|
enum_ext :status, enum_supersets: {
|
237
|
-
outside_wharehouse: (
|
239
|
+
outside_wharehouse: ( statuses.around_delivery_superset - statuses.in_warehouse_superset )#... any other array operations like &, + and so can be used
|
238
240
|
}
|
239
241
|
```
|
240
242
|
|
@@ -242,8 +244,8 @@ Rem: you can refer previously defined set as usual kind in the same method call:
|
|
242
244
|
|
243
245
|
```ruby
|
244
246
|
enum_ext :status, enum_supersets: {
|
245
|
-
|
246
|
-
not_in_cart: [:paid, :
|
247
|
+
around_delivery: [:ready_for_shipment, :on_delivery, :delivered],
|
248
|
+
not_in_cart: [:paid, :around_delivery] #
|
247
249
|
}
|
248
250
|
```
|
249
251
|
|
@@ -254,7 +256,7 @@ Rem: you can refer previously defined set as usual kind in the same method call:
|
|
254
256
|
ext: [:multi_enum_scopes]
|
255
257
|
|
256
258
|
# some place else:
|
257
|
-
Request.with_statuses( :payed, :
|
259
|
+
Request.with_statuses( :payed, :around_delivery ) # >> status IN (:payed, :ready_for_shipment, :on_delivery, :delivered)
|
258
260
|
Request.without_statuses( :payed, :in_warehouse ) # >> status NOT IN (:payed, :ready_for_shipment)
|
259
261
|
```
|
260
262
|
|
@@ -264,7 +266,7 @@ Rem: you can refer previously defined set as usual kind in the same method call:
|
|
264
266
|
|
265
267
|
**Use-case:** it's often case when I need bulk update without callbacks, so it's gets frustrating to repeat:
|
266
268
|
```
|
267
|
-
some_scope.update_all(status:
|
269
|
+
some_scope.update_all(status: :new_status, update_at: Time.now)
|
268
270
|
```
|
269
271
|
If you need callbacks you can do like this: some_scope.each(&:new_stat!) but if you don't need callbacks and you
|
270
272
|
have hundreds and thousands of records to change at once you better call update_all
|
@@ -318,20 +320,37 @@ And per extension methods (describe_enum_i, e.t.c)
|
|
318
320
|
```
|
319
321
|

|
320
322
|
|
323
|
+
## Configuration
|
324
|
+
```ruby
|
325
|
+
EnumExt.configure do |config|
|
326
|
+
# the name of class which will have EnumExt extended globally
|
327
|
+
# default: nil
|
328
|
+
config.application_record_class = ApplicationRecord
|
329
|
+
# default helpers, will be added to every enum ext definitions
|
330
|
+
# default value: [:multi_enum_scopes, :mass_assign_enum]
|
331
|
+
config.default_helpers = [:multi_enum_scopes, :mass_assign_enum]
|
332
|
+
end
|
333
|
+
|
334
|
+
```
|
335
|
+
|
336
|
+
|
321
337
|
## Tests
|
322
338
|
Starting version 0.6 added support for rails 7+ enum definitions, that's making testing a little bit not that easy as running `rake test`.
|
323
339
|
Now testings are done via `docker-compose up`. Look closer to Dockerfiles and `docker-compose.yml`
|
324
340
|
to get the idea how they working simultaneously without interference with each other.
|
325
341
|
|
342
|
+
Rem. I'm always freaking googling everytime I need to run single test so I'll just keep here:
|
343
|
+
|
344
|
+
```ruby
|
345
|
+
rake test TEST=test/test_enum_ext.rb TESTOPTS="--name=/bb/"
|
346
|
+
```
|
326
347
|
|
327
348
|
## Development
|
328
349
|
|
329
350
|
## TODO
|
330
|
-
[]
|
331
|
-
[] describe method to observe current extension
|
351
|
+
[] add global config and allow global extension
|
332
352
|
|
333
353
|
## Contributing
|
334
|
-
|
335
354
|
Bug reports and pull requests are welcome on GitHub at https://github.com/alekseyl/enum_ext or by email: leshchuk@gmail.com
|
336
355
|
|
337
356
|
|
data/lib/enum_ext/annotated.rb
CHANGED
@@ -2,15 +2,15 @@
|
|
2
2
|
# but have no idea how to do this in a super-neat way, so it a little bit chaotic and experimental
|
3
3
|
module EnumExt::Annotated
|
4
4
|
|
5
|
-
# call it to see what's your enum current
|
5
|
+
# call it to see what's your enum current options are
|
6
6
|
def describe_basic
|
7
7
|
puts yellow( "Basic #{enum_name} definition: \n" )
|
8
8
|
print_hash(enum_values)
|
9
9
|
end
|
10
10
|
|
11
|
-
# call it to see
|
11
|
+
# call it to see which enum extensions are defined.
|
12
12
|
def describe_long
|
13
|
-
puts yellow( "\nEnumExt extensions:" )
|
13
|
+
puts yellow( "\n\nEnumExt extensions:" )
|
14
14
|
|
15
15
|
puts [
|
16
16
|
describe_enum_i(false),
|
@@ -80,10 +80,19 @@ module EnumExt::Annotated
|
|
80
80
|
description = if enabled_features[:supersets].blank?
|
81
81
|
red( "\nSupersets not used!\n" )
|
82
82
|
else
|
83
|
+
superset_method = enabled_features[:supersets].keys.first
|
83
84
|
red( "\nSupersets definitions:\n" ) << inspect_hash(enabled_features[:supersets]) << <<~SUPERSETS
|
85
|
+
|
84
86
|
# Instance methods added: #{enabled_features[:supersets].keys.join("?, ")}?
|
85
|
-
|
86
|
-
#
|
87
|
+
# Usage:
|
88
|
+
#{black("instance")}.#{cyan(superset_method)}?
|
89
|
+
# Will be equal true if any of: #{supersets_raw[superset_method].join("?, ")}? is true
|
90
|
+
|
91
|
+
# Class level methods/scopes added: #{enabled_features[:supersets].keys.join(", ")}
|
92
|
+
# Usage:
|
93
|
+
#{black(base_class.to_s)}.#{cyan(superset_method)}
|
94
|
+
# Will be getting all instances with #{enum_name} equals to any of: #{supersets_raw[superset_method].join(", ")}
|
95
|
+
|
87
96
|
SUPERSETS
|
88
97
|
end
|
89
98
|
|
@@ -1,18 +1,26 @@
|
|
1
1
|
module EnumExt::BasicHelpers
|
2
2
|
|
3
|
+
private
|
3
4
|
# Defines instance method a shortcut for getting integer value of an enum.
|
4
5
|
# for enum named 'status' will generate:
|
5
6
|
#
|
6
7
|
# instance.status_i
|
7
|
-
|
8
|
+
#
|
9
|
+
# Rem. Will not define helper when enum values are strings, and will print warning
|
8
10
|
def enum_i( enum_name )
|
11
|
+
return puts(<<~NOTINTEGER) if table_exists? && columns_hash[enum_name.to_s].type != :integer
|
12
|
+
---------------------NOTINTEGER WARNING!---------------------------
|
13
|
+
#{enum_name} is not an integer column, so enum_i helper useless and method will not be defined
|
14
|
+
NOTINTEGER
|
15
|
+
|
9
16
|
define_method "#{enum_name}_i" do
|
10
|
-
self.class.send(
|
17
|
+
self.class.send(enum_name.to_s.pluralize)[send(enum_name)].to_i
|
11
18
|
end
|
12
19
|
end
|
13
20
|
|
14
21
|
# Defines two scopes for one for an inclusion: `WHERE enum IN( enum1, enum2 )`,
|
15
22
|
# and the second for an exclusion: `WHERE enum NOT IN( enum1, enum2 )`
|
23
|
+
# works fine with supersets and basic enums
|
16
24
|
#
|
17
25
|
# Ex:
|
18
26
|
# Request.with_statuses( :payed, :delivery_set ) # >> :payed and [:ready_for_shipment, :on_delivery, :delivered] requests
|
@@ -20,7 +28,6 @@ module EnumExt::BasicHelpers
|
|
20
28
|
# Request.without_statuses( :payed, :in_warehouse ) # >> scope all requests with statuses not eq to :payed or :ready_for_shipment
|
21
29
|
def multi_enum_scopes(enum_name)
|
22
30
|
enum_plural = enum_name.to_s.pluralize
|
23
|
-
enum_obj = send(enum_plural)
|
24
31
|
|
25
32
|
self.instance_eval do
|
26
33
|
# EnumExt.define_superset_to_enum_method(self, enum_plural)
|
@@ -28,12 +35,12 @@ module EnumExt::BasicHelpers
|
|
28
35
|
|
29
36
|
# with_enums scope
|
30
37
|
scope "with_#{enum_plural}", -> (*enum_list) {
|
31
|
-
enum_list.blank? ? nil : where( enum_name =>
|
38
|
+
enum_list.blank? ? nil : where( enum_name => send(enum_plural).superset_to_enum(*enum_list) )
|
32
39
|
} if !respond_to?("with_#{enum_plural}") && respond_to?(:scope)
|
33
40
|
|
34
41
|
# without_enums scope
|
35
42
|
scope "without_#{enum_plural}", -> (*enum_list) {
|
36
|
-
enum_list.blank? ? nil : where.not( enum_name =>
|
43
|
+
enum_list.blank? ? nil : where.not( enum_name => send(enum_plural).superset_to_enum(*enum_list) )
|
37
44
|
} if !respond_to?("without_#{enum_plural}") && respond_to?(:scope)
|
38
45
|
end
|
39
46
|
end
|
@@ -42,7 +49,7 @@ module EnumExt::BasicHelpers
|
|
42
49
|
#
|
43
50
|
# Used for mass assigning for collection without callbacks it creates bang methods for collections using update_all.
|
44
51
|
# it's often case when you need bulk update without callbacks, so it's gets frustrating to repeat:
|
45
|
-
# some_scope.update_all(status:
|
52
|
+
# some_scope.update_all(status: :new_status, update_at: Time.now)
|
46
53
|
#
|
47
54
|
# If you need callbacks you can do like this: some_scope.each(&:new_stat!) but if you don't need callbacks
|
48
55
|
# and you have lots of records to change at once you need update_all
|
@@ -66,11 +73,11 @@ module EnumExt::BasicHelpers
|
|
66
73
|
|
67
74
|
def mass_assign_enum( *enums_names )
|
68
75
|
enums_names.each do |enum_name|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
define_singleton_method( "#{
|
73
|
-
self.update_all( {enum_name =>
|
76
|
+
enum_plural = enum_name.to_s.pluralize
|
77
|
+
self.send(enum_plural).keys.each do |label|
|
78
|
+
method_name = self.send(enum_plural).transform_enum_label(label)
|
79
|
+
define_singleton_method( "#{method_name}!" ) do
|
80
|
+
self.update_all( {enum_name => self.send(enum_plural)[label]}.merge( self.column_names.include?('updated_at') ? {updated_at: Time.now} : {} ))
|
74
81
|
end
|
75
82
|
end
|
76
83
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module EnumExt
|
2
|
+
class EnumExtConfig
|
3
|
+
attr_accessor :default_helpers, :application_record_class
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
self.default_helpers = [:multi_enum_scopes, :mass_assign_enum]
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class << self
|
11
|
+
def configure
|
12
|
+
yield(config)
|
13
|
+
config.application_record_class&.extend( EnumExt ) if config.application_record_class.is_a?(Class)
|
14
|
+
end
|
15
|
+
|
16
|
+
def config
|
17
|
+
@config ||= EnumExtConfig.new
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -5,12 +5,12 @@ class EnumExt::EnumWrapper
|
|
5
5
|
include EnumExt::Annotated
|
6
6
|
|
7
7
|
# supersets is storing exact definitions, if you need a raw mapping use class.statuses.superset_statuses
|
8
|
-
attr_reader :enum_values, :supersets, :supersets_raw, :t_options_raw, :localizations, :base_class, :enum_name
|
8
|
+
attr_reader :enum_values, :supersets, :supersets_raw, :t_options_raw, :localizations, :base_class, :enum_name, :suffix, :prefix
|
9
9
|
|
10
10
|
delegate_missing_to :enum_values
|
11
11
|
delegate :inspect, to: :enum_values
|
12
12
|
|
13
|
-
def initialize(enum_values, base_class, enum_name)
|
13
|
+
def initialize(enum_values, base_class, enum_name, **options)
|
14
14
|
@enum_values = enum_values
|
15
15
|
@supersets = ActiveSupport::HashWithIndifferentAccess.new
|
16
16
|
@supersets_raw = ActiveSupport::HashWithIndifferentAccess.new
|
@@ -20,6 +20,8 @@ class EnumExt::EnumWrapper
|
|
20
20
|
|
21
21
|
@base_class = base_class
|
22
22
|
@enum_name = enum_name
|
23
|
+
@suffix = options[:suffix] || options[:_suffix]
|
24
|
+
@prefix = options[:prefix] || options[:_prefix]
|
23
25
|
end
|
24
26
|
|
25
27
|
# ext_sets_to_kinds( :ready_for_shipment, :delivery_set ) -->
|
@@ -51,6 +53,18 @@ class EnumExt::EnumWrapper
|
|
51
53
|
|
52
54
|
alias_method :t, :localizations
|
53
55
|
|
56
|
+
def transform_enum_label(label)
|
57
|
+
_prefix = if prefix
|
58
|
+
prefix == true ? "#{enum_name}_" : "#{prefix}_"
|
59
|
+
end
|
60
|
+
|
61
|
+
_suffix = if suffix
|
62
|
+
suffix == true ? "_#{enum_name}" : "_#{suffix}"
|
63
|
+
end
|
64
|
+
|
65
|
+
"#{_prefix}#{label}#{_suffix}"
|
66
|
+
end
|
67
|
+
|
54
68
|
private
|
55
69
|
|
56
70
|
def evaluate_localizations(t_enum_set)
|
@@ -75,4 +89,6 @@ class EnumExt::EnumWrapper
|
|
75
89
|
end
|
76
90
|
end
|
77
91
|
|
92
|
+
|
93
|
+
|
78
94
|
end
|
@@ -34,7 +34,7 @@ module EnumExt::HumanizeHelpers
|
|
34
34
|
# f.select :status, Request.t_statuses_options
|
35
35
|
#
|
36
36
|
# in select in Active Admin filter
|
37
|
-
# collection: Request.
|
37
|
+
# collection: Request.statuses.t_options_i
|
38
38
|
#
|
39
39
|
# Rem: select options breaks when using lambda() with params
|
40
40
|
#
|
@@ -48,12 +48,11 @@ module EnumExt::HumanizeHelpers
|
|
48
48
|
enum_name = args.shift
|
49
49
|
localization_definitions = args.pop
|
50
50
|
enum_plural = enum_name.to_s.pluralize
|
51
|
-
enum_object = send( enum_plural )
|
52
51
|
|
53
52
|
self.instance_eval do
|
54
53
|
# instance.t_enum
|
55
54
|
define_method "t_#{enum_name}" do
|
56
|
-
t = block ||
|
55
|
+
t = block || self.class.send(enum_plural).localizations[send(enum_name)]
|
57
56
|
if t.try(:lambda?)
|
58
57
|
t.try(:arity) == 1 && t.call( self ) || t.try(:call)
|
59
58
|
elsif t.is_a?(Proc)
|
@@ -64,7 +63,7 @@ module EnumExt::HumanizeHelpers
|
|
64
63
|
end
|
65
64
|
|
66
65
|
# if localization is absent than block must be given
|
67
|
-
|
66
|
+
send(enum_plural).localizations.merge!(
|
68
67
|
localization_definitions.try(:with_indifferent_access) ||
|
69
68
|
send(enum_plural).map do |k, _v|
|
70
69
|
# little bit hackerish: instantiate object just with enum setup and then call its t_.. method which
|
@@ -110,9 +109,8 @@ module EnumExt::HumanizeHelpers
|
|
110
109
|
#
|
111
110
|
def self.define_superset_humanization_helpers(base_class, superset_name, enum_name)
|
112
111
|
enum_plural = enum_name.to_s.pluralize
|
113
|
-
enum_object = base_class.send(enum_plural)
|
114
112
|
|
115
|
-
|
113
|
+
base_class.send(enum_plural).define_singleton_method( "t_#{superset_name}_options" ) do
|
116
114
|
result = evaluate_localizations(send("t_#{superset_name}"))
|
117
115
|
return result unless result.blank?
|
118
116
|
|
@@ -120,7 +118,7 @@ module EnumExt::HumanizeHelpers
|
|
120
118
|
end
|
121
119
|
|
122
120
|
# enums.t_options_i
|
123
|
-
|
121
|
+
base_class.send(enum_plural).define_singleton_method( "t_#{superset_name}_options_i" ) do
|
124
122
|
result = evaluate_localizations_to_i( send("t_#{superset_name}") )
|
125
123
|
return result unless result.to_h.values.all?(&:blank?)
|
126
124
|
|
@@ -129,10 +127,10 @@ module EnumExt::HumanizeHelpers
|
|
129
127
|
|
130
128
|
|
131
129
|
# enums.t_superset ( translations or humanizations subset for a given set )
|
132
|
-
|
130
|
+
base_class.send(enum_plural).define_singleton_method( "t_#{superset_name}" ) do
|
133
131
|
return [(["Enum translations are missing. Did you forget to translate #{enum_name}"]*2)].to_h if localizations.blank?
|
134
132
|
|
135
|
-
|
133
|
+
base_class.send(enum_plural).localizations.slice( *base_class.send(enum_plural).send(superset_name) )
|
136
134
|
end
|
137
135
|
end
|
138
136
|
end
|
@@ -1,69 +1,78 @@
|
|
1
1
|
module EnumExt::SupersetHelpers
|
2
2
|
# enum_supersets
|
3
|
-
#
|
4
|
-
# you should
|
3
|
+
# **Use-case** whenever you need superset of enums to behave like a super enum.
|
5
4
|
#
|
6
|
-
# it creates:
|
7
|
-
#
|
5
|
+
# You can do this with method **enum_supersets** it creates:
|
6
|
+
# - scopes for subsets,
|
7
|
+
# - instance methods with `?`
|
8
8
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#Rem:
|
17
|
-
# enum_supersets can be called twice defining a superposition of already defined supersets
|
18
|
-
# based on array operations, with already defined array methods ( considering previous example ):
|
19
|
-
# enum_supersets :status, {
|
20
|
-
# outside_warehouse: ( delivery_set_statuses - in_warehouse_statuses )... any other array operations like &, + and so can be used
|
21
|
-
# }
|
9
|
+
# For example:
|
10
|
+
# enum status: [:in_cart, :waiting_for_payment, :paid, :packing, :ready_for_shipment, :on_delivery, :delivered],
|
11
|
+
# ext: [enum_supersets: {
|
12
|
+
# around_delivery: [:ready_for_shipment, :on_delivery], # for shipping department for example
|
13
|
+
# in_warehouse: [:packing, :ready_for_shipment], # this scope is just for superposition example below
|
14
|
+
# sold: [:paid, :around_delivery, :in_warehouse, :delivered] # also you can define any superposition of already defined supersets or enum values
|
15
|
+
# }]
|
22
16
|
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
|
41
|
-
# Request.delivery_set.exists?(request) # >> true
|
42
|
-
# Request.in_warehouse.exists?(request) # >> false
|
17
|
+
# # supersets will be stored inside enum wrapper object, and can be de-referenced to basic enum values
|
18
|
+
# # using wrapper defined methods: "superset_enum_plural", i.e. statuses.sold_statuses -> [:paid, :packing, :ready_for_shipment, :on_delivery, :delivered]
|
19
|
+
# # so new supersets could be defined using Array operations against newly defined methods
|
20
|
+
# enum_ext :status, enum_supersets: {
|
21
|
+
# outside_warehouse: ( statuses.around_delivery - statuses.in_warehouse ) #... any other array operations like &, + and so can be used
|
22
|
+
# }
|
23
|
+
#
|
24
|
+
# it will generate:
|
25
|
+
#
|
26
|
+
# instance:
|
27
|
+
# - methods: around_delivery?, in_warehouse?
|
28
|
+
#
|
29
|
+
# class:
|
30
|
+
# - named scopes: around_delivery, in_warehouse
|
31
|
+
#
|
32
|
+
# enum methods:
|
33
|
+
# - Class.statuses.supersets -- will output superset definition hash
|
34
|
+
# - Class.statuses.supersets_raw -- will output superset decompositions to basic enum types hash
|
43
35
|
#
|
44
|
-
#
|
36
|
+
# - Class.statuses.around_delivery (=[:ready_for_shipment, :on_delivery, :delivered] ), in_warehouse_statuses
|
37
|
+
# - around_delivery_statuses_i (= [3,4,5]), in_warehouse_statuses_i (=[3])
|
38
|
+
#
|
39
|
+
# translation helpers grouped for superset ( started with t_... ):
|
40
|
+
# - Class.statuses.t_around_delivery_options (= [['translation or humanization', :ready_for_shipment] ...] ) for select inputs purposes
|
41
|
+
# - Class.statuses.t_around_delivery_options_i (= [['translation or humanization', 3] ...]) same as above but with integer as value ( for example to use in Active admin filters )
|
42
|
+
#
|
43
|
+
# In console:
|
44
|
+
# request.on_delivery!
|
45
|
+
# request.around_delivery? # >> true
|
46
|
+
#
|
47
|
+
# Request.around_delivery.exists?(request) # >> true
|
48
|
+
# Request.in_warehouse.exists?(request) # >> false
|
49
|
+
#
|
50
|
+
# Request.statuses.around_delivery # >> ["ready_for_shipment", "on_delivery", "delivered"]
|
51
|
+
|
45
52
|
private
|
46
53
|
def enum_supersets( enum_name, options = {} )
|
47
54
|
enum_plural = enum_name.to_s.pluralize
|
48
55
|
|
49
56
|
self.instance_eval do
|
50
|
-
|
51
|
-
|
57
|
+
suffix = send(enum_plural).suffix
|
58
|
+
prefix = send(enum_plural).prefix
|
59
|
+
send(enum_plural).supersets.merge!( options.transform_values{ _1.try(:map, &:to_s) || _1.to_s } )
|
52
60
|
|
53
61
|
options.each do |superset_name, enum_vals|
|
54
|
-
raise "Can't define superset with name: #{superset_name}, #{enum_plural} already has such method!" if
|
62
|
+
raise "Can't define superset with name: #{superset_name}, #{enum_plural} already has such method!" if send(enum_plural).respond_to?(superset_name)
|
55
63
|
|
56
|
-
|
64
|
+
send(enum_plural).supersets_raw[superset_name] = send(enum_plural).superset_to_enum(*enum_vals)
|
57
65
|
|
58
|
-
# class.
|
59
|
-
|
66
|
+
# class.enum_wrapper.superset
|
67
|
+
send(enum_plural).define_singleton_method(superset_name) { base_class.send(enum_plural).superset_to_enum(*enum_vals) }
|
60
68
|
|
69
|
+
superset_method_name = send(enum_plural).transform_enum_label(superset_name)
|
61
70
|
# superset_name scope
|
62
|
-
scope
|
71
|
+
scope superset_method_name, -> { where( enum_name => send(enum_plural).send(superset_name) ) } if respond_to?(:scope)
|
63
72
|
|
64
73
|
# instance.superset_name?
|
65
|
-
define_method "#{
|
66
|
-
send(enum_name) &&
|
74
|
+
define_method "#{superset_method_name}?" do
|
75
|
+
send(enum_name) && self.class.send(enum_plural).send(superset_name).include?( send(enum_name) )
|
67
76
|
end
|
68
77
|
|
69
78
|
EnumExt::HumanizeHelpers.define_superset_humanization_helpers( self, superset_name, enum_name )
|
data/lib/enum_ext/version.rb
CHANGED
data/lib/enum_ext.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "enum_ext/version"
|
2
|
+
require "enum_ext/config"
|
2
3
|
require "enum_ext/annotated"
|
3
4
|
require "enum_ext/enum_wrapper"
|
4
5
|
require "enum_ext/humanize_helpers"
|
@@ -16,24 +17,6 @@ require "enum_ext/superset_helpers"
|
|
16
17
|
# has_many :requests
|
17
18
|
# end
|
18
19
|
|
19
|
-
puts <<~DEPRECATION
|
20
|
-
---------------------DEPRECATION WARNING---------------------------
|
21
|
-
There are TWO MAJOR breaking changes coming into the next major version :
|
22
|
-
First deprecation: all major DSL moving class methods to
|
23
|
-
enum, just for the sake of clarity:
|
24
|
-
|
25
|
-
Ex for enum named kinds it could look like this:
|
26
|
-
|
27
|
-
Class.ext_sets_to_kinds --> Class.kinds.superset_to_basic
|
28
|
-
Class.ext_kinds --> Class.kinds.supersets
|
29
|
-
Class.all_kinds_defs --> Class.kinds.all
|
30
|
-
|
31
|
-
Class.t_kinds --> Class.kinds.t
|
32
|
-
Class.t_kinds_options --> Class.kinds.t_options
|
33
|
-
Class.t_named_set_kinds_options --> Class.kinds.t_named_set_options
|
34
|
-
|
35
|
-
DEPRECATION
|
36
|
-
|
37
20
|
module EnumExt
|
38
21
|
include HumanizeHelpers # translate and humanize
|
39
22
|
include SupersetHelpers # enum_supersets
|
@@ -48,34 +31,39 @@ module EnumExt
|
|
48
31
|
# so calling super should be different based on ActiveRecord major version
|
49
32
|
def enum(name = nil, values = nil, **options)
|
50
33
|
single_enum_definition = name.present?
|
51
|
-
extensions = options.delete(:ext)
|
34
|
+
extensions = [*EnumExt.config.default_helpers, *options.delete(:ext)]
|
35
|
+
options_dup = options.dup
|
52
36
|
|
53
37
|
(ActiveRecord::VERSION::MAJOR >= 7 ? super : super(options)).tap do |multiple_enum_definitions|
|
54
38
|
if single_enum_definition
|
55
|
-
|
39
|
+
replace_enum_with_wrapper(name, options_dup)
|
40
|
+
enum_ext(name, [*extensions])
|
56
41
|
else
|
57
|
-
multiple_enum_definitions.each { |enum_name,|
|
42
|
+
multiple_enum_definitions.each { |enum_name,|
|
43
|
+
replace_enum_with_wrapper(enum_name, options_dup)
|
44
|
+
enum_ext(enum_name, [*extensions])
|
45
|
+
}
|
58
46
|
end
|
59
47
|
end
|
60
48
|
end
|
61
49
|
|
62
50
|
# its an extension helper, on the opposite to basic enum method could be called multiple times
|
63
51
|
def enum_ext(enum_name, extensions)
|
64
|
-
|
65
|
-
# [:enum_i, :enum_multi_scopes, enum_supersets: { valid: [:fresh, :cool], invalid: [:stale] }]
|
52
|
+
# [:enum_i, :enum_multi_scopes, enum_supersets: { valid: [:fresh, :cool], invalid: [:stale] }]
|
66
53
|
# --> [:enum_i, :enum_multi_scopes, [:enum_supersets, { valid: [:fresh, :cool], invalid: [:stale] }]
|
67
54
|
[*extensions].map { _1.try(:to_a)&.flatten || _1 }
|
68
55
|
.each { |(ext_method, params)| send(*[ext_method, enum_name, params].compact) }
|
69
56
|
end
|
70
57
|
|
71
58
|
private
|
72
|
-
|
59
|
+
|
60
|
+
def replace_enum_with_wrapper(enum_name, options_dup)
|
73
61
|
enum_name_plural = enum_name.to_s.pluralize
|
74
62
|
return if send(enum_name_plural).is_a?(EnumWrapper)
|
75
63
|
|
76
64
|
# enum will freeze values so there is no other way to move extended functionality,
|
77
65
|
# than to use wrapper and delegate everything to enum_values
|
78
|
-
enum_wrapper = EnumWrapper.new(send(enum_name_plural), self, enum_name)
|
66
|
+
enum_wrapper = EnumWrapper.new(send(enum_name_plural), self, enum_name, **options_dup)
|
79
67
|
# "self" here is a base enum class, so we are replacing original enum definition, with a wrapper
|
80
68
|
define_singleton_method(enum_name_plural) { enum_wrapper }
|
81
69
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: enum_ext
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- alekseyl
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-
|
11
|
+
date: 2023-11-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -181,6 +181,7 @@ files:
|
|
181
181
|
- lib/enum_ext.rb
|
182
182
|
- lib/enum_ext/annotated.rb
|
183
183
|
- lib/enum_ext/basic_helpers.rb
|
184
|
+
- lib/enum_ext/config.rb
|
184
185
|
- lib/enum_ext/enum_wrapper.rb
|
185
186
|
- lib/enum_ext/humanize_helpers.rb
|
186
187
|
- lib/enum_ext/superset_helpers.rb
|