luk4s/mautic-rails

View on GitHub
lib/mautic/model.rb

Summary

Maintainability
A
35 mins
Test Coverage
A
98%
module Mautic
  # Virtual model for Mautic endpoint
  #   @see https://developer.mautic.org/#endpoints
  class Model < OpenStruct
    extend ActiveModel::Callbacks

    class MauticHash < Hash

      def []=(name, value)
        @changes ||= {}
        @changes[name] = value
        super
      end

      def changes
        @changes || {}
      end

    end

    class Attribute < OpenStruct

      def name
        @alias
      end

    end

    class << self

      def endpoint
        name.demodulize.underscore.pluralize
      end

      def in(connection)
        Proxy.new(connection, endpoint)
      end

    end

    define_model_callbacks :create, :update, :save

    attr_reader :connection
    attr_accessor :errors
    attr_writer :changed

    # @param [Mautic::Connection] connection
    def initialize(connection, hash = nil)
      @connection = connection
      @table = MauticHash.new
      self.attributes = { id: hash['id'], created_at: hash['dateAdded']&.to_time, updated_at: hash['dateModified']&.to_time } if hash
      assign_attributes(hash)
      clear_changes
    end

    def mautic_id
      "#{id}/#{@connection.id}"
    end

    def save(force = false)
      run_callbacks :save do
        id.present? ? update(force) : create
      end
    end

    def update(force = false)
      return false unless changed?

      begin
        run_callbacks :create do
          json = @connection.request((force && :put || :patch), "api/#{endpoint}/#{id}/edit", body: to_mautic)
          assign_attributes json[endpoint.singularize]
        end
        clear_changes
      rescue ValidationError => e
        self.errors = e.errors
      end

      errors.blank?
    end

    def update_columns(attributes = {})
      json = @connection.request(:patch, "api/#{endpoint}/#{id}/edit", body: to_mautic(attributes))
      assign_attributes json[endpoint.singularize]
      clear_changes
    rescue ValidationError => e
      self.errors = e.errors
    end

    def create
      begin
        run_callbacks :create do
          json = @connection.request(:post, "api/#{endpoint}/#{id && "#{id}/"}new", body: to_mautic)
          assign_attributes json[endpoint.singularize]
        end
        clear_changes
      rescue ValidationError => e
        self.errors = e.errors
      end

      errors.blank?
    end

    def destroy
      begin
        @connection.request(:delete, "api/#{endpoint}/#{id}/delete")
        true
      rescue RequestError => e
        self.errors = e.errors
        false
      end
    end

    def changes
      @table.changes
    end

    def changed?
      return @changed unless @changed.nil?

      @changed = !changes.empty?
    end

    def attributes
      @table.to_h
    end

    def attributes=(hash)
      hash.each_pair do |k, v|
        k = k.to_sym
        @table[k] = v
      end
    end

    def to_mautic(data = @table)
      data.transform_values do |val|
        if val.respond_to?(:to_mautic)
          val.to_mautic
        elsif val.is_a?(Array)
          val.join("|")
        else
          val
        end
      end
    end

    private

    def clear_changes
      @changed = nil
      @table.instance_variable_set(:@changes, nil)
    end

    def endpoint
      self.class.endpoint
    end

    def assign_attributes(source = nil)
      @mautic_attributes = []
      source ||= {}

      data = if (fields = source['fields'])
               attributes_from_fields(fields)
             elsif source
               source
             end
      self.attributes = data
    end

    def attributes_from_fields(fields)
      data = {}
      if fields['all']
        @mautic_attributes = fields['all'].collect do |key, value|
          data[key] = value
          Attribute.new(alias: key, value: value)
        end
      else
        fields.each do |_group, pairs|
          next unless pairs.is_a?(Hash)

          pairs.each do |key, attrs|
            @mautic_attributes << (a = Attribute.new(attrs))
            data[key] = a.value
          end
        end
      end

      data
    end

  end
end