enum_ext 0.5.3 → 0.8.1

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.
@@ -0,0 +1,181 @@
1
+ # I wanted to add some quick live annotation to what's defined and how it could be used
2
+ # but have no idea how to do this in a super-neat way, so it a little bit chaotic and experimental
3
+ module EnumExt::Annotated
4
+
5
+ # call it to see what's your enum current options are
6
+ def describe_basic
7
+ puts yellow( "Basic #{enum_name} definition: \n" )
8
+ print_hash(enum_values)
9
+ end
10
+
11
+ # call it to see which enum extensions are defined.
12
+ def describe_long
13
+ puts yellow( "\n\nEnumExt extensions:" )
14
+
15
+ puts [
16
+ describe_enum_i(false),
17
+ describe_mass_assign_enum(false),
18
+ describe_multi_enum_scopes(false),
19
+ describe_supersets(false),
20
+ describe_translations(false),
21
+ describe_humanizations(false)
22
+ ].join( "\n" + "-" * 100 + "\n" )
23
+ end
24
+
25
+ def describe(short = true)
26
+ describe_basic
27
+ short ? describe_short : describe_long
28
+ end
29
+
30
+ def describe_short
31
+ enabled, disabled = enabled_features.except(:key_sample).partition{!_2.blank?}.map{ |prt| prt.map(&:shift) }
32
+ puts <<~SHORT
33
+ #{yellow("EnumExt extensions:")}
34
+ #{cyan("Enabled")}: #{enabled.join(", ")}
35
+ #{red("Disabled")}: #{disabled.join(", ")}
36
+ SHORT
37
+
38
+ print_short(:supersets)
39
+ print_short(:translations)
40
+ print_short(:humanization)
41
+ end
42
+
43
+ # --------------------------------------------------------------------
44
+ # ------------- per helpers describers -------------------------------
45
+ # --------------------------------------------------------------------
46
+ def describe_enum_i(output = true)
47
+ description = basic_helpers_usage_header(:enum_i)
48
+ description << <<~ENUM_I if enabled_features[:enum_i]
49
+ #{black("instance")}.#{cyan( enabled_features[:enum_i] )}
50
+ # output will be same as #{base_class.to_s}.#{enum_name}[:#{enabled_features[:key_sample]}]
51
+ ENUM_I
52
+
53
+ output ? puts(description) : description
54
+ end
55
+
56
+ def describe_mass_assign_enum(output = true)
57
+ description = basic_helpers_usage_header(:mass_assign_enum)
58
+ description << <<~MASS_ASSIGN if enabled_features[:mass_assign_enum]
59
+ # To assign #{enabled_features[:key_sample]} to all elements of any_scope or relation call:
60
+ #{black(base_class.to_s)}.any_scope.#{cyan( enabled_features[:mass_assign_enum] )}
61
+ MASS_ASSIGN
62
+
63
+ output ? puts(description) : description
64
+ end
65
+
66
+ def describe_multi_enum_scopes(output = true)
67
+ description = basic_helpers_usage_header(:multi_enum_scopes)
68
+ description << <<~MULTI_SCOPES if enabled_features[:multi_enum_scopes]
69
+ # Two scopes: with_#{enum_name} and without_#{enum_name} are defined
70
+ # To get elements with a given enums or supersets values call:
71
+ #{black(base_class.to_s)}.#{cyan("with_#{enum_name}")}(:#{keys.sample(2).join(", :")})
72
+ \n# To get all elements except for the ones with enums or supersets values call:
73
+ #{black(base_class.to_s)}.#{cyan("without_#{enum_name}")}(:#{keys.sample(2).join(", :")})
74
+ MULTI_SCOPES
75
+
76
+ output ? puts(description) : description
77
+ end
78
+
79
+ def describe_supersets(output = true)
80
+ description = if enabled_features[:supersets].blank?
81
+ red( "\nSupersets not used!\n" )
82
+ else
83
+ superset_method = enabled_features[:supersets].keys.first
84
+ red( "\nSupersets definitions:\n" ) << inspect_hash(enabled_features[:supersets]) << <<~SUPERSETS
85
+
86
+ # Instance 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
+
96
+ SUPERSETS
97
+ end
98
+
99
+ output ? puts(description) : description
100
+ end
101
+
102
+ def describe_translations(output = true)
103
+ description = if enabled_features[:translations].blank?
104
+ red( "\nTranslations not used!\n" )
105
+ else
106
+ red( "\nTranslations definitions (will skip instance dependent translation)\n" ) <<
107
+ inspect_hash(enabled_features[:translations])
108
+ end
109
+
110
+ output ? puts(description) : description
111
+ end
112
+
113
+ def describe_humanizations(output = true)
114
+ description = if enabled_features[:humanization].blank?
115
+ red( "\nHumanization not used!\n" )
116
+ else
117
+ red( "\nHumanization definitions (will skip instance dependent humanization)\n" ) <<
118
+ inspect_hash(enabled_features[:humanization])
119
+ end
120
+
121
+ output ? puts(description) : description
122
+ end
123
+
124
+ private
125
+
126
+ def enabled_features
127
+ enum_sample = keys.first
128
+ {
129
+ key_sample: enum_sample,
130
+ enum_i: base_class.instance_methods.include?("#{enum_name}_i".to_sym) && "#{enum_name}_i",
131
+ mass_assign_enum: base_class.respond_to?("#{enum_sample}!") && "#{enum_sample}!",
132
+ multi_enum_scopes: base_class.respond_to?("with_#{enum_name.to_s.pluralize}") && "with_#{enum_name.to_s.pluralize}",
133
+ supersets: supersets_raw,
134
+ translations: try(:t_options),
135
+ humanization: try(:t_options)
136
+ }
137
+ end
138
+
139
+ def basic_helpers_usage_header(helper_name)
140
+ enabled_features[helper_name] ? "\n#{red(helper_name)} helpers enabled, usage:\n"
141
+ : "\n#{helper_name} wasn't used\n"
142
+ end
143
+
144
+ def print_hash(hsh)
145
+ defined?(ai) ? ap(hsh) : pp(hsh)
146
+ end
147
+
148
+ def inspect_hash(hsh)
149
+ defined?(ai) ? hsh.ai : hsh.inspect
150
+ end
151
+
152
+ def print_short(feature)
153
+ if enabled_features[feature].present?
154
+ puts black("#{feature.to_s.humanize}:")
155
+ print_hash(enabled_features[feature])
156
+ end
157
+ end
158
+
159
+ def yellow(str)
160
+ # yellow ANSI color
161
+ "\e[0;33;49m#{str}\e[0m"
162
+ end
163
+
164
+ def cyan(str)
165
+ # cyan ANSI color
166
+ "\e[0;36;49m#{str}\e[0m"
167
+ end
168
+
169
+ def red(str)
170
+ # red ANSI color
171
+ "\e[0;31;49m#{str}\e[0m"
172
+ end
173
+
174
+ def black(comment)
175
+ # bright black bold ANSI color
176
+ "\e[0;90;1;49m#{comment}\e[0m"
177
+ end
178
+
179
+ end
180
+
181
+
@@ -0,0 +1,86 @@
1
+ module EnumExt::BasicHelpers
2
+
3
+ private
4
+ # Defines instance method a shortcut for getting integer value of an enum.
5
+ # for enum named 'status' will generate:
6
+ #
7
+ # instance.status_i
8
+ #
9
+ # Rem. Will not define helper when enum values are strings, and will print warning
10
+ def enum_i( enum_name )
11
+ return puts(<<~NOTINTEGER) if 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
+
16
+ define_method "#{enum_name}_i" do
17
+ self.class.send(enum_name.to_s.pluralize)[send(enum_name)].to_i
18
+ end
19
+ end
20
+
21
+ # Defines two scopes for one for an inclusion: `WHERE enum IN( enum1, enum2 )`,
22
+ # and the second for an exclusion: `WHERE enum NOT IN( enum1, enum2 )`
23
+ # works fine with supersets and basic enums
24
+ #
25
+ # Ex:
26
+ # Request.with_statuses( :payed, :delivery_set ) # >> :payed and [:ready_for_shipment, :on_delivery, :delivered] requests
27
+ # Request.without_statuses( :payed ) # >> scope for all requests with statuses not eq to :payed
28
+ # Request.without_statuses( :payed, :in_warehouse ) # >> scope all requests with statuses not eq to :payed or :ready_for_shipment
29
+ def multi_enum_scopes(enum_name)
30
+ enum_plural = enum_name.to_s.pluralize
31
+
32
+ self.instance_eval do
33
+ # EnumExt.define_superset_to_enum_method(self, enum_plural)
34
+ # EnumExt.define_summary_methods(self, enum_plural)
35
+
36
+ # with_enums scope
37
+ scope "with_#{enum_plural}", -> (*enum_list) {
38
+ enum_list.blank? ? nil : where( enum_name => send(enum_plural).superset_to_enum(*enum_list) )
39
+ } if !respond_to?("with_#{enum_plural}") && respond_to?(:scope)
40
+
41
+ # without_enums scope
42
+ scope "without_#{enum_plural}", -> (*enum_list) {
43
+ enum_list.blank? ? nil : where.not( enum_name => send(enum_plural).superset_to_enum(*enum_list) )
44
+ } if !respond_to?("without_#{enum_plural}") && respond_to?(:scope)
45
+ end
46
+ end
47
+
48
+ # Ex mass_assign_enum
49
+ #
50
+ # Used for mass assigning for collection without callbacks it creates bang methods for collections using update_all.
51
+ # it's often case when you need bulk update without callbacks, so it's gets frustrating to repeat:
52
+ # some_scope.update_all(status: :new_status, update_at: Time.now)
53
+ #
54
+ # If you need callbacks you can do like this: some_scope.each(&:new_stat!) but if you don't need callbacks
55
+ # and you have lots of records to change at once you need update_all
56
+ #
57
+ # mass_assign_enum( :status )
58
+ #
59
+ # class methods:
60
+ # in_cart! paid! in_warehouse! and so
61
+ #
62
+ # Console:
63
+ # request1.in_cart!
64
+ # request2.waiting_for_payment!
65
+ # Request.with_statuses( :in_cart, :waiting_for_payment ).payed!
66
+ # request1.paid? # >> true
67
+ # request2.paid? # >> true
68
+ # request1.updated_at # >> Time.now
69
+ #
70
+ # order.requests.paid.all?(&:paid?) # >> true
71
+ # order.requests.paid.delivered!
72
+ # order.requests.map(&:status).uniq #>> [:delivered]
73
+
74
+ def mass_assign_enum( *enums_names )
75
+ enums_names.each do |enum_name|
76
+ enum_vals = self.send( enum_name.to_s.pluralize )
77
+
78
+ enum_vals.keys.each do |enum_el|
79
+ define_singleton_method( "#{enum_el}!" ) do
80
+ self.update_all( {enum_name => enum_vals[enum_el]}.merge( self.column_names.include?('updated_at') ? {updated_at: Time.now} : {} ))
81
+ end
82
+ end
83
+ end
84
+ end
85
+ alias_method :enum_mass_assign, :mass_assign_enum
86
+ end
@@ -0,0 +1,78 @@
1
+ # This is an wrapper class for a basic enum.
2
+ # Since enum values will be freezed right after the definition, we can't enrich enum directly functionality
3
+ # We can only wrap it with our own object and delegate enum base base functionality internally
4
+ class EnumExt::EnumWrapper
5
+ include EnumExt::Annotated
6
+
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
9
+
10
+ delegate_missing_to :enum_values
11
+ delegate :inspect, to: :enum_values
12
+
13
+ def initialize(enum_values, base_class, enum_name)
14
+ @enum_values = enum_values
15
+ @supersets = ActiveSupport::HashWithIndifferentAccess.new
16
+ @supersets_raw = ActiveSupport::HashWithIndifferentAccess.new
17
+
18
+ @t_options_raw = ActiveSupport::HashWithIndifferentAccess.new
19
+ @localizations = ActiveSupport::HashWithIndifferentAccess.new
20
+
21
+ @base_class = base_class
22
+ @enum_name = enum_name
23
+ end
24
+
25
+ # ext_sets_to_kinds( :ready_for_shipment, :delivery_set ) -->
26
+ # [:ready_for_shipment, :on_delivery, :delivered]
27
+ def superset_to_enum( *enum_or_sets )
28
+ return [] if enum_or_sets.blank?
29
+ enum_or_sets_strs = enum_or_sets.map(&:to_s)
30
+
31
+ next_level_deeper = supersets.slice( *enum_or_sets_strs ).values.flatten
32
+ (enum_or_sets_strs & enum_values.keys | send(:superset_to_enum, *next_level_deeper)).uniq
33
+ end
34
+
35
+ def all
36
+ {
37
+ **enum_values,
38
+ supersets: {
39
+ **send(:supersets_raw)
40
+ }
41
+ }
42
+ end
43
+
44
+ def t_options_i
45
+ evaluate_localizations_to_i(localizations)
46
+ end
47
+
48
+ def t_options
49
+ evaluate_localizations(localizations)
50
+ end
51
+
52
+ alias_method :t, :localizations
53
+
54
+ private
55
+
56
+ def evaluate_localizations(t_enum_set)
57
+ # { kind => kind_translator, kind2 => kind2_translator } --> [[kind_translator, kind], [kind2_translator, kind2]]
58
+ t_enum_set.invert.to_a.map do | translator, enum_key |
59
+ # since all procs in t_enum are evaluated in context of a record than it's not always possible to create select options automatically
60
+ translation = if translator.respond_to?(:call)
61
+ if translator.arity < 1
62
+ translator.call rescue "Cannot create option for #{enum_key} ( proc fails to evaluate )"
63
+ else
64
+ "Cannot create option for #{enum_key} because of a lambda"
65
+ end
66
+ end || translator
67
+ [translation, enum_key]
68
+ end
69
+ end
70
+
71
+ def evaluate_localizations_to_i(t_enum_set)
72
+ # { kind => kind_translation, kind2 => kind2_translation } --> [[kind_translation, kind_i], [kind2_translation, kind2_i]]
73
+ evaluate_localizations(t_enum_set).map do | translation, name |
74
+ [ translation, self[name] ]
75
+ end
76
+ end
77
+
78
+ end
@@ -0,0 +1,159 @@
1
+ module EnumExt::HumanizeHelpers
2
+
3
+ # if app doesn't need internationalization, it may use humanize_enum to make enum user friendly
4
+ #
5
+ # class Request
6
+ # humanize_enum :status, {
7
+ # #locale dependent example with pluralization and lambda:
8
+ # payed: -> (t_self) { I18n.t("request.status.payed", count: t_self.sum ) }
9
+ #
10
+ # #locale dependent example with pluralization and proc:
11
+ # payed: Proc.new{ I18n.t("request.status.payed", count: self.sum ) }
12
+ #
13
+ # #locale independent:
14
+ # ready_for_shipment: "Ready to go!"
15
+ # }
16
+ # end
17
+ #
18
+ # Could be called multiple times, all humanization definitions will be merged under the hood:
19
+ # humanize_enum :status, {
20
+ # payed: I18n.t("scope.#{status}")
21
+ # }
22
+ # humanize_enum :status, {
23
+ # billed: I18n.t("scope.#{status}")
24
+ # }
25
+ #
26
+ #
27
+ # Example with block:
28
+ #
29
+ # humanize_enum :status do
30
+ # I18n.t("scope.#{status}")
31
+ # end
32
+ #
33
+ # in views select:
34
+ # f.select :status, Request.t_statuses_options
35
+ #
36
+ # in select in Active Admin filter
37
+ # collection: Request.statuses.t_options_i
38
+ #
39
+ # Rem: select options breaks when using lambda() with params
40
+ #
41
+ # Console:
42
+ # request.sum = 3
43
+ # request.payed!
44
+ # request.status # >> payed
45
+ # request.t_status # >> "Payed 3 dollars"
46
+ # Request.t_statuses # >> { in_cart: -> { I18n.t("request.status.in_cart") }, .... }
47
+ def humanize_enum( *args, &block )
48
+ enum_name = args.shift
49
+ localization_definitions = args.pop
50
+ enum_plural = enum_name.to_s.pluralize
51
+
52
+ self.instance_eval do
53
+ # instance.t_enum
54
+ define_method "t_#{enum_name}" do
55
+ t = block || self.class.send(enum_plural).localizations[send(enum_name)]
56
+ if t.try(:lambda?)
57
+ t.try(:arity) == 1 && t.call( self ) || t.try(:call)
58
+ elsif t.is_a?(Proc)
59
+ instance_eval(&t)
60
+ else
61
+ t
62
+ end.to_s
63
+ end
64
+
65
+ # if localization is absent than block must be given
66
+ send(enum_plural).localizations.merge!(
67
+ localization_definitions.try(:with_indifferent_access) ||
68
+ send(enum_plural).map do |k, _v|
69
+ # little bit hackerish: instantiate object just with enum setup and then call its t_.. method which
70
+ [k, Proc.new{ self.new({ enum_name => k }).send("t_#{enum_name}") }]
71
+ end.to_h.with_indifferent_access
72
+ )
73
+
74
+ # hm.. lost myself here, why did I implement this method
75
+ define_method "t_#{enum_name}=" do |new_val|
76
+ send("#{enum_name}=", new_val)
77
+ end
78
+
79
+ end
80
+ end
81
+ alias localize_enum humanize_enum
82
+
83
+ # Simple way to translate enum.
84
+ # It use either given scope as second argument, or generated activerecord.attributes.model_name_underscore.enum_name
85
+ # If block is given than no scopes are taken in consider
86
+ def translate_enum( *args, &block )
87
+ enum_name = args.shift
88
+ enum_plural = enum_name.to_s.pluralize
89
+ t_scope = args.pop || "activerecord.attributes.#{self.name.underscore}.#{enum_plural}"
90
+
91
+ if block_given?
92
+ humanize_enum( enum_name, &block )
93
+ else
94
+ humanize_enum( enum_name, send(enum_plural).keys.map{|en| [ en, Proc.new{ I18n.t("#{t_scope}.#{en}") }] }.to_h )
95
+ end
96
+ end
97
+
98
+ # human_attribute_name is redefined for automation like this:
99
+ # p #{object.class.human_attribute_name( attr_name )}:
100
+ # p object.send(attr_name)
101
+ def human_attribute_name( name, options = {} )
102
+ # if name starts from t_ and there is a column with the last part then ...
103
+ name[0..1] == 't_' && column_names.include?(name[2..-1]) ? super( name[2..-1], options ) : super( name, options )
104
+ end
105
+
106
+
107
+ # t_... methods for supersets will just slice
108
+ # original enum t_.. methods output and return only superset related values from it
109
+ #
110
+ def self.define_superset_humanization_helpers(base_class, superset_name, enum_name)
111
+ enum_plural = enum_name.to_s.pluralize
112
+
113
+ base_class.send(enum_plural).define_singleton_method( "t_#{superset_name}_options" ) do
114
+ result = evaluate_localizations(send("t_#{superset_name}"))
115
+ return result unless result.blank?
116
+
117
+ [["Enum translations call missed. Did you forget to call translate #{enum_name}"]*2]
118
+ end
119
+
120
+ # enums.t_options_i
121
+ base_class.send(enum_plural).define_singleton_method( "t_#{superset_name}_options_i" ) do
122
+ result = evaluate_localizations_to_i( send("t_#{superset_name}") )
123
+ return result unless result.to_h.values.all?(&:blank?)
124
+
125
+ [["Enum translations are missing. Did you forget to translate #{enum_name}"]*2]
126
+ end
127
+
128
+
129
+ # enums.t_superset ( translations or humanizations subset for a given set )
130
+ base_class.send(enum_plural).define_singleton_method( "t_#{superset_name}" ) do
131
+ return [(["Enum translations are missing. Did you forget to translate #{enum_name}"]*2)].to_h if localizations.blank?
132
+
133
+ base_class.send(enum_plural).localizations.slice( *base_class.send(enum_plural).send(superset_name) )
134
+ end
135
+ end
136
+ end
137
+
138
+
139
+ # # t_... - are translation dependent methods
140
+ # # This one is a narrow case helpers just a quick subset of t_ enums options for a set
141
+ # # class.t_enums_options
142
+ # enum_obj.define_singleton_method( "t_#{superset_name}_options" ) do
143
+ # return [["Enum translations call missed. Did you forget to call translate #{enum_name}"]*2] unless respond_to?( "t_#{enum_plural}_options_raw" )
144
+ #
145
+ # send("t_#{enum_plural}_options_raw", send("t_#{superset_name}_#{enum_plural}") )
146
+ # end
147
+ #
148
+ # # class.t_enums_options_i
149
+ # enum_obj.define_singleton_method( "t_#{superset_name}_options_i" ) do
150
+ # return [["Enum translations call missed. Did you forget to call translate #{enum_name}"]*2] unless respond_to?( "t_#{enum_plural}_options_raw_i" )
151
+ #
152
+ # send("t_#{enum_plural}_options_raw_i", send("t_#{superset_name}_#{enum_plural}") )
153
+ # end
154
+ #
155
+ # enum_obj.define_singleton_method( "t_#{superset_name}_options" ) do
156
+ # return [["Enum translations call missed. Did you forget to call translate #{enum_name}"]*2] if t_options_raw.blank?
157
+ #
158
+ # t_options_raw["t_#{superset_name}"]
159
+ # end
@@ -0,0 +1,83 @@
1
+ module EnumExt::SupersetHelpers
2
+ # enum_supersets
3
+ # **Use-case** whenever you need superset of enums to behave like a super enum.
4
+ #
5
+ # You can do this with method **enum_supersets** it creates:
6
+ # - scopes for subsets,
7
+ # - instance methods with `?`
8
+ #
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
+ # }]
16
+ #
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
35
+ #
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
+
52
+ private
53
+ def enum_supersets( enum_name, options = {} )
54
+ enum_plural = enum_name.to_s.pluralize
55
+
56
+ self.instance_eval do
57
+ send(enum_plural).supersets.merge!( options.transform_values{ _1.try(:map, &:to_s) || _1.to_s } )
58
+
59
+ options.each do |superset_name, enum_vals|
60
+ raise "Can't define superset with name: #{superset_name}, #{enum_plural} already has such method!" if send(enum_plural).respond_to?(superset_name)
61
+
62
+ send(enum_plural).supersets_raw[superset_name] = send(enum_plural).superset_to_enum(*enum_vals)
63
+
64
+ # class.enum_wrapper.superset
65
+ send(enum_plural).define_singleton_method(superset_name) { base_class.send(enum_plural).superset_to_enum(*enum_vals) }
66
+
67
+ # superset_name scope
68
+ scope superset_name, -> { where( enum_name => send(enum_plural).send(superset_name) ) } if respond_to?(:scope)
69
+
70
+ # instance.superset_name?
71
+ define_method "#{superset_name}?" do
72
+ send(enum_name) && self.class.send(enum_plural).send(superset_name).include?( send(enum_name) )
73
+ end
74
+
75
+ EnumExt::HumanizeHelpers.define_superset_humanization_helpers( self, superset_name, enum_name )
76
+ end
77
+
78
+ end
79
+ end
80
+ alias_method :ext_enum_sets, :enum_supersets
81
+ end
82
+
83
+
@@ -1,3 +1,3 @@
1
1
  module EnumExt
2
- VERSION = "0.5.3"
2
+ VERSION = "0.8.1"
3
3
  end