Allow for default values

This commit is contained in:
David Heinemeier Hansson
2025-11-28 16:35:25 -08:00
parent 593f8dd04c
commit f7c3aaa2a9
3 changed files with 36 additions and 6 deletions

View File

@@ -2,5 +2,5 @@ class Account < ApplicationRecord
include Joinable
has_one_attached :logo
has_settings restrict_room_creation_to_administrators: :boolean, max_invites: 10, name: "hero"
has_settings restrict_room_creation_to_administrators: false
end

View File

@@ -1,23 +1,24 @@
module ActiveRecord
class TypedJson
class SchematizedJson
def initialize(schema, data:)
@schema, @data = schema, data
update_data_with_schema_defaults
end
def assign_data_with_type_casting(new_data)
new_data.each { |key, value| send("#{key}=", value) }
new_data.each { |key, value| send "#{key}=", value }
end
private
def method_missing(method_name, *args, **kwargs)
key = method_name.to_s.remove(/(\?|=)/)
if key_type = @schema[key.to_sym]
if @schema.key? key.to_sym
if method_name.ends_with?("?")
@data[key].present?
elsif method_name.ends_with?("=")
value = args.first
@data[key] = ActiveModel::Type.lookup(key_type).cast(value)
@data[key] = lookup_schema_type_for(key).cast(value)
else
@data[key]
end
@@ -29,13 +30,34 @@ module ActiveRecord
def respond_to_missing?(method_name, include_private = false)
@schema.key?(method_name.to_s.remove(/[?=]/).to_sym) || super
end
def lookup_schema_type_for(key)
type_or_default_value = @schema[key.to_sym]
case type_or_default_value
when :boolean, :integer, :string
ActiveModel::Type.lookup(type_or_default_value)
when TrueClass, FalseClass
ActiveModel::Type.lookup(:boolean)
when Integer
ActiveModel::Type.lookup(:integer)
when String
ActiveModel::Type.lookup(:string)
else
raise "Only boolean, integer, or strings are allowed as JSON schema types"
end
end
def update_data_with_schema_defaults
@data.reverse_merge!(@schema.to_h { |k, v| [ k.to_s, v.is_a?(Symbol) ? nil : v ] })
end
end
class Base
class << self
def has_json(delegate: false, **schemas)
schemas.each do |name, schema|
define_method(name) { ActiveRecord::TypedJson.new(schema, data: self[name]) }
define_method(name) { ActiveRecord::SchematizedJson.new(schema, data: self[name]) }
define_method("#{name}=") { |data| send(name).assign_data_with_type_casting(data) }
schema.keys.each do |schema_key|
@@ -43,6 +65,9 @@ module ActiveRecord
define_method("#{schema_key}?") { send(name).send("#{schema_key}?") }
define_method("#{schema_key}=") { |value| send(name).send("#{schema_key}=", value) }
end if delegate
# Ensures default values are set before saving
before_save -> { send(name) }
end
end

View File

@@ -15,4 +15,9 @@ class AccountTest < ActiveSupport::TestCase
accounts(:signal).update!(settings: { "restrict_room_creation_to_administrators" => "false" })
assert_not accounts(:signal).reload.restrict_room_creation_to_administrators?
end
test "default settings" do
a = Account.create! name: "New account"
assert_equal({ "restrict_room_creation_to_administrators" => false }, a[:settings])
end
end