Module: EnumExt
- Defined in:
- lib/enum_ext.rb,
lib/enum_ext/version.rb
Overview
Let’s assume we have model Request with enum status, and we have model Order with requests like this: class Request
extend EnumExt
belongs_to :order
enum status: { in_cart: 0, waiting_for_payment: 1, payed: 2, ready_for_shipment: 3, on_delivery: 4, delivered: 5 }
end
class Order
has_many :requests
end
Constant Summary collapse
- VERSION =
"0.4.5"
Instance Method Summary collapse
-
#enum_i(enum_name) ⇒ Object
defines shortcut for getting integer value of 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_wharehouse: ( 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 class Request humanize_enum :status, { #locale dependent example with pluralization and lambda: payed: -> (t_self) { I18n.t(“request.status.payed”, count: t_self.sum ) }.
-
#mass_assign_enum(*enums_names) ⇒ Object
Ex mass_assign_enum Used for mass assigning for collection without callbacks it creates bang methods for collections using update_all.
-
#translate_enum(*args, &block) ⇒ Object
Simple way to translate enum.
Instance Method Details
#enum_i(enum_name) ⇒ Object
defines shortcut for getting integer value of enum. for enum named ‘status’ will generate: instance.status_i
19 20 21 22 23 |
# File 'lib/enum_ext.rb', line 19 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_wharehouse: ( delivery_set_statuses - in_warehouse_statuses )... any other array operations like &, + and so can be used
}
72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 |
# File 'lib/enum_ext.rb', line 72 def ext_enum_sets( enum_name, = {} ) enum_plural = enum_name.to_s.pluralize self.instance_eval do define_singleton_method("ext_#{enum_plural}") do @enum_ext_summary ||= {} end unless respond_to?("ext_#{enum_plural}") send("ext_#{enum_plural}").merge!( ) # with_enums scope scope "with_#{enum_plural}", -> (sets_arr) { where( enum_name => self.send( enum_plural ).slice( *sets_arr.map{|set_name| self.try( "#{set_name}_#{enum_plural}" ) || set_name }.flatten.uniq.map(&:to_s) ).values ) } if !respond_to?("with_#{enum_plural}") && respond_to?(:scope) # without_enums scope scope "without_#{enum_plural}", -> (sets_arr) { where.not( id: self.send("with_#{enum_plural}", sets_arr) ) } if !respond_to?("without_#{enum_plural}") && respond_to?(:scope) .each do |set_name, enum_vals| # set_name scope scope set_name, -> { where( enum_name => self.send( enum_plural ).slice( *enum_vals.map(&:to_s) ).values ) } if respond_to?(:scope) # class.enum_set_values define_singleton_method( "#{set_name}_#{enum_plural}" ) do enum_vals end # class.enum_set_enums_i define_singleton_method( "#{set_name}_#{enum_plural}_i" ) do self.send( "#{enum_plural}" ).slice( *self.send("#{set_name}_#{enum_plural}") ).values end # t_... - are translation dependent methods # class.t_enums_options define_singleton_method( "t_#{set_name}_#{enum_plural}_options" ) do return [["Enum translations call missed. Did you forget to call translate #{enum_name}"]*2] unless respond_to?( "t_#{enum_plural}_options_raw" ) send("t_#{enum_plural}_options_raw", send("t_#{set_name}_#{enum_plural}") ) end # class.t_enums_options_i define_singleton_method( "t_#{set_name}_#{enum_plural}_options_i" ) do return [["Enum translations call missed. Did you forget to call translate #{enum_name}"]*2] unless respond_to?( "t_#{enum_plural}_options_raw_i" ) send("t_#{enum_plural}_options_raw_i", send("t_#{set_name}_#{enum_plural}") ) end # instance.set_name? define_method "#{set_name}?" do self.send(enum_name) && ( enum_vals.include?( self.send(enum_name) ) || enum_vals.include?( self.send(enum_name).to_sym )) end # protected? # class.t_setname_enums ( translations or humanizations subset for a given set ) define_singleton_method( "t_#{set_name}_#{enum_plural}" ) do return [(["Enum translations call missed. Did you forget to call translate #{enum_name}"]*2)].to_h unless respond_to?( "t_#{enum_plural}" ) send( "t_#{enum_plural}" ).slice( *self.send("#{set_name}_#{enum_plural}") ) end end end end |
#human_attribute_name(name, options = {}) ⇒ Object
human_attribute_name is redefined for automation like this: p #attr_name ): p object.send(attr_name)
306 307 308 309 |
# File 'lib/enum_ext.rb', line 306 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.t_statuses_options_i
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") }, .... }
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 |
# File 'lib/enum_ext.rb', line 218 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
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 defined?(Request::MassAssignEnum) # >> true
order.requests.paid.all?(&:paid?) # >> true order.requests.paid.delivered! order.requests.map(&:status).uniq #>> [:delivered]
163 164 165 166 167 168 169 170 171 172 173 |
# File 'lib/enum_ext.rb', line 163 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 |
#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
291 292 293 294 295 296 297 298 299 300 301 |
# File 'lib/enum_ext.rb', line 291 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 |