This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
require 'forwardable'
|
||||
|
||||
module Sawyer
|
||||
class Resource
|
||||
SPECIAL_METHODS = Set.new(%w(agent rels fields))
|
||||
attr_reader :_agent, :_rels, :_fields
|
||||
attr_reader :attrs
|
||||
include Enumerable
|
||||
extend Forwardable
|
||||
|
||||
# Initializes a Resource with the given data.
|
||||
#
|
||||
# agent - The Sawyer::Agent that made the API request.
|
||||
# data - Hash of key/value properties.
|
||||
def initialize(agent, data = {})
|
||||
@_agent = agent
|
||||
data, links = agent.parse_links(data)
|
||||
@_rels = Relation.from_links(agent, links)
|
||||
@_fields = Set.new
|
||||
@_metaclass = (class << self; self; end)
|
||||
@attrs = {}
|
||||
data.each do |key, value|
|
||||
@_fields << key
|
||||
@attrs[key.to_sym] = process_value(value)
|
||||
end
|
||||
@_metaclass.send(:attr_accessor, *data.keys)
|
||||
end
|
||||
|
||||
# Processes an individual value of this resource. Hashes get exploded
|
||||
# into another Resource, and Arrays get their values processed too.
|
||||
#
|
||||
# value - An Object value of a Resource's data.
|
||||
#
|
||||
# Returns an Object to set as the value of a Resource key.
|
||||
def process_value(value)
|
||||
case value
|
||||
when Hash then self.class.new(@_agent, value)
|
||||
when Array then value.map { |v| process_value(v) }
|
||||
else value
|
||||
end
|
||||
end
|
||||
|
||||
# Checks to see if the given key is in this resource.
|
||||
#
|
||||
# key - A Symbol key.
|
||||
#
|
||||
# Returns true if the key exists, or false.
|
||||
def key?(key)
|
||||
@_fields.include? key
|
||||
end
|
||||
|
||||
# Allow fields to be retrieved via Hash notation
|
||||
#
|
||||
# method - key name
|
||||
#
|
||||
# Returns the value from attrs if exists
|
||||
def [](method)
|
||||
send(method.to_sym)
|
||||
rescue NoMethodError
|
||||
nil
|
||||
end
|
||||
|
||||
# Allow fields to be set via Hash notation
|
||||
#
|
||||
# method - key name
|
||||
# value - value to set for the attr key
|
||||
#
|
||||
# Returns - value
|
||||
def []=(method, value)
|
||||
send("#{method}=", value)
|
||||
rescue NoMethodError
|
||||
nil
|
||||
end
|
||||
|
||||
def_delegators :attrs, :dig, :fetch
|
||||
|
||||
ATTR_SETTER = '='.freeze
|
||||
ATTR_PREDICATE = '?'.freeze
|
||||
|
||||
# Provides access to a resource's attributes.
|
||||
def method_missing(method, *args)
|
||||
attr_name, suffix = method.to_s.scan(/([a-z0-9\_]+)(\?|\=)?$/i).first
|
||||
if suffix == ATTR_SETTER
|
||||
@_metaclass.send(:attr_accessor, attr_name)
|
||||
@_fields << attr_name.to_sym
|
||||
send(method, args.first)
|
||||
elsif attr_name && @_fields.include?(attr_name.to_sym)
|
||||
value = @attrs[attr_name.to_sym]
|
||||
case suffix
|
||||
when nil
|
||||
@_metaclass.send(:attr_accessor, attr_name)
|
||||
value
|
||||
when ATTR_PREDICATE then !!value
|
||||
end
|
||||
elsif suffix.nil? && SPECIAL_METHODS.include?(attr_name)
|
||||
instance_variable_get "@_#{attr_name}"
|
||||
elsif attr_name && !@_fields.include?(attr_name.to_sym)
|
||||
nil
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
# Wire up accessor methods to pull from attrs
|
||||
def self.attr_accessor(*attrs)
|
||||
attrs.each do |attribute|
|
||||
class_eval do
|
||||
define_method attribute do
|
||||
@attrs[attribute.to_sym]
|
||||
end
|
||||
|
||||
define_method "#{attribute}=" do |value|
|
||||
@attrs[attribute.to_sym] = value
|
||||
end
|
||||
|
||||
define_method "#{attribute}?" do
|
||||
!!@attrs[attribute.to_sym]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def inspect
|
||||
to_attrs.respond_to?(:pretty_inspect) ? to_attrs.pretty_inspect : to_attrs.inspect
|
||||
end
|
||||
|
||||
def each(&block)
|
||||
@attrs.each(&block)
|
||||
end
|
||||
|
||||
# private
|
||||
def to_yaml_properties
|
||||
[:@attrs, :@_fields, :@_rels]
|
||||
end
|
||||
|
||||
def to_attrs
|
||||
hash = self.attrs.clone
|
||||
hash.keys.each do |k|
|
||||
if hash[k].is_a?(Sawyer::Resource)
|
||||
hash[k] = hash[k].to_attrs
|
||||
elsif hash[k].is_a?(Array) && hash[k].all?{|el| el.is_a?(Sawyer::Resource)}
|
||||
hash[k] = hash[k].collect{|el| el.to_attrs}
|
||||
end
|
||||
end
|
||||
hash
|
||||
end
|
||||
|
||||
alias to_hash to_attrs
|
||||
alias to_h to_attrs
|
||||
|
||||
def marshal_dump
|
||||
[@attrs, @_fields, @_rels]
|
||||
end
|
||||
|
||||
def marshal_load(dumped)
|
||||
@attrs, @_fields, @_rels = *dumped.shift(3)
|
||||
@_metaclass = (class << self; self; end)
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user