Module: PersistentEnum
- Extended by:
- ActiveSupport::Concern
- Defined in:
- lib/persistent_enum.rb,
lib/persistent_enum/railtie.rb,
lib/persistent_enum/version.rb,
lib/persistent_enum/acts_as_enum.rb
Overview
Provide a database-backed enumeration between indices and symbolic values. This allows us to have a valid foreign key which behaves like a enumeration. Values are cached at startup, and cannot be changed.
Defined Under Namespace
Modules: ActsAsEnum, ClassMethods Classes: AbstractDummyModel, EnumSpec, EnumTableInvalid, MissingEnumTypeError, Railtie
Constant Summary collapse
- VERSION =
'1.2.4'
Class Method Summary collapse
-
.cache_constants(model, required_members, name_attr: 'name', required_attributes: nil, sql_enum_type: nil) ⇒ Object
Given an ‘enum-like’ table with (id, name, …) structure and a set of enum members specified as either [name, …] or {attr: val, …, …}, ensure that there is a row in the table corresponding to each name, and cache the models as constants on the model class.
-
.cache_records(model, name_attr: :name) ⇒ Object
Given an ‘enum-like’ table with (id, name, …) structure, load existing records from the database and cache them in constants on this class.
- .dummy_class(model, name_attr) ⇒ Object
Class Method Details
.cache_constants(model, required_members, name_attr: 'name', required_attributes: nil, sql_enum_type: nil) ⇒ Object
Given an ‘enum-like’ table with (id, name, …) structure and a set of enum members specified as either [name, …] or {attr: val, …, …}, ensure that there is a row in the table corresponding to each name, and cache the models as constants on the model class.
When using a database such as postgresql that supports native enumerated types, can additionally specify a native enum type to use as the primary key. In this case, the required members will be added to the native type by name using ‘ALTER TYPE` before insertion. This ensures that enum table ids will have predictable values and can therefore be used in database level constraints.
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 137 138 139 140 141 142 143 144 145 146 147 148 |
# File 'lib/persistent_enum.rb', line 109 def cache_constants(model, required_members, name_attr: 'name', required_attributes: nil, sql_enum_type: nil) # normalize member specification unless required_members.is_a?(Hash) required_members = required_members.each_with_object({}) { |c, h| h[c] = {} } end # Normalize symbols name_attr = name_attr.to_s required_members = required_members.each_with_object({}) do |(name, attrs), h| h[name.to_s] = attrs.transform_keys(&:to_s) end required_attributes = required_attributes.map(&:to_s) if required_attributes # We need to cope with (a) loading this class and (b) ensuring that all the # constants are defined (if not functional) in the case that the database # isn't present yet. If no database is present, create dummy values to # populate the constants. values = begin cache_constants_in_table(model, name_attr, required_members, required_attributes, sql_enum_type) rescue EnumTableInvalid => ex # If we're running the application in any way, under no circumstances # do we want to introduce the dummy models: crash out now. Our # conservative heuristic to detect a 'safe' loading outside the # application is whether there is a current Rake task. unless Object.const_defined?(:Rake) && Rake.try(:application)&.top_level_tasks.present? raise end # Otherwise, we want to try as hard as possible to allow the # application to be initialized enough to run the Rake task (e.g. # db:migrate). log_warning("Database table initialization error for model #{model.name}, "\ 'initializing constants with dummy records instead: ' + ex.) cache_constants_in_dummy_class(model, name_attr, required_members, required_attributes, sql_enum_type) end return cache_values(model, values, name_attr) end |
.cache_records(model, name_attr: :name) ⇒ Object
Given an ‘enum-like’ table with (id, name, …) structure, load existing records from the database and cache them in constants on this class
152 153 154 155 156 157 158 159 160 161 |
# File 'lib/persistent_enum.rb', line 152 def cache_records(model, name_attr: :name) if model.table_exists? values = model.scoped cache_values(model, values, name_attr) else puts "Database table for model #{model.name} doesn't exist, no constants cached." end rescue ActiveRecord::NoDatabaseError puts "Database for model #{model.name} doesn't exist, no constants cached." end |
.dummy_class(model, name_attr) ⇒ Object
163 164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/persistent_enum.rb', line 163 def dummy_class(model, name_attr) if model.const_defined?(:DummyModel, false) dummy_class = model::DummyModel unless dummy_class.superclass == AbstractDummyModel && dummy_class.name_attr == name_attr raise NameError.new("PersistentEnum dummy class type mismatch: '#{dummy_class.inspect}' does not match '#{model.name}'") end dummy_class else nil end end |