class Attributor::Model

Public Class Methods

check_option!(name, value) click to toggle source
Calls superclass method Attributor::Hash::check_option!
# File lib/attributor/types/model.rb, line 81
def self.check_option!(name, value)
  case name
  when :identity
    raise AttributorException, "Invalid identity type #{value.inspect}" unless value.is_a?(::Symbol)
    :ok # FIXME: ... actually do something smart, that doesn't break lazy attribute creation
  when :reference
    :ok # FIXME: ... actually do something smart
  when :dsl_compiler
    :ok # FIXME: ... actually do something smart
  when :dsl_compiler_options
    :ok
  else
    super
  end
end
define_accessors(name) click to toggle source

Define accessors for attribute of given name.

@param name [::Symbol] attribute name

# File lib/attributor/types/model.rb, line 58
def self.define_accessors(name)
  name = name.to_sym
  define_reader(name)
  define_writer(name)
end
define_reader(name) click to toggle source
# File lib/attributor/types/model.rb, line 64
    def self.define_reader(name)
      module_eval <<-RUBY, __FILE__, __LINE__ + 1
        def #{name}
          @contents[:#{name}]
        end
      RUBY
    end
define_writer(name) click to toggle source
# File lib/attributor/types/model.rb, line 72
def self.define_writer(name)
  context = ['assignment', "of(#{name})"].freeze
  module_eval do
    define_method(name.to_s + '=') do |value|
      set(name, value, context: context)
    end
  end
end
example(context = nil, **values) click to toggle source
# File lib/attributor/types/model.rb, line 101
def self.example(context = nil, **values)
  context ||= ["#{name || 'Struct'}-#{rand(10_000_000)}"]
  context = Array(context)

  if keys.any?
    result = new
    result.extend(ExampleMixin)

    result.lazy_attributes = example_contents(context, result, **values)
  else
    result = new
  end
  result
end
generate_subcontext(context, subname) click to toggle source
# File lib/attributor/types/model.rb, line 97
def self.generate_subcontext(context, subname)
  context + [subname]
end
inherited(klass) click to toggle source
# File lib/attributor/types/model.rb, line 31
def self.inherited(klass)
  k = key_type
  ka = key_attribute

  v = value_type
  va = value_attribute

  klass.instance_eval do
    @saved_blocks = []
    @options = {}
    @keys = {}
    @key_type = k
    @value_type = v

    @key_attribute = ka
    @value_attribute = va

    @requirements = []
    @cached_defaults = {}
    @error = false
  end
end
new(data = nil) click to toggle source
# File lib/attributor/types/model.rb, line 116
def initialize(data = nil)
  if data
    loaded = self.class.load(data)
    @contents = loaded.attributes
  else
    @contents = {}
  end
end

Public Instance Methods

attributes() click to toggle source
# File lib/attributor/types/model.rb, line 140
def attributes
  @contents
end
dump(context: Attributor::DEFAULT_ROOT_CONTEXT, **_opts) click to toggle source
# File lib/attributor/types/model.rb, line 165
def dump(context: Attributor::DEFAULT_ROOT_CONTEXT, **_opts)
  return CIRCULAR_REFERENCE_MARKER if @dumping
  @dumping = true

  attributes.each_with_object({}) do |(name, value), hash|
    attribute = self.class.attributes[name]

    # skip dumping undefined attributes
    unless attribute
      warn "WARNING: Trying to dump unknown attribute: #{name.inspect} with context: #{context.inspect}"
      next
    end

    hash[name.to_sym] = attribute.dump(value, context: context + [name], **_opts)
  end
ensure
  @dumping = false
end
method_missing(name, *args) click to toggle source
Calls superclass method
# File lib/attributor/types/model.rb, line 153
def method_missing(name, *args)
  attribute_name = name.to_s
  attribute_name.chomp!('=')

  if self.class.attributes.key?(attribute_name.to_sym)
    self.class.define_accessors(attribute_name)
    return __send__(name, *args)
  end

  super
end
respond_to_missing?(name, *) click to toggle source
Calls superclass method
# File lib/attributor/types/model.rb, line 144
def respond_to_missing?(name, *)
  attribute_name = name.to_s
  attribute_name.chomp!('=')

  return true if self.class.attributes.key?(attribute_name.to_sym)

  super
end
to_hash() click to toggle source

This allows the splatting of these instances into method calls (top level hash conversion only)

# File lib/attributor/types/model.rb, line 185
def to_hash
  @contents
end
validate(context = Attributor::DEFAULT_ROOT_CONTEXT) click to toggle source

TODO: memoize validation results here, but only after rejiggering how we store the context.

Two calls to validate() with different contexts should return get the same errors,
but with their respective contexts.
# File lib/attributor/types/model.rb, line 128
def validate(context = Attributor::DEFAULT_ROOT_CONTEXT)
  raise AttributorException, 'validation conflict' if @validating
  @validating = true

  context = [context] if context.is_a? ::String
  # Use the common, underlying attribute validation of the hash (which will use our _get_attr)
  # to know how to retrieve a value from a model (instead of a hash)
  validate_keys(context)
ensure
  @validating = false
end