class MetalArchives::Base

Abstract model class

Public Class Methods

new(attributes = {}) click to toggle source

Generic shallow copy constructor

# File lib/metal_archives/models/base.rb, line 11
def initialize(attributes = {})
  raise Errors::NotImplementedError, "no :id property in model" unless respond_to? :id

  set(**attributes)
end

Protected Class Methods

boolean(name, opts = {}) click to toggle source

Defines a model boolean property. This method is an alias for enum name, :values => [true, false]

name

Name of the property

opts
multiple

Whether or not the property has multiple values (which turns it into an Array of type)

# File lib/metal_archives/models/base.rb, line 220
def boolean(name, opts = {})
  enum name, opts.merge(values: [true, false])
end
cache(id) click to toggle source

Generate cache key for id

# File lib/metal_archives/models/base.rb, line 100
def cache(id)
  "#{self.class.name}//#{id}"
end
enum(name, opts) click to toggle source

Defines a model enum property.

name

Name of the property

opts
values

Required. An array of possible values

multiple

Whether or not the property has multiple values (which turns it into an Array of type)

# File lib/metal_archives/models/base.rb, line 172
def enum(name, opts)
  raise ArgumentError, "opts[:values] is required" unless opts && opts[:values]

  properties[name] = opts

  # property
  define_method(name) do
    load! unless loaded?

    instance_variable_get(:"@#{name}")
  end

  # property?
  define_method("#{name}?") do
    load! unless loaded? && instance_variable_defined?("@#{name}")

    property = instance_variable_get(:"@#{name}")
    property.respond_to?(:empty?) ? !property.empty? : !property.nil?
  end

  # property=
  define_method("#{name}=") do |value|
    # Check enum type
    if opts[:multiple]
      raise Errors::TypeError, "invalid enum value #{value}, must be Array for #{name}" unless value.is_a? Array

      value.each do |val|
        raise Errors::TypeError, "invalid enum value #{val} for #{name}" unless opts[:values].include? val
      end
    else
      raise Errors::TypeError, "invalid enum value #{value} for #{name}" unless opts[:values].include? value
    end

    instance_variable_set(:"@#{name}", value)
  end
end
properties() click to toggle source

Declared properties

# File lib/metal_archives/models/base.rb, line 93
def properties
  @properties ||= {}
end
property(name, opts = {}) click to toggle source

Defines a model property.

name

Name of the property

opts
type

Data type of property (a constant)

Default: String

multiple

Whether or not the property has multiple values (which turns it into an Array of type)

# File lib/metal_archives/models/base.rb, line 122
def property(name, opts = {})
  properties[name] = opts

  # property
  define_method(name) do
    # Load only when not loaded or id property
    load! unless loaded? || name == :id

    instance_variable_get(:"@#{name}")
  end

  # property?
  define_method("#{name}?") do
    send(name).present?
  end

  # property=
  define_method("#{name}=") do |value|
    return instance_variable_set(:"@#{name}", value) if value.nil?

    # Check value type
    type = opts[:type] || String
    if opts[:multiple]
      raise Errors::TypeError, "invalid type #{value.class}, must be Array for #{name}" unless value.is_a? Array

      value.each do |val|
        raise Errors::TypeError, "invalid type #{val.class}, must be #{type} for #{name}" unless val.is_a? type
      end
    else
      raise Errors::TypeError, "invalid type #{value.class}, must be #{type} for #{name}" unless value.is_a? type
    end

    instance_variable_set(:"@#{name}", value)
  end
end

Public Instance Methods

==(other) click to toggle source

Returns true if two objects have the same type and id

# File lib/metal_archives/models/base.rb, line 27
def ==(other)
  other.is_a?(self.class) &&
    id == other.id
end
cached?() click to toggle source

Whether or not the object is currently cached

# File lib/metal_archives/models/base.rb, line 63
def cached?
  loaded? && MetalArchives.cache.include?(self.class.cache(id))
end
inspect() click to toggle source

String representation

# File lib/metal_archives/models/base.rb, line 70
def inspect
  "#<#{self.class.name} @id=#{@id} @name=\"#{@name}\">"
end
load!() click to toggle source

Fetch, parse and load the data

Raises
# File lib/metal_archives/models/base.rb, line 39
def load!
  raise Errors::InvalidIDError, "no id present" unless id

  # Use constructor to set attributes
  set(**assemble)

  @loaded = true
  MetalArchives.cache[self.class.cache(id)] = self
rescue StandardError => e
  # Don't cache invalid requests
  MetalArchives.cache.delete self.class.cache(id)
  raise e
end
loaded?() click to toggle source

Whether or not the object is currently loaded

# File lib/metal_archives/models/base.rb, line 56
def loaded?
  @loaded ||= false
end
set(**attributes) click to toggle source

Set properties

# File lib/metal_archives/models/base.rb, line 20
def set(**attributes)
  attributes.each { |key, value| instance_variable_set(:"@#{key}", value) }
end

Protected Instance Methods

assemble() click to toggle source

Fetch the data and assemble the model

Override this method

Raises
# File lib/metal_archives/models/base.rb, line 85
def assemble
  raise Errors::NotImplementedError, "method :assemble not implemented"
end