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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8d99346b1ae5f9458733025608cad6e8a1ab427091ce44d10a544e880722633c
4
- data.tar.gz: 6a1943ddedaa37022bdfe421dbc4c5d063c63149a96aaa0f05cdab35f423de6b
3
+ metadata.gz: d9693b1516f925bdb67d488f84e3ac2409d9df9d56643a729c2523910664ce81
4
+ data.tar.gz: c1a4d7210af6cdfefaaf136f3098a1517e66f4eb86af26488e5d9cb7b76047d6
5
5
  SHA512:
6
- metadata.gz: ce540db8aca9002c6a46a5ae5eb7a45f7a296d4fd183493a8796e3b9dd007ebb04bc0793b1162b82e177de5b59ff06e8e553248a2aa4c725b17d3570d682444f
7
- data.tar.gz: 7db85461bd47c03b3169259f699a29e7a1855fd791d53fb4fe84995b812739fe4968b8184665dfd04230b71e2d78152dc8868f78f89452f37d6b20ed00c77ecc
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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- enum_ext (0.8.0)
4
+ enum_ext (0.9.0)
5
5
  activerecord (>= 5.2.4.3)
6
6
 
7
7
  GEM
@@ -82,6 +82,7 @@ PLATFORMS
82
82
  ruby
83
83
 
84
84
  DEPENDENCIES
85
+ activerecord (< 7)
85
86
  amazing_print
86
87
  bundler (>= 1.11)
87
88
  byebug
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.7'
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 kinds: {}, ext: [:enum_i, :enum_mass_assign, ]
31
- humanize_enum #...
32
- translate_enum #...
33
- ext_enum_sets #...
34
- end
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 kind: {}
60
- enum_i :kind # will raise an private method error starting ver 0.6
61
- enum_mass_assign :kind # will raise an private method error starting ver 0.6
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 kinds: {}, ext: [:enum_i, :enum_mass_assign]
64
+ enum status: {}, ext: [:enum_i, :enum_mass_assign]
65
65
 
66
66
  # OR in case of standalone enum definition:
67
- enum kinds: {} # somewhere where you can't or don't want reach
68
- enum_ext :kinds, [:enum_i, :enum_mass_assign, enum_supersets: {} ]
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 :kinds, [:enum_i, :enum_mass_assign, :translate_enum, enum_supersets: {}]
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 :kinds, [:enum_i, :enum_mass_assign, :translate_enum, enum_supersets: {}] do
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
- if app doesn't need internationalization, it may use humanize_enum to make enum user friendly
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
- - t_in_cart, t_paid, t_ready_for_shipment
106
+ - t_status
107
107
 
108
- and to class:
109
- - t_statuses - as given or generated values
110
- - t_statuses_options - translated enum values options for select input
111
- - t_statuses_options_i - same as above but use int values with translations works for ActiveAdmin filters for instance
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.t_statuses_options
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.t_statuses_options_i
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.t_statuses # >> { in_cart: -> { I18n.t("request.status.in_cart") }, .... }
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
- delivery_set: [:ready_for_shipment, :on_delivery], # for shipping department for example
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, :delivery_set, :in_warehouse, :delivered] # also you can define any superposition of already defined supersets or enum values
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.delivery_set_statuses - statuses.in_warehouse_statuses ) #... any other array operations like &, + and so can be used
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: delivery_set?, in_warehouse?
208
+ - methods: around_delivery?, in_warehouse?
206
209
 
207
210
  class:
208
- - named scopes: delivery_set, in_warehouse
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.delivery_superset (=[:ready_for_shipment, :on_delivery, :delivered] ), in_warehouse_statuses
216
- - delivery_set_statuses_i (= [3,4,5]), in_warehouse_statuses_i (=[3])
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.t_delivery_set_options (= [['translation or humanization', :ready_for_shipment] ...] ) for select inputs purposes
220
- - Class.statuses.t_delivery_set_options_i (= [['translation or humanization', 3] ...]) same as above but with integer as value ( for example to use in Active admin filters )
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.delivery_set? # >> true
227
+ request.around_delivery? # >> true
226
228
 
227
- Request.delivery_set.exists?(request) # >> true
229
+ Request.around_delivery.exists?(request) # >> true
228
230
  Request.in_warehouse.exists?(request) # >> false
229
231
 
230
- Request.delivery_set_statuses # >> ["ready_for_shipment", "on_delivery", "delivered"]
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: ( delivery_set_statuses - in_warehouse_statuses )#... any other array operations like &, + and so can be used
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
- delivery_set: [:ready_for_shipment, :on_delivery, :delivered],
246
- not_in_cart: [:paid, :delivery_set] #
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, :delivery_set ) # >> status IN (:payed, :ready_for_shipment, :on_delivery, :delivered)
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: Request.statuses[:new_status], update_at: Time.now)
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
  ![img_3.png](img_3.png)
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
- [] better support for suffix/prefix as enum does
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
 
@@ -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 opitons
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 all enum extensions defined.
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
- # Class level methods added: #{enabled_features[:supersets].keys.join(", ")}
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
- private
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("#{enum_name.to_s.pluralize}")[send(enum_name)].to_i
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 => enum_obj.superset_to_enum(*enum_list) )
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 => enum_obj.superset_to_enum(*enum_list) )
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: Request.statuses[:new_status], update_at: Time.now)
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
- enum_vals = self.send( enum_name.to_s.pluralize )
70
-
71
- enum_vals.keys.each do |enum_el|
72
- define_singleton_method( "#{enum_el}!" ) do
73
- self.update_all( {enum_name => enum_vals[enum_el]}.merge( self.column_names.include?('updated_at') ? {updated_at: Time.now} : {} ))
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.t_statuses_options_i
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 || enum_object.localizations[send(enum_name)]
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
- enum_object.localizations.merge!(
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
- enum_object.define_singleton_method( "t_#{superset_name}_options" ) do
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
- enum_object.define_singleton_method( "t_#{superset_name}_options_i" ) do
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
- enum_object.define_singleton_method( "t_#{superset_name}" ) do
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
- enum_object.localizations.slice( *enum_object.send(superset_name) )
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
- # This method intend for creating and using some sets of enum values,
4
- # you should
3
+ # **Use-case** whenever you need superset of enums to behave like a super enum.
5
4
  #
6
- # it creates: scopes for subsets,
7
- # instance method with ?
5
+ # You can do this with method **enum_supersets** it creates:
6
+ # - scopes for subsets,
7
+ # - instance methods with `?`
8
8
  #
9
- # For this call:
10
- # enum status: [:in_cart, :waiting_for_payment, :paid, :packing, :ready_for_shipment, :on_delivery, :delivered],
11
- # ext:[ , supersets: {
12
- # delivery_set: [: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: [:payd, :delivery_set, :in_warehouse, :delivered]
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
- # so the enum_supersets will generate:
24
- # instance:
25
- # methods: delivery_set?, in_warehouse?
26
- # class:
27
- # named scopes: delivery_set, in_warehouse
28
- # class helpers:
29
- # - delivery_set_statuses (=[:ready_for_shipment, :on_delivery, :delivered] ), in_warehouse_statuses
30
- # - delivery_set_statuses_i (= [3,4,5]), in_warehouse_statuses_i (=[3])
31
- # class translation helpers ( started with t_... )
32
- # for select inputs purposes:
33
- # - t_delivery_set_statuses_options (= [['translation or humanization', :ready_for_shipment] ...])
34
- # same as above but with integer as value ( for example to use in Active admin filters )
35
- # - t_delivery_set_statuses_options_i (= [['translation or humanization', 3] ...])
36
-
37
- # Console:
38
- # request.on_delivery!
39
- # request.delivery_set? # >> true
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
- # Request.statuses.supersets[:delivery_set] # >> [:ready_for_shipment, :on_delivery, :delivered]
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
- enum_obj = send(enum_plural)
51
- enum_obj.supersets.merge!( options.transform_values{ _1.try(:map, &:to_s) || _1.to_s } )
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 enum_obj.respond_to?(superset_name)
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
- enum_obj.supersets_raw[superset_name] = enum_obj.superset_to_enum(*enum_vals)
64
+ send(enum_plural).supersets_raw[superset_name] = send(enum_plural).superset_to_enum(*enum_vals)
57
65
 
58
- # class.statuses.superset_statuses
59
- enum_obj.define_singleton_method(superset_name) { enum_obj.superset_to_enum(*enum_vals) }
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 superset_name, -> { where( enum_name => enum_obj.send(superset_name) ) } if respond_to?(: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 "#{superset_name}?" do
66
- send(enum_name) && enum_obj.send(superset_name).include?( 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 )
@@ -1,3 +1,3 @@
1
1
  module EnumExt
2
- VERSION = "0.8.0"
2
+ VERSION = "1.0.0"
3
3
  end
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
- enum_ext(name, extensions)
39
+ replace_enum_with_wrapper(name, options_dup)
40
+ enum_ext(name, [*extensions])
56
41
  else
57
- multiple_enum_definitions.each { |enum_name,| enum_ext(enum_name, extensions) }
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
- replace_enum_with_wrapper(enum_name)
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
- def replace_enum_with_wrapper(enum_name)
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.8.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-10-02 00:00:00.000000000 Z
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