### 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| %> <% 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.