-
Notifications
You must be signed in to change notification settings - Fork 0
4. Array and Hash and other custom types
This gem uses ActiveModel::Attributes under the hood:
The ActiveModel::Attributes
module provides a powerful way to define attributes on non-ActiveRecord models, handling type casting and default values similarly to ActiveRecord.
Out of the box, ActiveModel::Attributes
supports the following primitive types, typically registered via symbols:
-
:string
- For standard string values. -
:text
- For longer text content. -
:integer
- For whole numbers. -
:float
- For floating-point numbers. -
:decimal
- For high-precision decimal numbers (suitable for currency). -
:boolean
- For true/false values (casts common representations like1
,'1'
,'t'
,'true'
,0
,'0'
,'f'
,'false'
). -
:date
- For date values (without time). -
:time
- For time values (without date). -
:datetime
- For timestamp values including both date and time. -
:binary
- For raw binary data.
If you need to handle attributes as specific data structures like Arrays or Hashes, or custom Value Objects, you must define and register custom types. ActiveModel doesn't include :array
or :hash
types by default.
Here's how you can implement custom types for Array and Hash attributes using JSON serialization:
Place the following class definitions in appropriate files, for example, app/types/array_type.rb
and app/types/hash_type.rb
:
app/types/array_type.rb
require 'active_model'
require 'json'
class ArrayType < ActiveModel::Type::Value
def cast(value)
case value
when String
begin
decoded = JSON.parse(value)
decoded.is_a?(Array) ? decoded : nil
rescue JSON::ParserError
nil
end
when Array
value
when nil
nil
else
nil
end
end
def serialize(value)
case value
when Array
value.to_json
when nil
nil
else
Array(value).to_json
end
end
def type
:array
end
end
app/types/hash_type.rb
require 'active_model'
require 'json'
class HashType < ActiveModel::Type::Value
def cast(value)
case value
when String
begin
decoded = JSON.parse(value)
decoded.is_a?(Hash) ? decoded : nil
rescue JSON::ParserError
nil
end
when Hash
value
when nil
nil
else
nil
end
end
def serialize(value)
case value
when Hash
value.to_json
when nil
nil
else
nil
end
end
def type
:hash
end
end
Register these custom types so ActiveModel knows about them. Create or modify an initializer file, for example, config/initializers/custom_types.rb
:
# Ensure the type classes are loaded if they aren't automatically
# (though Rails typically autoloads from app/types)
# require_relative '../../app/types/array_type' # Adjust path as necessary
# require_relative '../../app/types/hash_type' # Adjust path as necessary
ActiveModel::Type.register(:array, ArrayType)
ActiveModel::Type.register(:hash, HashType)
You can now use these custom types directly in your Form class alongside the other attributes:
class YourCustomForm
...
attribute :tags, :array, default: -> { [] }
attribute :config, :hash, default: -> { {} }
attribute :name, :string
...
end