Module: EnumExt
- Defined in:
- lib/enum_ext.rb,
lib/enum_ext/version.rb
Constant Summary collapse
- VERSION =
"0.5.3"
Class Method Summary collapse
- .define_set_to_enum_method(extended_class, enum_plural) ⇒ Object
- .define_summary_methods(extended_class, enum_plural) ⇒ Object
Instance Method Summary collapse
-
#enum(definitions) ⇒ Object
extending enum with inplace settings enum status: {}, ext: [:enum_i, :mass_assign_enum, :enum_multi_scopes] enum_i and mass_assign_enum ara.
-
#enum_i(enum_name) ⇒ Object
Defines instance method a shortcut for getting integer value of an enum.
-
#ext_enum_sets(enum_name, options = {}) ⇒ Object
Rem: ext_enum_sets can be called twice defining a superposition of already defined sets ( considering previous example ): ext_enum_sets :status, { outside_warehouse: ( delivery_set_statuses - in_warehouse_statuses )… any other array operations like &, + and so can be used }.
-
#human_attribute_name(name, options = {}) ⇒ Object
human_attribute_name is redefined for automation like this: p #attr_name ): p object.send(attr_name).
-
#humanize_enum(*args, &block) ⇒ Object
(also: #localize_enum)
if app doesn’t need internationalization, it may use humanize_enum to make enum user friendly.
-
#mass_assign_enum(*enums_names) ⇒ Object
(also: #enum_mass_assign)
Ex mass_assign_enum.
-
#multi_enum_scopes(enum_name) ⇒ Object
Defines two scopes for one for an inclusion: ‘WHERE enum IN( enum1, enum2 )`, and the second for an exclusion: `WHERE enum NOT IN( enum1, enum2 )`.
-
#translate_enum(*args, &block) ⇒ Object
Simple way to translate enum.
Class Method Details
.define_set_to_enum_method(extended_class, enum_plural) ⇒ Object
46 47 48 49 50 51 52 53 54 55 |
# File 'lib/enum_ext.rb', line 46 def define_set_to_enum_method( extended_class, enum_plural) # ext_sets_to_kinds( :ready_for_shipment, :delivery_set ) --> [:ready_for_shipment, :on_delivery, :delivered] extended_class.define_singleton_method("ext_sets_to_#{enum_plural}") do |*enum_or_sets| return [] if enum_or_sets.blank? enum_or_sets_strs = enum_or_sets.map(&:to_s) next_level_deeper = try("ext_#{enum_plural}").slice( *enum_or_sets_strs ).values.flatten (enum_or_sets_strs & send(enum_plural).keys | send("ext_sets_to_#{enum_plural}", *next_level_deeper)).uniq end end |
.define_summary_methods(extended_class, enum_plural) ⇒ Object
57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
# File 'lib/enum_ext.rb', line 57 def define_summary_methods(extended_class, enum_plural) extended_class.define_singleton_method("ext_#{enum_plural}") do @enum_ext_summary ||= ActiveSupport::HashWithIndifferentAccess.new end unless respond_to?("ext_#{enum_plural}") extended_class.define_singleton_method("all_#{enum_plural}") do { **send(enum_plural), "ext_#{enum_plural}": { **send("ext_#{enum_plural}") } } unless respond_to?("all_#{enum_plural}") end end |
Instance Method Details
#enum(definitions) ⇒ Object
extending enum with inplace settings enum status: {}, ext: [:enum_i, :mass_assign_enum, :enum_multi_scopes] enum_i and mass_assign_enum ara
76 77 78 79 80 81 82 83 84 |
# File 'lib/enum_ext.rb', line 76 def enum(definitions) extensions = definitions.delete(:ext) super(definitions).tap do definitions.each do |name,| [*extensions].each{|ext_method| send(ext_method, name) } end end end |
#enum_i(enum_name) ⇒ Object
Defines instance method a shortcut for getting integer value of an enum. for enum named ‘status’ will generate:
instance.status_i
90 91 92 93 94 |
# File 'lib/enum_ext.rb', line 90 def enum_i( enum_name ) define_method "#{enum_name}_i" do self.class.send("#{enum_name.to_s.pluralize}")[send(enum_name)].to_i end end |
#ext_enum_sets(enum_name, options = {}) ⇒ Object
Rem:
ext_enum_sets can be called twice defining a superposition of already defined sets ( considering previous example ):
ext_enum_sets :status, {
outside_warehouse: ( delivery_set_statuses - in_warehouse_statuses )... any other array operations like &, + and so can be used
}
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 |
# File 'lib/enum_ext.rb', line 164 def ext_enum_sets( enum_name, = {} ) enum_plural = enum_name.to_s.pluralize self.instance_eval do EnumExt.define_set_to_enum_method(self, enum_plural) EnumExt.define_summary_methods(self, enum_plural) puts(" ----------------DEPRECATION WARNING----------------\n - with/without_\#{enum_plural} are served now via multi_enum_scopes method, \n and will be removed from the ext_enum_sets in the next version!\n DEPRECATION\n multi_enum_scopes(enum_name)\n\n send(\"ext_\#{enum_plural}\").merge!( options.transform_values{ _1.map(&:to_s) } )\n\n options.each do |set_name, enum_vals|\n # set_name scope\n scope set_name, -> { where( enum_name => send(\"\#{set_name}_\#{enum_plural}\") ) } if respond_to?(:scope)\n\n # class.enum_set_values\n define_singleton_method( \"\#{set_name}_\#{enum_plural}\" ) do\n send(\"ext_sets_to_\#{enum_plural}\", *enum_vals)\n end\n\n # instance.set_name?\n define_method \"\#{set_name}?\" do\n send(enum_name) && self.class.send( \"\#{set_name}_\#{enum_plural}\" ).include?( send(enum_name) )\n end\n\n # t_... - are translation dependent methods\n # This one is a narrow case helpers just a quick subset of t_ enums options for a set\n # class.t_enums_options\n define_singleton_method( \"t_\#{set_name}_\#{enum_plural}_options\" ) do\n return [[\"Enum translations call missed. Did you forget to call translate \#{enum_name}\"]*2] unless respond_to?( \"t_\#{enum_plural}_options_raw\" )\n\n send(\"t_\#{enum_plural}_options_raw\", send(\"t_\#{set_name}_\#{enum_plural}\") )\n end\n\n # class.t_enums_options_i\n define_singleton_method( \"t_\#{set_name}_\#{enum_plural}_options_i\" ) do\n return [[\"Enum translations call missed. Did you forget to call translate \#{enum_name}\"]*2] unless respond_to?( \"t_\#{enum_plural}_options_raw_i\" )\n\n send(\"t_\#{enum_plural}_options_raw_i\", send(\"t_\#{set_name}_\#{enum_plural}\") )\n end\n\n # protected?\n # class.t_set_name_enums ( translations or humanizations subset for a given set )\n define_singleton_method( \"t_\#{set_name}_\#{enum_plural}\" ) do\n return [([\"Enum translations call missed. Did you forget to call translate \#{enum_name}\"]*2)].to_h unless respond_to?( \"t_\#{enum_plural}\" )\n\n send( \"t_\#{enum_plural}\" ).slice( *send(\"\#{set_name}_\#{enum_plural}\") )\n end\n end\n end\nend\n") unless respond_to?("with_#{enum_plural}") |
#human_attribute_name(name, options = {}) ⇒ Object
human_attribute_name is redefined for automation like this: p #attr_name ): p object.send(attr_name)
392 393 394 395 |
# File 'lib/enum_ext.rb', line 392 def human_attribute_name( name, = {} ) # if name starts from t_ and there is a column with the last part then ... name[0..1] == 't_' && column_names.include?(name[2..-1]) ? super( name[2..-1], ) : super( name, ) end |
#humanize_enum(*args, &block) ⇒ Object Also known as: localize_enum
if app doesn’t need internationalization, it may use humanize_enum to make enum user friendly
class Request
humanize_enum :status, {
#locale dependent example with pluralization and lambda:
payed: -> (t_self) { I18n.t("request.status.payed", count: t_self.sum ) }
#locale dependent example with pluralization and proc:
payed: Proc.new{ I18n.t("request.status.payed", count: self.sum ) }
#locale independent:
ready_for_shipment: "Ready to go!"
}
end
Could be called multiple times, all humanization definitions will be merged under the hood:
humanize_enum :status, {
payed: I18n.t("scope.#{status}")
} humanize_enum :status,
billed: I18n.t("scope.#{status")
}
Example with block:
humanize_enum :status do
I18n.t("scope.#status")
end
in views select:
f.select :status, Request.
in select in Active Admin filter
collection: Request.
Rem: select options breaks when using lambda() with params
Console:
request.sum = 3
request.payed!
request.status # >> payed
request.t_status # >> "Payed 3 dollars"
Request.t_statuses # >> { in_cart: -> { I18n.t("request.status.in_cart") }, .... }
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 |
# File 'lib/enum_ext.rb', line 304 def humanize_enum( *args, &block ) enum_name = args.shift localizations = args.pop enum_plural = enum_name.to_s.pluralize self.instance_eval do #t_enum define_method "t_#{enum_name}" do t = block || @@localizations.try(:with_indifferent_access)[send(enum_name)] if t.try(:lambda?) t.try(:arity) == 1 && t.call( self ) || t.try(:call) elsif t.is_a?(Proc) instance_eval(&t) else t end.to_s end @@localizations ||= {}.with_indifferent_access # if localization is abscent than block must be given @@localizations.merge!( localizations.try(:with_indifferent_access) || localizations || send(enum_plural).keys.map{|en| [en, Proc.new{ self.new({ enum_name => en }).send("t_#{enum_name}") }] }.to_h.with_indifferent_access ) #t_enums define_singleton_method( "t_#{enum_plural}" ) do @@localizations end #t_enums_options define_singleton_method( "t_#{enum_plural}_options" ) do send("t_#{enum_plural}_options_raw", send("t_#{enum_plural}") ) end #t_enums_options_i define_singleton_method( "t_#{enum_plural}_options_i" ) do send("t_#{enum_plural}_options_raw_i", send("t_#{enum_plural}") ) end define_method "t_#{enum_name}=" do |new_val| send("#{enum_name}=", new_val) end #protected? define_singleton_method( "t_#{enum_plural}_options_raw_i" ) do |t_enum_set| send("t_#{enum_plural}_options_raw", t_enum_set ).map do | key_val | key_val[1] = send(enum_plural)[key_val[1]] key_val end end define_singleton_method( "t_#{enum_plural}_options_raw" ) do |t_enum_set| t_enum_set.invert.to_a.map do | key_val | # since all procs in t_enum are evaluated in context of a record than it's not always possible to create select options if key_val[0].respond_to?(:call) if key_val[0].try(:arity) < 1 key_val[0] = key_val[0].try(:call) rescue "Cannot create option for #{key_val[1]} ( proc fails to evaluate )" else key_val[0] = "Cannot create option for #{key_val[1]} because of a lambda" end end key_val end end end end |
#mass_assign_enum(*enums_names) ⇒ Object Also known as: enum_mass_assign
Ex mass_assign_enum
Used for mass assigning for collection without callbacks it creates bang methods for collections using update_all. it’s often case when you need bulk update without callbacks, so it’s gets frustrating to repeat: some_scope.update_all(status: Request.statuses, update_at: Time.now)
If you need callbacks you can do like this: some_scope.each(&:new_stat!) but if you don’t need callbacks and you have lots of records to change at once you need update_all
mass_assign_enum( :status )
class methods:
in_cart! paid! in_warehouse! and so
Console: request1.in_cart! request2.waiting_for_payment! Request.with_statuses( :in_cart, :waiting_for_payment ).payed! request1.paid? # >> true request2.paid? # >> true request1.updated_at # >> Time.now
order.requests.paid.all?(&:paid?) # >> true order.requests.paid.delivered! order.requests.map(&:status).uniq #>> [:delivered]
247 248 249 250 251 252 253 254 255 256 257 |
# File 'lib/enum_ext.rb', line 247 def mass_assign_enum( *enums_names ) enums_names.each do |enum_name| enum_vals = self.send( enum_name.to_s.pluralize ) enum_vals.keys.each do |enum_el| define_singleton_method( "#{enum_el}!" ) do self.update_all( {enum_name => enum_vals[enum_el]}.merge( self.column_names.include?('updated_at') ? {updated_at: Time.now} : {} )) end end end end |
#multi_enum_scopes(enum_name) ⇒ Object
Defines two scopes for one for an inclusion: ‘WHERE enum IN( enum1, enum2 )`, and the second for an exclusion: `WHERE enum NOT IN( enum1, enum2 )`
Ex:
Request.with_statuses( :payed, :delivery_set ) # >> :payed and [:ready_for_shipment, :on_delivery, :delivered] requests
Request.without_statuses( :payed ) # >> scope for all requests with statuses not eq to :payed
Request.without_statuses( :payed, :in_warehouse ) # >> scope all requests with statuses not eq to :payed or :ready_for_shipment
103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 |
# File 'lib/enum_ext.rb', line 103 def multi_enum_scopes(enum_name) enum_plural = enum_name.to_s.pluralize self.instance_eval do # with_enums scope scope "with_#{enum_plural}", -> (*enum_list) { enum_list.blank? ? nil : where( enum_name => send("ext_sets_to_#{enum_plural}", *enum_list) ) } if !respond_to?("with_#{enum_plural}") && respond_to?(:scope) # without_enums scope scope "without_#{enum_plural}", -> (*enum_list) { enum_list.blank? ? nil : where.not( enum_name => send("ext_sets_to_#{enum_plural}", *enum_list) ) } if !respond_to?("without_#{enum_plural}") && respond_to?(:scope) EnumExt.define_set_to_enum_method(self, enum_plural) EnumExt.define_summary_methods(self, enum_plural) end end |
#translate_enum(*args, &block) ⇒ Object
Simple way to translate enum. It use either given scope as second argument, or generated activerecord.attributes.model_name_underscore.enum_name If block is given than no scopes are taken in consider
377 378 379 380 381 382 383 384 385 386 387 |
# File 'lib/enum_ext.rb', line 377 def translate_enum( *args, &block ) enum_name = args.shift enum_plural = enum_name.to_s.pluralize t_scope = args.pop || "activerecord.attributes.#{self.name.underscore}.#{enum_plural}" if block_given? humanize_enum( enum_name, &block ) else humanize_enum( enum_name, send(enum_plural).keys.map{|en| [ en, Proc.new{ I18n.t("#{t_scope}.#{en}") }] }.to_h ) end end |