class SkullIsland::Resource

A generic API resource TODO: Thread safety

Attributes

api_client[RW]
entity[R]
errors[R]

Public Class Methods

all(options = {}) click to toggle source
# File lib/skull_island/resource.rb, line 92
def self.all(options = {})
  # TODO: Add validations for options
  # TODO: add validation checks for the required pieces

  api_client = options[:api_client] || APIClient.instance

  root = 'data' # root for API JSON response data
  # TODO: do something with lazy requests...

  collection_entity = api_client.cache(relative_uri) do |client|
    client.get(relative_uri)[root]
  end

  ResourceCollection.new(
    collection_entity.collect do |record|
      unless options[:lazy]
        api_client.invalidate_cache_for "#{relative_uri}/#{record['id']}"
        api_client.cache("#{relative_uri}/#{record['id']}") do
          record
        end
      end
      new(
        entity: record,
        lazy: (options[:lazy] ? true : false),
        tainted: false,
        api_client: api_client
      )
    end,
    type: self,
    api_client: api_client
  )
end
cleanup_except(project, keep_these, from_these = nil) click to toggle source
# File lib/skull_island/resource.rb, line 183
def self.cleanup_except(project, keep_these, from_these = nil)
  old_resources = from_these || where(:project, project)

  old_resources.reject { |res| keep_these.include?(res.id) }.map do |res|
    puts "[WARN] ! Removing #{name} (#{res.id})"
    res.destroy
  end
end
find(attribute, value, options = {}) click to toggle source

Returns the first (and hopefully only) resource given some criteria This is a very crude helper and could be made much better

# File lib/skull_island/resource.rb, line 141
def self.find(attribute, value, options = {})
  results = where(attribute, value, options)
  raise Exceptions::AmbiguousFind, 'Found more than one result' if results.size > 1

  results.first
end
from_hash(hash, options = {}) click to toggle source
# File lib/skull_island/resource.rb, line 125
def self.from_hash(hash, options = {})
  # TODO: better options validations
  raise Exceptions::InvalidOptions unless options.is_a?(Hash)

  api_client = options[:api_client] || APIClient.instance

  new(
    entity: hash,
    lazy: true,
    tainted: true,
    api_client: api_client
  )
end
gen_getter_method(name, opts) click to toggle source
# File lib/skull_island/resource.rb, line 40
def self.gen_getter_method(name, opts)
  determine_getter_names(name, opts).each do |method_name|
    define_method(method_name) do
      name_as_string = name.to_s
      reload if @lazy && !@entity.key?(name_as_string)

      if opts[:postprocess]
        send("postprocess_#{name}".to_sym, @entity[name_as_string])
      else
        @entity[name_as_string]
      end
    end
  end
end
gen_property_methods() click to toggle source
# File lib/skull_island/resource.rb, line 75
def self.gen_property_methods
  properties.each do |prop, opts|
    # Getter methods
    next if opts[:id_property]

    gen_getter_method(prop, opts) unless opts[:write_only]

    # Setter methods (don't make one for obviously read-only properties)
    gen_setter_method(prop, opts) unless opts[:read_only]
  end
end
gen_setter_method(name, opts) click to toggle source
# File lib/skull_island/resource.rb, line 55
def self.gen_setter_method(name, opts)
  determine_setter_names(name, opts).each do |method_name|
    define_method(method_name) do |value|
      raise Exceptions::ImmutableModification if immutable?

      if opts[:validate] && !send("validate_#{name}".to_sym, value)
        raise Exceptions::InvalidArguments, name
      end

      @entity[name.to_s] = if opts[:preprocess]
                             send("preprocess_#{name}".to_sym, value)
                           else
                             value
                           end
      @tainted = true
      @modified_properties << name.to_sym
    end
  end
end
get(id, options = {}) click to toggle source
# File lib/skull_island/resource.rb, line 148
def self.get(id, options = {})
  # TODO: Add validations for options

  api_client = options[:api_client] || APIClient.instance

  if options[:lazy]
    new(
      entity: { 'id' => id },
      lazy: true,
      tainted: false,
      api_client: api_client
    )
  else
    entity_data = api_client.cache("#{relative_uri}/#{id}") do |client|
      client.get("#{relative_uri}/#{id}")
    end

    new(
      entity: entity_data,
      lazy: false,
      tainted: false,
      api_client: api_client
    )
  end
end
immutable(status) click to toggle source

Can this type of resource be changed client-side?

# File lib/skull_island/resource.rb, line 16
def self.immutable(status)
  raise Exceptions::InvalidArguments unless status.is_a?(TrueClass) || status.is_a?(FalseClass)

  @immutable = status
end
new(options = {}) click to toggle source
# File lib/skull_island/resource.rb, line 192
def initialize(options = {})
  # TODO: better options validations
  raise Exceptions::InvalidOptions unless options.is_a?(Hash)

  @entity = options[:entity] || {}

  # Allows lazy-loading if we're told this is a lazy instance
  #  This means only the minimal attributes were fetched.
  #  This shouldn't be set by end-users.
  @lazy = options.key?(:lazy) ? options[:lazy] : false
  # This allows local, user-created instances to be differentiated from fetched
  # instances from the backend API. This shouldn't be set by end-users.
  @tainted = options.key?(:tainted) ? options[:tainted] : true
  # This is the API Client used to get data for this resource
  @api_client = options[:api_client] || APIClient.instance
  @errors = {}
  # A place to store which properties have been modified
  @modified_properties = []

  validate_mutability
  validate_id

  self.class.class_eval { gen_property_methods }
end
property(name, options = {}) click to toggle source

Define a property for a model @!macro [attach] property

The $1 property

@todo add more validations on options and names

# File lib/skull_island/resource.rb, line 26
def self.property(name, options = {})
  @properties ||= {}

  invalid_prop_names = %i[
    > < = class def
    % ! / . ? * {}
    \[\]
  ]

  raise(Exceptions::InvalidProperty) if invalid_prop_names.include?(name.to_sym)

  @properties[name.to_sym] = options
end
relative_uri() click to toggle source

The URI (relative to the API base) for this object (or its index/list)

# File lib/skull_island/resource.rb, line 88
def self.relative_uri
  route_key
end
where(attribute, value, options = {}) click to toggle source
# File lib/skull_island/resource.rb, line 174
def self.where(attribute, value, options = {})
  # TODO: validate incoming options
  options[:comparison] ||= value.is_a?(Regexp) ? :match : '=='
  api_client = options[:api_client] || APIClient.instance
  all(lazy: (options[:lazy] ? true : false), api_client: api_client).where(
    attribute, value, comparison: options[:comparison]
  )
end

Public Instance Methods

relative_uri() click to toggle source
# File lib/skull_island/resource.rb, line 217
def relative_uri
  "#{self.class.relative_uri}/#{id}"
end

Private Instance Methods

raw_set(key, value) click to toggle source

A way to add things without preprocessing them

# File lib/skull_island/resource.rb, line 224
def raw_set(key, value)
  raise Exceptions::ImmutableModification if immutable?

  @entity[key.to_s] = value

  @modified_properties << key.to_sym
  @tainted = true
end