ubicloud/lib/resource_methods.rb
Jeremy Evans cd3ead3109 Support configurable labels for links on admin site
Previously, links always were displayed as ubids. This changes it
to call the admin_label method on the object. By default, this will
call name on the object if it is defined, otherwise, it will use
the ubid.

Use a custom link label of email for Account, since that should be
more useful than the account name.

This makes many object types automatically have useful link labels.
Objects that don't implement name can override admin_label
appropriately. Just be careful not to introduce an N+1 query issue
when doing so.
2025-09-04 02:37:23 +09:00

165 lines
3.5 KiB
Ruby

# frozen_string_literal: true
module ResourceMethods
def self.configure(model, etc_type: false, redacted_columns: nil, encrypted_columns: nil)
model.instance_exec do
extend UbidTypeEtcMethods if etc_type
@ubid_format = /\A#{ubid_type}[a-z0-9]{24}\z/
@encrypted_columns = Array(encrypted_columns).freeze
@redacted_columns = (Array(redacted_columns) + @encrypted_columns).freeze
unless @encrypted_columns.empty?
plugin :column_encryption do |enc|
@encrypted_columns.each do |col|
enc.column col
end
end
end
end
end
module UbidTypeEtcMethods
def ubid_type
UBID::TYPE_ETC
end
end
module InstanceMethods
def freeze
ubid
super
end
def ubid
@ubid ||= UBID.from_uuidish(id).to_s.downcase
end
def admin_label
defined?(name) ? name : ubid
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_hash
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
end
def inspect_values
inspect_values_hash.inspect
end
NON_ARCHIVED_MODELS = ["ArchivedRecord", "Semaphore"].freeze
def before_destroy
model_name = self.class.name
unless NON_ARCHIVED_MODELS.include?(model_name)
model_values = values.merge(model_name:)
self.class.encrypted_columns.each do |key|
model_values.delete(key)
end
ArchivedRecord.create(model_name:, model_values:)
end
super
end
end
module ClassMethods
attr_reader :ubid_format
attr_reader :encrypted_columns
attr_reader :redacted_columns
# 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 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 create_with_id(id, **)
obj = new(**)
obj.id = id
obj.save_changes
end
def new_with_id(...)
new(...).set_uuid
end
end
end