### Install All Gem Versions with Gemika
Source: https://github.com/makandra/active_type/blob/main/README.md
Use this rake task to install all gem versions compatible with your current Ruby version for development and testing.
```ruby
rake matrix:install
```
--------------------------------
### Controller for Nested Attributes Form
Source: https://github.com/makandra/active_type/blob/main/README.md
Example controller actions for handling a form that uses nested attributes defined with `nests_many`. It demonstrates initialization, saving, and redirection.
```ruby
class HolidaysController < ApplicationController
def edit
@holidays_form = HolidaysForm.new
end
def update
@holidays_form = HolidaysForm.new(params[:holidays_form])
if @holidays_form.save
redirect_to root_url, notice: "Success!"
else
render :edit
end
end
end
```
--------------------------------
### Add ActiveType Gem to Gemfile
Source: https://github.com/makandra/active_type/blob/main/README.md
Include this line in your Gemfile to add the ActiveType gem. Ensure you run `bundle install` afterwards.
```ruby
gem 'active_type'
```
--------------------------------
### Build Scope for New Records
Source: https://github.com/makandra/active_type/blob/main/README.md
Example of using `build_scope` with a proc to define criteria for building new records within a `nests_many` association.
```ruby
nests_many :documents, build_scope: proc { Document.where(:state => "fresh") }
```
--------------------------------
### Customizing Association Class Name
Source: https://github.com/makandra/active_type/blob/main/README.md
Example of using `change_association` to override the default class name for an association, mapping it to an `ActiveType::Record`.
```ruby
class SignUp < ActiveType::Record[User]
change_association :credentials, class_name: 'SignUpCredential'
end
```
--------------------------------
### Form for Nested Holidays
Source: https://github.com/makandra/active_type/blob/main/README.md
Example of using `form_for` with `fields_for` to handle nested `holidays` attributes in a Rails view.
```erb
<%= form_for @holidays_form, url: '/holidays', method: :put do |form| %>
<%= form.fields_for :holidays do |holiday_form| %>
- <%= holiday_form.text_field :date %>
<% end %>
<% end %>
```
--------------------------------
### Define Nested Attributes with nests_many
Source: https://github.com/makandra/active_type/blob/main/README.md
Use `nests_many` to manage collections of nested objects, similar to ActiveRecord's `accepts_nested_attributes_for`. This example shows how to handle a form for multiple `Holiday` records.
```ruby
class Holiday < ActiveRecord::Base
validates :date, presence: true
end
class HolidaysForm < ActiveType::Object
nests_many :holidays, reject_if: :all_blank, default: proc { Holiday.all }
end
```
--------------------------------
### Usage of SignUp Model
Source: https://context7.com/makandra/active_type/llms.txt
Instantiate and save a SignUp object. This demonstrates creating a user, sending a welcome email, and subscribing to a newsletter.
```ruby
sign_up = SignUp.new(
email: "new@example.com",
password: "secret123",
password_confirmation: "secret123",
terms_of_service: "1",
newsletter_opt_in: true
)
sign_up.save! # Creates user, sends welcome email, subscribes to newsletter
```
--------------------------------
### Default Association Initialization
Source: https://github.com/makandra/active_type/blob/main/README.md
Shows how to use the `default` option with a proc to initialize an association on first access.
```ruby
nests_many :documents, default: proc { Documents.all }
```
--------------------------------
### Basic Form Object Usage
Source: https://context7.com/makandra/active_type/llms.txt
Demonstrates creating and using a basic form object with associated models. Validation is also shown.
```ruby
category = Category.create!(name: "Electronics")
product = Product.create!(name: "Laptop", category: category)
form = ProductForm.new(
category_id: category.id,
product_id: product.id,
quantity: 5
)
form.category # => #
form.product # => #
form.category.name # => "Electronics"
# Validation works
form.category_id = nil
form.valid? # => false
form.errors[:category] # => ["must exist"]
```
--------------------------------
### Usage of Order Model with Defaults
Source: https://context7.com/makandra/active_type/llms.txt
Instantiate an Order object and observe the behavior of static and dynamic default attributes.
```ruby
# Usage
order = Order.new(items: 5, unit_price: 19.99)
order.currency # => "USD" (static default)
order.created_at # => 2024-01-15 10:30:00 (evaluated when first accessed)
order.reference # => "ORD-A1B2C3D4" (evaluated lazily)
order.total # => 99.95 (computed from other attributes)
# Defaults not used when value provided
order2 = Order.new(items: 3, unit_price: 10.00, currency: "EUR")
order2.currency # => "EUR"
```
--------------------------------
### Usage of Contact Model with Overridden Accessors
Source: https://context7.com/makandra/active_type/llms.txt
Demonstrate how overridden attribute accessors in the Contact model normalize email and phone inputs and format the name.
```ruby
# Usage
contact = Contact.new
contact.email = " USER@EXAMPLE.COM "
contact.email # => "user@example.com"
contact.phone = "(555) 123-4567"
contact.phone # => "5551234567"
contact.name # => "Anonymous"
contact.name = "john doe"
contact.name # => "John Doe"
```
--------------------------------
### Extended Model for Sign-Up Form
Source: https://context7.com/makandra/active_type/llms.txt
Define a SignUp model extending ActiveType::Record[User] to add virtual attributes, context-specific validations, and callbacks for user registration.
```ruby
class SignUp < ActiveType::Record[User]
attribute :password_confirmation, :string
attribute :terms_of_service, :boolean
attribute :newsletter_opt_in, :boolean, default: false
validates :password, confirmation: true
validates :terms_of_service, acceptance: true
after_create :send_welcome_email
after_create :subscribe_to_newsletter, if: :newsletter_opt_in
private
def send_welcome_email
UserMailer.welcome(self).deliver_later
end
def subscribe_to_newsletter
NewsletterService.subscribe(email)
end
end
```
--------------------------------
### Run Specs Against All Gemfiles
Source: https://github.com/makandra/active_type/blob/main/README.md
Execute this rake task to run all specs against all compatible Gemfiles, ensuring broad compatibility.
```ruby
rake matrix:spec
```
--------------------------------
### Run Specs Against a Single Gemfile
Source: https://github.com/makandra/active_type/blob/main/README.md
Specify a particular Gemfile to run specs against, useful for targeted testing or debugging.
```ruby
BUNDLE_GEMFILE=Gemfile bundle exec rspec spec
```
--------------------------------
### Casting with Force Option
Source: https://github.com/makandra/active_type/blob/main/README.md
Illustrates how to use the `force: true` option with `ActiveType.cast` to allow casting of records with changes in loaded associations.
```ruby
sign_up = ActiveType.cast(user, SignUp, force: true)
```
--------------------------------
### Casting a Single ActiveRecord Instance
Source: https://github.com/makandra/active_type/blob/main/README.md
Demonstrates casting a single `ActiveRecord` instance to an `ActiveType::Record` variant using `ActiveType.cast`.
```ruby
class User < ActiveRecord::Base
...
end
class SignUp < ActiveType::Record[User]
...
end
user = User.find(1)
sign_up = ActiveType.cast(user, SignUp)
sign_up.is_a?(SignUp) # => true
```
--------------------------------
### Extended Model for Profile Editing
Source: https://context7.com/makandra/active_type/llms.txt
Create a ProfileEditor model extending ActiveType::Record[User] for a different context, including validations for current password and clearing password if blank.
```ruby
class ProfileEditor < ActiveType::Record[User]
attribute :current_password, :string
validates :current_password, presence: true
validate :verify_current_password
before_save :clear_password_if_blank
private
def verify_current_password
unless authenticate(current_password)
errors.add(:current_password, "is incorrect")
end
end
def clear_password_if_blank
self.password = nil if password.blank?
end
end
```
--------------------------------
### Serialization and Marshalling
Source: https://context7.com/makandra/active_type/llms.txt
Demonstrates that ActiveType objects support YAML serialization and Marshal dump/load, preserving virtual attributes. `serializable_hash` and `attributes` methods are also shown.
```ruby
class Session < ActiveType::Object
attribute :user_id, :integer
attribute :token, :string
attribute :expires_at, :datetime
attribute :metadata, :string # Can store JSON string
end
# Create and populate
session = Session.new(
user_id: 123,
token: "abc123",
expires_at: 1.hour.from_now,
metadata: '{"ip": "127.0.0.1"}'
)
# Marshal serialization
serialized = Marshal.dump(session)
restored = Marshal.load(serialized)
restored.user_id # => 123
restored.token # => "abc123"
restored.metadata # => '{"ip": "127.0.0.1"}'
# YAML serialization
yaml = session.to_yaml
restored_yaml = YAML.load(yaml)
restored_yaml.user_id # => 123
# serializable_hash for JSON APIs
session.serializable_hash
# => {"user_id" => 123, "token" => "abc123", "expires_at" => ..., "metadata" => '{"ip": "127.0.0.1"}'}
# attributes method
session.attributes
# => {"user_id" => 123, "token" => "abc123", "expires_at" => ..., "metadata" => '{"ip": "127.0.0.1"}'}
```
--------------------------------
### Attribute Defaults with Static and Dynamic Values
Source: https://context7.com/makandra/active_type/llms.txt
Define attributes with default values, including static strings and dynamic defaults evaluated lazily via procs.
```ruby
class Order < ActiveType::Object
attribute :items, :integer
attribute :unit_price, :decimal
attribute :currency, :string, default: "USD".freeze
attribute :created_at, :datetime, default: proc { Time.current }
attribute :reference, :string, default: proc { generate_reference }
attribute :total, :decimal, default: proc { items.to_i * unit_price.to_d }
private
def generate_reference
"ORD-" + SecureRandom.hex(4).upcase
end
end
```
--------------------------------
### Nested Attributes Parameters (Hash)
Source: https://github.com/makandra/active_type/blob/main/README.md
Demonstrates the expected hash format for nested attributes when using `nests_many` or `nests_one` with `ActiveType`.
```ruby
{
'1' => { date: "new record's date" },
'2' => { id: '3', date: "existing record's date" }
}
```
--------------------------------
### Nested Attributes Parameters (Array)
Source: https://github.com/makandra/active_type/blob/main/README.md
Demonstrates the expected array format for nested attributes when using `nests_many` or `nests_one` with `ActiveType`.
```ruby
[
{ date: "new record's date" },
{ id: '3', date: "existing record's date" }
]
```
--------------------------------
### Define Tableless Models with ActiveType::Object
Source: https://context7.com/makandra/active_type/llms.txt
Use ActiveType::Object to create models without a database table. These models support virtual attributes, validations, callbacks, mass assignment, and type casting.
```ruby
class SignIn < ActiveType::Object
# Define virtual attributes with type casting
attribute :email, :string
attribute :password, :string
attribute :remember_me, :boolean
attribute :login_attempts, :integer
attribute :last_attempt, :datetime
# All standard validations work
validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
validates :password, presence: true, length: { minimum: 8 }
# Callbacks work (use before_save/after_save, not before_create/before_update)
before_save :normalize_email
after_save :log_sign_in_attempt
def authenticate
# Business logic here
user = User.find_by(email: email)
user&.authenticate(password)
end
private
def normalize_email
self.email = email.downcase.strip
end
def log_sign_in_attempt
Rails.logger.info("Sign in attempt for #{email}")
end
end
# Usage
sign_in = SignIn.new(email: "USER@EXAMPLE.COM", password: "secret123", remember_me: "1")
sign_in.email # => "USER@EXAMPLE.COM"
sign_in.remember_me # => true (automatically cast from string)
sign_in.remember_me? # => true (query method)
sign_in.valid? # => true
sign_in.save # => true (runs callbacks, returns true/false without DB)
# Mass assignment works
sign_in.attributes = { email: "new@example.com", password: "newpass123" }
# Constructor with attributes
sign_in = SignIn.new(email: "test@example.com", password: "password123")
# Validation errors work as expected
invalid_sign_in = SignIn.new(email: "invalid", password: "short")
invalid_sign_in.valid? # => false
invalid_sign_in.errors[:email] # => ["is invalid"]
invalid_sign_in.errors[:password] # => ["is too short (minimum is 8 characters)"]
```
--------------------------------
### Define a Model Inheriting from ActiveRecord::Base with ActiveType::Record
Source: https://github.com/makandra/active_type/blob/main/README.md
Use `ActiveType::Record` to create models that inherit from an existing `ActiveRecord::Base` class, adding custom validations and callbacks.
```ruby
class User < ActiveRecord::Base
# ...
end
class SignUp < ActiveType::Record[User]
# this inherits from User
validates :password, confirmation: true
after_create :send_confirmation_email
def send_confirmation_email
# this should happen on sign-up, but not when creating a user in tests etc.
end
# ...
end
```
--------------------------------
### Instantiate and Assign Attributes in ActiveType::Object
Source: https://github.com/makandra/active_type/blob/main/README.md
Attributes defined with `attribute` can be assigned via the constructor or mass-assignment and are automatically typecast.
```ruby
sign_in = SignIn.new(date_of_birth: "1980-01-01", accepted_terms: "1", account_type: AccountType::Trial.new)
sign_in.date_of_birth.class # Date
sign_in.accepted_terms? # true
```
--------------------------------
### Define Default Attribute Values
Source: https://github.com/makandra/active_type/blob/main/README.md
Set default values for attributes using a proc. The default value is lazily evaluated on the first read if no value has been explicitly set.
```ruby
class SignIn < ActiveType::Object
attribute :created_at, :datetime, default: proc { Time.now }
end
```
--------------------------------
### Define a Non-Database-Backed Model with ActiveType::Object
Source: https://github.com/makandra/active_type/blob/main/README.md
Use `ActiveType::Object` for models that do not require a database table. Define attributes using the `attribute` method, similar to ActiveRecord.
```ruby
class SignIn < ActiveType::Object
# this is not backed by a db table
attribute :username, :string
attribute :password, :string
validates :username, presence: true
validates :password, presence: true
# ...
end
```
--------------------------------
### Extend Existing ActiveRecord Models with ActiveType::Record[BaseClass]
Source: https://context7.com/makandra/active_type/llms.txt
Use ActiveType::Record[BaseClass] to create specialized versions of existing ActiveRecord models. This is useful for form objects, presenters, or context-specific behaviors that should not be part of the base model.
```ruby
# Base model
class User < ActiveRecord::Base
has_many :posts
validates :email, presence: true
end
```
--------------------------------
### Default Attribute Value Based on Other Attributes
Source: https://github.com/makandra/active_type/blob/main/README.md
Use a default proc that references other attributes of the object. The proc is evaluated in the context of the object, allowing access to other attribute values.
```ruby
class SignIn < ActiveType::Object
attribute :email, :string
attribute :nickname, :string, default: proc { email.split('@').first }
end
SignIn.new(email: "tobias@example.org").nickname # "tobias"
SignIn.new(email: "tobias@example.org", :nickname => "kratob").nickname # "kratob"
```
--------------------------------
### Dirty Tracking for Virtual Attributes
Source: https://context7.com/makandra/active_type/llms.txt
Shows how virtual attributes in ActiveType support dirty tracking, similar to regular ActiveRecord attributes. Use `*_changed?` and `*_was` methods to inspect changes.
```ruby
class Settings < ActiveType::Object
attribute :theme, :string, default: "light".freeze
attribute :notifications, :boolean, default: true
attribute :language, :string, default: "en".freeze
end
# Usage
settings = Settings.new
settings.changed? # => false
settings.theme = "dark"
settings.changed? # => true
settings.theme_changed? # => true
settings.theme_was # => "light"
settings.changes # => {"theme" => ["light", "dark"]}
settings.notifications = false
settings.changes # => {"theme" => ["light", "dark"], "notifications" => [true, false]}
# After save, changes are cleared
settings.save
settings.changed? # => false
settings.theme_changed? # => false
```
--------------------------------
### Inherit from ActiveType::Record
Source: https://github.com/makandra/active_type/blob/main/README.md
Create new classes that inherit from an existing ActiveType::Record class to build upon its functionality. This allows for hierarchical definitions of data structures.
```ruby
class SignUp < ActiveType::Record[User]
# ...
end
class SpecialSignUp < SignUp
# ...
end
```
--------------------------------
### Casting an ActiveRecord Relation
Source: https://github.com/makandra/active_type/blob/main/README.md
Shows how to cast an entire `ActiveRecord` relation (scope) to a relation of an `ActiveType::Record`.
```ruby
adult_users = User.where('age >= 18')
adult_sign_ups = ActiveType.cast(adult_users, SignUp)
sign_up = adult_sign_ups.find(1)
sign_up.is_a?(SignUp) # => true
```
--------------------------------
### Casting ActiveRecord Record to UserPresenter
Source: https://context7.com/makandra/active_type/llms.txt
Use ActiveType.cast to convert a single ActiveRecord User instance into a UserPresenter, adding presenter-specific behavior.
```ruby
class User < ActiveRecord::Base
has_many :posts
end
class UserPresenter < ActiveType::Record[User]
attribute :formatted_name, :string
after_initialize :set_formatted_name
def display_name
"#{first_name} #{last_name} (#{email})"
end
def after_cast(original_record)
# Called after casting with the original record
Rails.logger.info("Cast user #{original_record.id} to presenter")
end
private
def set_formatted_name
self.formatted_name = "#{first_name} #{last_name}"
end
end
# Cast a single record
user = User.find(1)
presenter = ActiveType.cast(user, UserPresenter)
presenter.is_a?(UserPresenter) # => true
presenter.display_name # => "John Doe (john@example.com)"
presenter.formatted_name # => "John Doe"
```
--------------------------------
### Define Attributes for ActiveType::Object
Source: https://github.com/makandra/active_type/blob/main/README.md
Define attributes for `ActiveType::Object` using the `attribute` method, specifying type or leaving it blank for virtual attributes.
```ruby
class SignIn < ActiveType::Object
attribute :email, :string
attribute :date_of_birth, :date
attribute :accepted_terms, :boolean
attribute :account_type
end
```
--------------------------------
### Add Virtual Attributes to Database Models with ActiveType::Record
Source: https://context7.com/makandra/active_type/llms.txt
Use ActiveType::Record to add virtual attributes to existing database-backed models. These attributes are not persisted but support type casting, validations, and dirty tracking.
```ruby
class User < ActiveType::Record
# Database columns are automatically available
# Virtual attributes are added with the attribute method
attribute :password_confirmation, :string
attribute :terms_accepted, :boolean
attribute :computed_full_name, :string
validates :password_confirmation, presence: true, on: :create
validates :terms_accepted, acceptance: true
before_save :compute_full_name
private
def compute_full_name
self.computed_full_name = "#{first_name} #{last_name}"
end
end
# Usage - virtual and persisted attributes work together
user = User.new(
first_name: "John", # persisted
last_name: "Doe", # persisted
email: "john@example.com", # persisted
password_confirmation: "secret", # virtual
terms_accepted: true # virtual
)
user.save!
# Virtual attributes accessible after save but not persisted
user.password_confirmation # => "secret"
user.terms_accepted # => true
# Reload clears virtual attributes
User.find(user.id).password_confirmation # => nil
# Attributes hash includes both persisted and virtual
user.attributes
# => { "id" => 1, "first_name" => "John", ..., "password_confirmation" => "secret", "terms_accepted" => true }
```
--------------------------------
### Define Nested Many Associations with nests_many
Source: https://context7.com/makandra/active_type/llms.txt
Use `nests_many` to handle multiple nested records. It supports Rails form helpers and `fields_for`. Configure with `scope`, `default`, `reject_if`, and `allow_destroy` options.
```ruby
class Holiday < ActiveRecord::Base
validates :date, presence: true
validates :name, presence: true
end
```
```ruby
class HolidaysForm < ActiveType::Object
nests_many :holidays,
scope: proc { Holiday },
default: proc { Holiday.all },
reject_if: :all_blank,
allow_destroy: true
def self.model_name
ActiveModel::Name.new(self, nil, "HolidaysForm")
end
end
```
```ruby
class HolidaysController < ApplicationController
def edit
@form = HolidaysForm.new
end
def update
@form = HolidaysForm.new(holidays_form_params)
if @form.save
redirect_to holidays_path, notice: "Holidays updated!"
else
render :edit
end
end
private
def holidays_form_params
params.require(:holidays_form).permit(
holidays_attributes: [:id, :date, :name, :_destroy]
)
end
end
```
```erb
# <%= form_for @form, url: holidays_path, method: :put do |f| %>
# <%= f.fields_for :holidays do |holiday_form|
# <%= holiday_form.text_field :name %>
# <%= holiday_form.date_field :date %>
# <%= holiday_form.check_box :_destroy %>
# <% end %>
<% end %>
```
```ruby
form = HolidaysForm.new
form.holidays_attributes = [
{ name: "New Year", date: "2024-01-01" },
{ name: "Christmas", date: "2024-12-25" }
]
form.holidays.map(&:name) # => ["New Year", "Christmas"]
form.save # Saves all nested records
```
```ruby
# Updating existing records
form.holidays_attributes = [
{ id: 1, name: "Updated Name" },
{ id: 2, _destroy: "1" } # Destroys record with id 2
]
```
--------------------------------
### Using ar_attribute for Rails Native Attributes
Source: https://context7.com/makandra/active_type/llms.txt
Illustrates how to use `ar_attribute` to access Rails' native `.attribute` method when it's overridden by ActiveType. This is useful for integrating custom type casting for database columns.
```ruby
class PasswordType < ActiveModel::Type::Value
def cast(value)
return nil if value.blank?
BCrypt::Password.create(value)
end
def serialize(value)
value.to_s
end
end
class SecureUser < ActiveType::Record
self.table_name = "users"
# Use Rails native attribute for database column with custom type
ar_attribute :password_digest, PasswordType.new
# Use ActiveType attribute for virtual attributes
attribute :password, :string
attribute :password_confirmation, :string
validates :password, confirmation: true
before_save :set_password_digest
private
def set_password_digest
self.password_digest = password if password.present?
end
end
# Usage
user = SecureUser.new(password: "secret123", password_confirmation: "secret123")
user.save!
user.password_digest # => BCrypt::Password instance
```
--------------------------------
### Force Option for ActiveType Casting
Source: https://context7.com/makandra/active_type/llms.txt
The `force: true` option allows modifying the original object during ActiveType casting. This is generally not recommended.
```ruby
user2 = User.find(2)
presenter2 = ActiveType.cast(user2, UserPresenter, force: true)
user2.email = "allowed@example.com" # Works but not recommended
```
--------------------------------
### Accessing Base Class of Extended Record
Source: https://context7.com/makandra/active_type/llms.txt
Retrieve the base class from which an ActiveType::Record extension was derived.
```ruby
SignUp.extended_record_base_class # => User
```
--------------------------------
### Define Single Nested Record with nests_one
Source: https://context7.com/makandra/active_type/llms.txt
Use `nests_one` to manage a single nested record. It accepts the same options as `nests_many` and supports cascading validations.
```ruby
class Address < ActiveRecord::Base
validates :street, :city, :zip, presence: true
end
```
```ruby
class OrderForm < ActiveType::Object
attribute :customer_name, :string
attribute :customer_email, :string
nests_one :shipping_address,
scope: proc { Address },
default: proc { Address.new(country: "US") }
nests_one :billing_address,
scope: proc { Address },
reject_if: proc { |attrs| attrs[:same_as_shipping] == "1" }
validates :customer_name, presence: true
end
```
```ruby
# Usage
order = OrderForm.new(
customer_name: "John Doe",
customer_email: "john@example.com"
)
```
```ruby
# Nested attribute assignment
order.shipping_address_attributes = {
street: "123 Main St",
city: "Boston",
zip: "02101"
}
```
```ruby
order.shipping_address.street # => "123 Main St"
order.shipping_address.country # => "US" (from default)
```
```ruby
# Validation cascades to nested records
order.valid? # => false (billing address missing)
order.errors["shipping_address.city"] # => [] (valid)
```
```ruby
order.save # Saves order form and shipping address
```
--------------------------------
### Safety Feature: Original Record Unusable After Casting
Source: https://context7.com/makandra/active_type/llms.txt
Demonstrates that the original ActiveRecord record becomes unusable after being cast to an ActiveType::Record variant, raising an error on mutation.
```ruby
# The original record becomes unusable after casting (safety feature)
begin
user.email = "new@example.com" # Raises ActiveType::MutationAfterCastError
rescue ActiveType::MutationAfterCastError => e
puts "Cannot modify original record after casting"
end
```
--------------------------------
### Override Attribute Accessors
Source: https://github.com/makandra/active_type/blob/main/README.md
Customize the behavior of attribute getters and setters by overriding them and using `super`. This allows for pre- or post-processing of attribute values.
```ruby
class SignIn < ActiveType::Object
attribute :email, :string
attribute :nickname, :string
def email
super.downcase
end
def nickname=(value)
super(value.titleize)
end
end
```
--------------------------------
### Extend ActiveRecord::Base with ActiveType::Record
Source: https://github.com/makandra/active_type/blob/main/README.md
Use ActiveType::Record to add virtual attributes to an ActiveRecord model. Virtual attributes are not persisted to the database.
```ruby
class SignUp < ActiveType::Record[User]
# ...
end
```
--------------------------------
### Overriding Attribute Getter for Email Normalization
Source: https://context7.com/makandra/active_type/llms.txt
Customize the email attribute's getter to automatically convert the value to lowercase and strip whitespace.
```ruby
class Contact < ActiveType::Object
attribute :email, :string
attribute :phone, :string
attribute :name, :string
# Override getter to normalize output
def email
super&.downcase&.strip
end
# Override setter to normalize input
def phone=(value)
# Remove all non-digit characters
super(value&.gsub(/\D/, ''))
end
# Override getter to compute value
def name
value = super
value.present? ? value.titleize : "Anonymous"
end
end
```
--------------------------------
### Belongs To with Virtual Attributes in ActiveType::Object
Source: https://context7.com/makandra/active_type/llms.txt
Demonstrates using `belongs_to` associations with virtual foreign key attributes within an `ActiveType::Object`. This allows defining associations on form objects.
```ruby
class Category < ActiveRecord::Base
end
```
```ruby
class Product < ActiveRecord::Base
end
```
```ruby
class ProductForm < ActiveType::Object
# Virtual foreign key attribute
attribute :category_id, :integer
attribute :product_id, :integer
# Associations using virtual attributes
belongs_to :category, optional: false
belongs_to :product, optional: true
attribute :quantity, :integer
attribute :notes, :string
validates :quantity, numericality: { greater_than: 0 }
end
```
--------------------------------
### Casting ActiveRecord Relation to UserPresenters
Source: https://context7.com/makandra/active_type/llms.txt
Convert an ActiveRecord relation of Users into an array of UserPresenter objects using ActiveType.cast.
```ruby
# Cast an entire relation
users = User.where(active: true)
presenters = ActiveType.cast(users, UserPresenter)
presenters.find(1).is_a?(UserPresenter) # => true
```
--------------------------------
### Overriding Attribute Setter for Phone Normalization
Source: https://context7.com/makandra/active_type/llms.txt
Customize the phone attribute's setter to remove all non-digit characters from the input value.
```ruby
# Override setter to normalize input
def phone=(value)
# Remove all non-digit characters
super(value&.gsub(/\D/, ''))
end
```
--------------------------------
### Access Original ActiveRecord Attribute Method
Source: https://github.com/makandra/active_type/blob/main/README.md
If you need to use Rails's native `.attribute` method, you can access it via `ar_attribute` to avoid conflicts with ActiveType's implementation.
```ruby
class User < ApplicationRecord
# use my custom type to serialize to the database
ar_attribute :password, MyPasswordType.new
end
```
--------------------------------
### Access Extended ActiveRecord Base Class
Source: https://github.com/makandra/active_type/blob/main/README.md
Retrieve the base ActiveRecord class that has been extended by ActiveType::Record. This can be accessed via the class method `extended_record_base_class`.
```ruby
SignUp.extended_record_base_class # => "User (...)"
```
```ruby
sign_up = SignUp.new
sign_up.class.extended_record_base_class # => "User (...)"
```
--------------------------------
### Modify Association Options with change_association
Source: https://context7.com/makandra/active_type/llms.txt
Use `change_association` to override association options for presenter classes without affecting the base class. This is useful for automatically casting associated records to presenter types.
```ruby
class Post < ActiveRecord::Base
belongs_to :author, class_name: "User"
has_many :comments
end
```
```ruby
class Comment < ActiveRecord::Base
belongs_to :post
end
```
```ruby
# Presenter classes
class CommentPresenter < ActiveType::Record[Comment]
def formatted_body
body.truncate(100)
end
end
```
```ruby
class PostPresenter < ActiveType::Record[Post]
# Change the comments association to return presenters
change_association :comments, class_name: "CommentPresenter"
# Can also change scopes
change_association :comments, -> { order(created_at: :desc) }, class_name: "CommentPresenter"
def summary
"#{title} by #{author.name}"
end
end
```
```ruby
# Usage
post = Post.find(1)
presenter = ActiveType.cast(post, PostPresenter)
```
```ruby
# Comments are now CommentPresenter instances
presenter.comments.first.is_a?(CommentPresenter) # => true
presenter.comments.first.formatted_body # => "This is a truncated..."
```
```ruby
# Base class unaffected
Post.find(1).comments.first.is_a?(Comment) # => true
```
--------------------------------
### Overriding Attribute Getter for Computed Name
Source: https://context7.com/makandra/active_type/llms.txt
Customize the name attribute's getter to return a titleized version of the name or 'Anonymous' if the name is not present.
```ruby
# Override getter to compute value
def name
value = super
value.present? ? value.titleize : "Anonymous"
end
```
=== COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.