module Acfs::Resource::Attributes

Acfs Attributes

Allows to specify attributes of a class with default values and type safety.

@example

class User < Acfs::Resource
  attribute :name, :string, default: 'Anon'
  attribute :age, :integer
  attribute :special, My::Special::Type
end

For each attribute a setter and getter will be created and values will be type casted when set.

Constants

Uuid

@api public

UUID attribute type. Use it in your model as an attribute type:

@example

class User < Acfs::Resource
  attribute :id, :uuid
end

Public Class Methods

new(*attrs) click to toggle source

@api public

Write default attributes defined in resource class.

@see write_attributes @see ClassMethods#attributes

Calls superclass method
# File lib/acfs/resource/attributes.rb, line 31
def initialize(*attrs)
  write_attributes self.class.attributes
  reset_changes
  super
end

Public Instance Methods

attributes() click to toggle source

@api public

Returns ActiveModel compatible list of attributes and values.

@example

class User < Acfs::Resource
  attribute :name, type: String, default: 'Anon'
end
user = User.new(name: 'John')
user.attributes # => { "name" => "John" }

@return [HashWithIndifferentAccess{Symbol => Object}]

Attributes and their values.

rubocop:disable Naming/MemoizedInstanceVariableName

# File lib/acfs/resource/attributes.rb, line 52
def attributes
  @_attrs ||= HashWithIndifferentAccess.new
end
attributes=(attributes) click to toggle source

@api public

Update all attributes with given hash. Attribute values will be casted to defined attribute type.

@example

user.attributes = { :name => 'Adam' }
user.name # => 'Adam'

@param [Hash{String, Symbol => Object}, each{|key, value|}]

Attributes to set in resource.

@see write_attributes Delegates attributes hash to {#write_attributes}.

# File lib/acfs/resource/attributes.rb, line 70
def attributes=(attributes)
  write_attributes(attributes)
end
read_attribute(name) click to toggle source

@api public

Read an attribute from instance variable.

@param [Symbol, String] name Attribute name. @return [Object] Attribute value.

# File lib/acfs/resource/attributes.rb, line 81
def read_attribute(name)
  attributes[name.to_s]
end
write_attribute(name, value, opts = {}) click to toggle source

@api public

Write single attribute with given value. Value will be casted to defined attribute type.

@param [String, Symbol] name Attribute name. @param [Object] value Value to write. @raise [ArgumentError] If no attribute with given name is defined.

# File lib/acfs/resource/attributes.rb, line 160
def write_attribute(name, value, opts = {})
  attr_type = self.class.defined_attributes[name.to_s]
  if attr_type
    write_raw_attribute name, attr_type.cast(value), opts
  else
    write_raw_attribute name, value, opts
  end
end
write_attributes(attributes, **opts) click to toggle source

@api public

Write a hash of attributes and values.

If attribute value is a `Proc` it will be evaluated in the context of the resource after all non-proc attribute values are set. Values will be casted to defined attribute type.

The behavior is used to apply default attributes from resource class definition.

@example

user.write_attributes name: 'john', email: ->{ "#{name}@example.org" }
user.name  # => 'john'
user.email # => 'john@example.org'

@param [Hash{String, Symbol => Object, Proc}, each{|key, value|}]

Attributes to write.

@see write_attribute Delegates attribute values to `#write_attribute`.

# File lib/acfs/resource/attributes.rb, line 106
def write_attributes(attributes, **opts)
  unless attributes.respond_to?(:each) && attributes.respond_to?(:keys)
    return false
  end

  if opts.fetch(:unknown, :ignore) == :raise &&
     (attributes.keys.map(&:to_s) - self.class.attributes.keys).any?
    missing = attributes.keys - self.class.attributes.keys
    missing.map!(&:inspect)
    raise ArgumentError.new "Unknown attributes: #{missing.join(', ')}"
  end

  procs = {}

  attributes.each do |key, _|
    if attributes[key].is_a? Proc
      procs[key] = attributes[key]
    else
      write_local_attribute(key, attributes[key], **opts)
    end
  end

  procs.each do |key, proc|
    write_local_attribute(key, instance_exec(&proc), **opts)
  end

  true
end
write_local_attribute(name, value, opts = {}) click to toggle source

@api private

Check if a public getter for attribute exists that should be called to write it or of {#write_attribute} should be called directly. This is necessary as {#write_attribute} should go though setters but can also handle unknown attribute that will not have a generated setter method.

# File lib/acfs/resource/attributes.rb, line 142
def write_local_attribute(name, value, opts = {})
  method = "#{name}="
  if respond_to? method, true
    public_send method, value
  else
    write_attribute name, value, opts
  end
end
write_raw_attribute(name, value, _ = {}) click to toggle source

@api private

Write an attribute without checking type or existence or casting value to attributes type. Value be stored in an instance variable named after attribute name.

@param [String, Symbol] name Attribute name. @param [Object] value Attribute value.

# File lib/acfs/resource/attributes.rb, line 178
def write_raw_attribute(name, value, _ = {})
  attributes[name.to_s] = value
end