Module: ActiveRecordCompose::Attributes

Extended by:
ActiveSupport::Concern
Includes:
ActiveModel::Attributes
Included in:
Inspectable, Model
Defined in:
lib/active_record_compose/attributes.rb,
lib/active_record_compose/attributes/querying.rb,
lib/active_record_compose/attributes/delegation.rb,
lib/active_record_compose/attributes/attribute_predicate.rb

Overview

Provides attribute-related functionality for use within ActiveRecordCompose::Model.

This module allows you to define attributes on your composed model, including support for query methods (e.g., #attribute?) and delegation of attributes to underlying ActiveRecord instances via macros.

For example, .delegate_attribute defines attribute accessors that delegate to a specific model, similar to:

delegate :name, :name=, to: :account

Additionally, delegated attributes are included in the composed model's #attributes hash.

Examples:

class AccountRegistration < ActiveRecordCompose::Model
  def initialize(, attributes = {})
    @account = 
    super(attributes)
    models.push()
  end

  attribute :original_attribute, :string, default: "qux"
  delegate_attribute :name, to: :account

  private

  attr_reader :account
end

 = Account.new
.name = "foo"

registration = AccountRegistration.new()
registration.name         # => "foo" (delegated)
registration.name?        # => true  (delegated attribute method + `?`)

registration.name = "bar" # => updates account.name
.name              # => "bar"
.name?             # => true

registration.attributes
# => { "original_attribute" => "qux", "name" => "bar" }

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.attribute_namesArray<String>

Returns a array of attribute name. Attributes declared with delegate_attribute are also merged.

Returns:

  • (Array<String>)

    array of attribute name.

See Also:



100
# File 'lib/active_record_compose/attributes.rb', line 100

def attribute_names = super + delegated_attributes.to_a.map { _1.attribute_name }

.delegate_attribute(*attributes, to:, allow_nil: false) ⇒ void

Provides a method of attribute access to the encapsulated model.

It provides a way to access the attributes of the model it encompasses, allowing transparent access as if it had those attributes itself.

Examples:

Basic usage

delegate_attribute :name, :email, to: :profile

Allowing nil

delegate_attribute :bio, to: :profile, allow_nil: true

Parameters:

  • attributes (Array<Symbol, String>)

    attributes A variable-length list of attribute names to delegate.

  • to (Symbol, String)

    The target object to which attributes are delegated (keyword argument).

  • allow_nil (Boolean) (defaults to: false)

    allow_nil Whether to allow nil values. Defaults to false.

See Also:

  • for similar behavior in ActiveSupport


84
85
86
87
88
89
90
91
92
93
# File 'lib/active_record_compose/attributes.rb', line 84

def delegate_attribute(*attributes, to:, allow_nil: false)
  if to.start_with?("@")
    raise ArgumentError, "Instance variables cannot be specified in delegate to. (#{to})"
  end

  delegations = attributes.map { Delegation.new(attribute: _1, to:, allow_nil:) }
  delegations.each { _1.define_delegated_attribute(self) }

  self.delegated_attributes = (delegated_attributes.to_a + delegations).reverse.uniq { _1.attribute }.reverse
end

Instance Method Details

#_require_attributes_initializedvoid (private)



174
175
176
177
178
179
180
181
# File 'lib/active_record_compose/attributes.rb', line 174

def _require_attributes_initialized
  unless @attributes
    raise ActiveRecordCompose::UninitializedAttribute,
          "No attributes have been set. Is proper initialization performed, such as calling `super` in `initialize`?"
  end

  yield
end

#_write_attributevoid (private)

steep:ignore



170
# File 'lib/active_record_compose/attributes.rb', line 170

def _write_attribute(...) = _require_attributes_initialized { super } # steep:ignore

#attributevoid (private)



172
# File 'lib/active_record_compose/attributes.rb', line 172

def attribute(...) = _require_attributes_initialized { super }

#attribute_namesArray<String>

Returns a array of attribute name. Attributes declared with delegate_attribute are also merged.

class Foo < ActiveRecordCompose::Base
  def initialize(attributes = {})
    @account = Account.new
    super
  end

  attribute :confirmation, :boolean, default: false   # plain attribute
  delegate_attribute :name, to: :account              # delegated attribute

  private

  attr_reader :account
end

Foo.attribute_names                                   # Returns the merged state of plain and delegated attributes
# => ["confirmation" ,"name"]

foo = Foo.new
foo.attribute_names                                   # Similar behavior for instance method version
# => ["confirmation", "name"]

Returns:

  • (Array<String>)

    array of attribute name.

See Also:



131
132
133
134
135
# File 'lib/active_record_compose/attributes.rb', line 131

def attribute_names
  _require_attributes_initialized do
    super + delegated_attributes.to_a.map { _1.attribute_name }
  end
end

#attributesHash<String, Object>

Returns a hash with the attribute name as key and the attribute value as value. Attributes declared with delegate_attribute are also merged.

class Foo < ActiveRecordCompose::Base
  def initialize(attributes = {})
    @account = Account.new
    super
  end

  attribute :confirmation, :boolean, default: false   # plain attribute
  delegate_attribute :name, to: :account              # delegated attribute

  private

  attr_reader :account
end

foo = Foo.new
foo.name = "Alice"
foo.confirmation = true

foo.attributes                                        # Returns the merged state of plain and delegated attributes
# => { "confirmation" => true, "name" => "Alice" }

Returns:

  • (Hash<String, Object>)

    hash with the attribute name as key and the attribute value as value.



162
163
164
165
166
# File 'lib/active_record_compose/attributes.rb', line 162

def attributes
  _require_attributes_initialized do
    super.merge(*delegated_attributes.to_a.map { _1.attribute_hash(self) })
  end
end