helium/helium-ruby

View on GitHub
lib/helium/resource.rb

Summary

Maintainability
A
0 mins
Test Coverage
module Helium
  # Abstract base class for Helium Resources returned by the API
  class Resource
    attr_reader :id, :type, :params
    include Helium::Utils

    def initialize(opts = {})
      @client = opts.fetch(:client)
      @params = opts.fetch(:params)

      @id         = @params["id"]
      @type       = @params.dig('type')
      @created_at = @params.dig('meta', 'created')
      @updated_at = @params.dig('meta', 'updated')
    end

    class << self
      include Helium::Utils

      # The resource's index API route
      # @return [String] path to resource's index
      def all_path
        "/#{resource_name}"
      end

      def all(opts = {})
        client = opts.fetch(:client)
        Collection.new(klass: self, client: client).all
      end

      # Finds a single Resource by id
      # @param id [String] An id to find the Resource
      # @option opts [Client] :client A Helium::Client
      # @return [Resource]
      def find(id, opts = {})
        client = opts.fetch(:client)
        initialize_from_path(path: "/#{resource_name}/#{id}", client: client)
      end

      # Fetches a singleton resource (e.g. organization, user)
      # @option opts [Client] :client A Helium::Client
      # @return [Resource] A singleton resource
      def singleton(opts = {})
        client = opts.fetch(:client)
        initialize_from_path(path: all_path, client: client)
      end

      # Creates a new resource with given attributes
      # @param attributes [Hash] The attributes for the new Resource
      # @option opts [Client] :client A Helium::Client
      # @return [Resource]
      def create(attributes, opts = {})
        client = opts.fetch(:client)

        path = "/#{resource_name}"

        body = {
          data: {
            attributes: attributes,
            type: resource_name
          }
        }

        response = client.post(path, body: body)
        resource_data = JSON.parse(response.body)["data"]

        return self.new(client: client, params: resource_data)
      end

      def resource_name
        kebab_case(self.name.split('::').last)
      end

      def initialize_from_path(opts = {})
        client = opts.fetch(:client)
        path = opts.fetch(:path)

        response = client.get(path)
        resource_data = JSON.parse(response.body)["data"]
        return self.new(client: client, params: resource_data)
      end
    end # << self

    # Returns a path identifying the current resource. Can be overridden
    # in child classes to handle non-standard resources (e.g. Organization)
    # @return [String] path to resource
    def resource_path
      "/#{resource_name}/#{self.id}"
    end

    # Updates a Resource
    # @param attributes [Hash] The attributes to update
    # @return [Resource] The updated resource
    def update(attributes)
      body = {
        data: {
          attributes: attributes,
          id: self.id,
          type: resource_name
        }
      }

      response = @client.patch(resource_path, body: body)
      resource_data = JSON.parse(response.body)["data"]

      return self.class.new(client: @client, params: resource_data)
    end

    # Deletes the Resource
    # @return [Boolean] Whether the operation was successful
    def destroy
      @client.delete(resource_path)
    end

    def metadata
      Metadata.new(client: @client, klass: self)
    end

    # Override equality to use id for comparisons
    # @return [Boolean]
    def ==(other)
      self.id == other.id
    end

    # Override equality to use id for comparisons
    # @return [Boolean]
    def eql?(other)
      self == other
    end

    # Override equality to use id for comparisons
    # @return [Integer]
    def hash
      id.hash
    end

    # @return [DateTime, nil] when the resource was created
    def created_at
      return nil if @created_at.nil?
      @_created_at ||= DateTime.parse(@created_at)
    end

    # @return [DateTime, nil] when the resource was last updated
    def updated_at
      return nil if @updated_at.nil?
      @_updated_at ||= DateTime.parse(@updated_at)
    end

    # Inheriting resources should implement this with super
    # @return [Hash] a Hash of the object's attributes for JSON
    def as_json
      {
        id: id,
        type: type,
        created_at: created_at,
        updated_at: updated_at
      }
    end

    # @return [String] a JSON-encoded String representing the resource
    def to_json(*options)
      as_json.to_json(*options)
    end

    def resource_name
      kebab_case(self.class.name.split('::').last)
    end
  end
end