senseobservationsystems/commonsense-ruby-lib

View on GitHub
lib/cs/end_point.rb

Summary

Maintainability
A
25 mins
Test Coverage
require 'cs/serializer'
require 'set'

module CS
  module EndPoint
    include CS::Serializer

    attr_accessor :session

    def initialize(hash={})
      from_hash(hash)
    end

    # generate a hash representation of this end point
    def to_parameters
      r = self.resource rescue nil
      r.nil? ? self.to_h(false) : { self.resource => self.to_h(false) }
    end

    def inspect
      inspection = self.to_h.collect {|k,v| "#{k}: #{v.inspect}"}.compact.join(", ")
      "#<#{self.class} #{inspection}>"
    end

    # get value of property name
    def parameter(name)
      self.instance_variable_get("@#{name}")
    end

    # Persist end point object to CS. It will create a new record on CS
    # if it's a new object or it will update the object. It will throw an exception
    # if it could not persist object to CS
    #
    # example for {Sensor} object:
    #
    #     sensor = client.sensors.build
    #     sensor.name = "accelerometer"
    #     sensor.display_name = "Accelerometer"
    #     sensor.device_type = "BMA123"
    #     sensor.pager_type = "email"
    #     sensor.data_type = "json"
    #     sensor.data_structure = {"x-axis" => "Float", "y-axis" => "Float", "z-axis" => "Float"}
    #
    #     sensor.save! # this will create new sensor on CS
    #     sensor.id # should give you the id of the sensor
    #
    #     sensor.name = "accelerometer edit"
    #     sensor.save! # this will update the sensor
    def save!(options={})
      check_session!

      if @id
        self.update!(options)
      else
        self.create!(options)
      end
    end

    # it will persist data to CS just like {#save!} but it will return false instead of exception
    # if it encouter error while persiting data
    def save(options={})
      save!(options) rescue false
    end

    # Create a new end point object to CS. It will raise an exception if there is an error
    #
    # example for {Sensor} object:
    #
    #     sensor = client.sensors.build
    #     sensor.name = "accelerometer"
    #     sensor.display_name = "Accelerometer"
    #     sensor.device_type = "BMA123"
    #     sensor.pager_type = "email"
    #     sensor.data_type = "json"
    #     sensor.data_structure = {"x-axis" => "Float", "y-axis" => "Float", "z-axis" => "Float"}
    #
    #     sensor.create! # this will create new sensor on CS
    #     sensor.id # should give you the id of the sensor
    def create!(options={})
      parameter = self.to_parameters
      parameter.merge!(options)
      res = session.post(post_url, parameter)

      if session.response_code != 201
        errors = session.errors rescue nil
        raise Error::ResponseError, errors
      end

      location_header = session.response_headers["location"]
      id = scan_header_for_id(location_header)
      self.id = id[0] if id

      true
    end

    # Create a new endpoint object to CS, just like {#create!} but it will return false
    # if there is an error.
    def create(options={})
      create!(options) rescue false
    end

    # Retrieve Data from CS of the current object based on the id of the object.
    # It will return an exception if there is an error
    #
    # example for {Sensor} object:
    #
    #     sensor = client.sensors.build
    #     sensor.id = "1"
    #     sensor.retrieve!
    def retrieve!
      check_session!
      raise Error::ResourceIdError unless @id

      res = session.get(get_url)
      if session.response_code != 200
        errors = session.errors rescue nil
        raise Error::ResponseError, errors
      end

      from_hash(res[resource.to_s]) if res
      true
    end

    # alias for {#retrieve!}
    def reload!
      retieve!
    end

    # it will retrieve / reload current object form CS, just like {#retrieve!} but it
    # will return false instead of raise an exception if there is an error.
    def retrieve
      retrieve! rescue false
    end

    # alias for {#retrieve}
    def reload
      retrieve
    end

    # Update current end point object to CS. It will throw an exception if there is an error
    #
    # example for {Sensor} object:
    #
    #     sensor = client.sensors.find(1)
    #     sensor.name = "new name"
    #     sensor.update!
    def update!(options={})
      check_session!
      raise Error::ResourceIdError unless @id

      parameter = self.to_parameters
      parameter.merge!(options)
      res = session.put(put_url, parameter)

      if session.response_code != 200
        errors = session.errors rescue nil
        raise Error::ResponseError, errors
      end

      true
    end

    # Update current end point object to CS, just like {#update!} but it will return nil
    # if there is an error
    def update(options={})
      update!(options) rescue false
    end


    # Delete the current end point object from CS. It will throw an exception if there is an error
    #
    # example for {Sensor} object:
    #
    #     sensor = client.sensors.find(1)
    #     sensor.name = "new name"
    #     sensor.delete!
    def delete!
      check_session!
      raise Error::ResourceIdError unless @id

      res = session.delete(delete_url)

      if session.response_code != 200
        errors = session.errors rescue nil
        raise Error::ResponseError, errors
      end

      self.id = nil

      true
    end

    # Delete the current end point object from CS, just like {#delete!} but it will return nil
    # if there is an error
    def delete
      delete! rescue false
    end

    # return the commonsense URL for method
    # vaild value for method is `:get`, `:post`, `:put`, or `:delete`
    def url_for(method, id=nil)
      raise Error::ResourcesError if resources.nil?
      url = self.class.class_variable_get("@@#{method}_url".to_sym)
      url = url.sub(":id", "#{@id}") if id
      url
    end

    def duplicate
      clone = self.dup
      clone.id = nil
      clone
    end

    protected
    def scan_header_for_id(location_header)
      location_header.scan(/.*\/#{resources}\/(.*)/)[0] if location_header
    end

    def post_url
      url_for(:post)
    end

    def get_url
      url_for(:get, self.id)
    end

    def put_url
      url_for(:put, self.id)
    end

    def delete_url
      url_for(:delete, self.id)
    end

    def self.included(base)
      base.extend(ClassMethod)
    end

    def resource
      self.class.class_variable_get(:@@resource)
    rescue
      raise Error::ResourceError, "'resource' is not set up for class : #{self.class}"
    end

    def resources
      self.class.class_variable_get(:@@resources)
    end

    private
    def check_session!
      raise Error::SessionEmptyError unless @session
    end

    module ClassMethod
      def attribute(*args)
        attr_accessor *args

        unless @attribute_set
          @attribute_set = Set.new([:id])
          attr_accessor :id
        end
        @attribute_set.merge(args)
      end

      def attribute_set
        @attribute_set
      end

      def resources(resources)
        class_variable_set(:@@resources, resources)
        class_variable_set(:@@post_url, "/#{resources}.json")
        class_variable_set(:@@get_url, "/#{resources}/:id.json")
        class_variable_set(:@@put_url, "/#{resources}/:id.json")
        class_variable_set(:@@delete_url, "/#{resources}/:id.json")
      end

      def resources_name
        class_variable_get(:@@resources)
      end

      def resource(resource)
        class_variable_set(:@@resource, resource)
      end

      def resource_name
        class_variable_get(:@@resource)
      end
    end
  end
end