I’m building a project using a few ActiveModel APIs including ActiveModel::Attributes
. I have various objects that associate to each other via attributes, with custom ActiveModel types that support being cast from a small number of other objects (mainly String
). Mostly, things are working great, but I noticed that I can assign invalid attributes without raising exceptions until reading that attribute. For example:
class PlainText
def initialize(text:)
@text = text
end
def as_json(*)
{type: "plain_text", text: text}
end
end
class Types::PlainText < ActiveModel::Type::Value
def cast(value)
case value
when ::PlainText
value
when String
::PlainText.new(value)
else
raise ArgumentError, "Cannot implicitly cast `#{value}' to PlainText"
end
end
end
class Header
attribute :text, Types::PlainText.new
end
header = Header.new
header.text = "Hello, world!"
# => "Hello, world!"
header.text
# => #<PlainText @text="Hello, world!">
header.text = :this_is_invalid
# => :this_is_invalid
header.text
# => ArgumentError: Cannot implicitly cast `:this_is_invalid' to PlainText
Is there any way to have casting fail on write? I’m worried that I’ll have to override the setters for every attribute like this, which defeats a large purpose of using ActiveModel, IMO.