enum_ext 0.5.3 → 0.8.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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