Files
ubicloud/lib/resource_methods.rb
Jeremy Evans e26412a41e Override Model.ubid_type before including ResourceMethods
This avoids an exception per class during class loading.

For FirewallsPrivateSubnets and CertsLoadBalancers, this defines
a ubid_type method when one was previously not defined.  This
method isn't used, since these models do not have an id field
currently.  I'm guessing these models only use ResourceMethods
for the before_destroy hook, so potentially, that could be moved
to a separate module, or we could use a trigger approach to
updating the archive table instead of a model hook approach.
2025-02-26 09:23:01 -08:00

144 lines
3.0 KiB
Ruby

# frozen_string_literal: true
module ResourceMethods
def self.included(base)
base.extend(ClassMethods)
base.instance_variable_set(:@ubid_format, /\A#{base.ubid_type}[a-z0-9]{24}\z/)
end
def freeze
ubid
super
end
def ubid
@ubid ||= UBID.from_uuidish(id).to_s.downcase
end
def to_s
inspect_prefix
end
def inspect_pk
ubid if id
end
def before_validation
set_uuid
super
end
def set_uuid
self.id ||= self.class.generate_uuid if new?
self
end
def validate
super
if self.class.ubid_format.match?(values[:name])
errors.add(:name, "cannot be exactly 26 numbers/lowercase characters starting with #{self.class.ubid_type} to avoid overlap with id format")
end
end
INSPECT_CONVERTERS = {
"uuid" => lambda { |v| UBID.from_uuidish(v).to_s },
"cidr" => :to_s.to_proc,
"inet" => :to_s.to_proc,
"timestamp with time zone" => lambda { |v| v.strftime("%F %T") }
}.freeze
def inspect_values
inspect_values = {}
sch = db_schema
@values.except(*self.class.redacted_columns).each do |k, v|
next if k == :id
inspect_values[k] = if v
if (converter = INSPECT_CONVERTERS[sch[k][:db_type]])
converter.call(v)
else
v
end
else
v
end
end
inspect_values.inspect
end
NON_ARCHIVED_MODELS = ["ArchivedRecord", "Semaphore"]
def before_destroy
model_name = self.class.name
unless NON_ARCHIVED_MODELS.include?(model_name)
model_values = values.merge(model_name: model_name)
encryption_metadata = self.class.instance_variable_get(:@column_encryption_metadata)
unless encryption_metadata.empty?
encryption_metadata.keys.each do |key|
model_values.delete(key)
end
end
ArchivedRecord.create(model_name: model_name, model_values: model_values)
end
super
end
module ClassMethods
attr_reader :ubid_format
# Adapted from sequel/model/inflections.rb's underscore, to convert
# class names into symbols
def self.uppercase_underscore(s)
s.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2').gsub(/([a-z\d])([A-Z])/, '\1_\2').tr("-", "_").upcase
end
def [](arg)
if arg.is_a?(UBID)
super(arg.to_uuid)
elsif arg.is_a?(String) && arg.bytesize == 26
begin
ubid = UBID.parse(arg)
rescue UBIDParseError
super
else
super(ubid.to_uuid)
end
else
super
end
end
def from_ubid(ubid)
self[id: UBID.parse(ubid).to_uuid]
rescue UBIDParseError
nil
end
def ubid_type
UBID.const_get("TYPE_#{ClassMethods.uppercase_underscore(name)}")
end
def generate_ubid
UBID.generate(ubid_type)
end
def generate_uuid
generate_ubid.to_uuid
end
def new_with_id(...)
new(...).set_uuid
end
def create_with_id(...)
create(...)
end
def redacted_columns
column_encryption_metadata.keys || []
end
end
end