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, :waiting_for_payment, :payed, :ready_for_shipment, :on_delivery, :delivered ]
end
class Order
has_many :requests
end
Constant Summary collapse
- VERSION =
"0.2.1"
Instance Method Summary collapse
- #enum_i(enum_name) ⇒ Object
-
#enum_translated?(name) ⇒ Boolean
helper to determine is attribute is translated enum.
-
#ext_enum_sets(enum_name, options) ⇒ Object
Rem: ext_enum_sets can be called twice defining a superpositoin of already defined sets: class Request …
-
#human_attribute_name(name, options = {}) ⇒ Object
It useful for Active Admin, since it use by default human_attribute_name to translate or humanize elements, if no translation given.
-
#humanize_enum(*args, &block) ⇒ Object
(also: #localize_enum)
if you need some substitution you can go like this localize_enum :status, { .. delivered: “Delivered at: %date” } request.delivered! request.t_status % Time.now.to_s # >> Delivered at: 05.02.2016.
-
#mass_assign_enum(*options) ⇒ Object
association_relation: true - Order.first.requests.scope.new_stat! - works but it wouldn’t works without ‘scope’ part! If you want to use it without ‘scope’ you may do it this way: class Request …
-
#translate_enum(*args, &block) ⇒ Object
Simple way to translate enum.
Instance Method Details
#enum_i(enum_name) ⇒ Object
16 17 18 19 20 |
# File 'lib/enum_ext.rb', line 16 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 |
#enum_translated?(name) ⇒ Boolean
helper to determine is attribute is translated enum
287 288 289 |
# File 'lib/enum_ext.rb', line 287 def enum_translated?( name ) translated_enums.include?( name.to_sym ) end |
#ext_enum_sets(enum_name, options) ⇒ Object
Rem:
ext_enum_sets can be called twice defining a superpositoin of already defined sets:
class Request
...
ext_enum_sets (... first time call )
ext_enum_sets :status, {
already_payed: ( [:payed] | delivery_set_statuses ),
outside_wharehouse: ( delivery_set_statuses - in_warehouse_statuses )... any other array operations like &, + and so can be used
}
63 64 65 66 67 68 69 70 71 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 |
# File 'lib/enum_ext.rb', line 63 def ext_enum_sets( enum_name, ) enum_plural = enum_name.to_s.pluralize self.instance_eval do .each do |set_name, enum_vals| scope set_name, -> { where( enum_name => self.send( enum_plural ).slice( *enum_vals.map(&:to_s) ).values ) } define_singleton_method( "#{set_name}_#{enum_plural}" ) do enum_vals end # set? 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 # t_set_enums define_singleton_method( "t_#{set_name}_#{enum_plural}" ) do send( "t_#{enum_plural}" ).slice( *self.send("#{set_name}_#{enum_plural}") ) end # t_set_enums_options define_singleton_method( "t_#{set_name}_#{enum_plural}_options" ) do send( "t_#{set_name}_#{enum_plural}" ).invert.to_a.map do | key_val | key_val[0] = key_val[0].call if key_val[0].respond_to?(:call) && key_val[0].try(:arity) < 1 key_val end end # 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 end 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 ) } unless respond_to?("with_#{enum_plural}") scope "without_#{enum_plural}", -> (sets_arr) { where.not( id: self.send("with_#{enum_plural}", sets_arr) ) } unless respond_to?("without_#{enum_plural}") end end |
#human_attribute_name(name, options = {}) ⇒ Object
It useful for Active Admin, since it use by default human_attribute_name to translate or humanize elements, if no translation given. So when enums translated it breaks default human_attribute_name since it’s search I18n scope from
282 283 284 |
# File 'lib/enum_ext.rb', line 282 def human_attribute_name( name, = {} ) enum_translated?(name) ? super( "t_#{name}", ) : super( name, ) end |
#humanize_enum(*args, &block) ⇒ Object Also known as: localize_enum
if you need some substitution you can go like this
localize_enum :status, {
..
delivered: "Delivered at: %{date}"
}
request.delivered!
request.t_status % {date: Time.now.to_s} # >> Delivered at: 05.02.2016
Using in select:
f.select :status, Request.
Rem: select options breaks when using lambda
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 |
# File 'lib/enum_ext.rb', line 224 def humanize_enum( *args, &block ) enum_name = args.shift localizations = args.pop enum_pural = enum_name.to_s.pluralize self.instance_eval do #t_enums define_singleton_method( "t_#{enum_pural}" ) do # if localization is abscent than block must be given localizations.try(:with_indifferent_access) || localizations || send(enum_pural).keys.map {|en| [en, self.new( {enum_name => en} ).send("t_#{enum_name}")] }.to_h.with_indifferent_access end #t_enums_options define_singleton_method( "t_#{enum_pural}_options" ) do send("t_#{enum_pural}").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 key_val[0] = ( key_val[0].try(:call) || "Cannot create option for #{key_val[0]}" ) if key_val[0].respond_to?(:call) && key_val[0].try(:arity) < 1 key_val end end #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 end end |
#mass_assign_enum(*options) ⇒ Object
association_relation: true - Order.first.requests.scope.new_stat! - works but it wouldn’t works without ‘scope’ part! If you want to use it without ‘scope’ you may do it this way: class Request
...
mass_assign_enum( :status, association_relation: false )
end class Order
has_many :requests, extend: Request::MassAssignEnum
end
Order.first.requests.respond_to?(:in_cart!) # >> true
Rem2: you can mass-assign more than one enum ::MassAssignEnum module will contain mass assign for both. It will break nothing since all enum name must be uniq across model
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/enum_ext.rb', line 159 def mass_assign_enum( * ) = ([-1].is_a?(Hash) && .pop || {relation: true, association_relation: true} ).with_indifferent_access enums_names = enums_names.each do |enum_name| enum_vals = self.send( enum_name.to_s.pluralize ) mass_ass_module = ( defined?(self::MassAssignEnum) && self::MassAssignEnum || Module.new ) mass_ass_module.instance_eval do enum_vals.keys.each do |enum_el| define_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 self.const_set( :MassAssignEnum, mass_ass_module ) unless defined?(self::MassAssignEnum) self::ActiveRecord_Relation.include( self::MassAssignEnum ) if [:relation] self::ActiveRecord_AssociationRelation.include( self::MassAssignEnum ) if [:association_relation] 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
265 266 267 268 269 270 271 272 273 274 275 276 277 |
# File 'lib/enum_ext.rb', line 265 def translate_enum( *args, &block ) enum_name = args.shift t_scope = args.pop || "activerecord.attributes.#{self.name.underscore}.#{enum_name}" translated_enums << enum_name.to_sym if block_given? humanize_enum( enum_name, &block ) else humanize_enum( enum_name, send(enum_name.to_s.pluralize).keys.map{|en| [ en, Proc.new{ I18n.t("#{t_scope}.#{en}") }] }.to_h ) end end |