e-travel/agnostic_backend

View on GitHub
lib/agnostic_backend/indexable/indexable.rb

Summary

Maintainability
A
1 hr
Test Coverage
module AgnosticBackend
  module Indexable

    class << self
      attr_reader :includers

      def indexable_class(index_name)
        includers.find { |klass| klass.index_name == index_name }
      end
    end

    def self.included(base)
      @includers ||= []
      @includers << base if @includers.none?{|klass| klass.name == base.name}
      base.send :include, InstanceMethods
      base.send :extend, ClassMethods
    end

    module ClassMethods

      def create_index
        AgnosticBackend::Indexable::Config.create_index_for(self)
      end

      def create_indices(include_primary: true)
        AgnosticBackend::Indexable::Config.create_indices_for(self,
                                                              include_primary: include_primary)
      end

      # establishes the convention for determining the index name from the class name
      def index_name(source=nil)
        (source.nil? ? name : source.to_s).split('::').last.underscore.pluralize
      end

      def _index_content_managers
        @__index_content_managers ||= {}
      end

      def index_content_manager(index_name)
        _index_content_managers[index_name.to_s]
      end

      def _index_root_notifiers
        @__index_root_notifiers ||= {}
      end

      def index_root_notifier(index_name)
        _index_root_notifiers[index_name.to_s]
      end

      def schema(for_index: nil, &block)
        index_name = for_index.nil? ? self.index_name : for_index
        manager = index_content_manager(index_name)
        raise "Index #{index_name} has not been defined for #{name}" if manager.nil?
        kv_pairs = manager.contents.map do |field_name, field|
          schema =
              if field.type.nested?
                field.from.map { |klass| klass.schema(for_index: index_name, &block) }.reduce(&:merge)
              elsif block_given?
                yield field.type
              else
                field.type.type
              end
          [field_name, schema]
        end
        Hash[kv_pairs]
      end

      # specifies which fields should be indexed for a given index_name
      # also sets up the manager for the specified index_name
      def define_index_fields(owner: nil, &block)
        return unless block_given?
        _index_content_managers[index_name(owner)] ||= ContentManager.new
        _index_content_managers[index_name(owner)].add_definitions &block
        unless instance_methods(false).include? :_index_content_managers
          define_method(:_index_content_managers) { self.class._index_content_managers }
        end
      end

      # specifies who should be notified when this object is saved
      def define_index_notifier(target: nil, &block)
        return unless block_given?
        _index_root_notifiers[index_name(target)] = block
        unless instance_methods(false).include? :_index_root_notifiers
          define_method(:_index_root_notifiers) { self.class._index_root_notifiers }
        end
      end
    end

    module InstanceMethods

      def index_name(source=nil)
        self.class.index_name(source)
      end

      def generate_document(for_index: nil, observer: nil)
        index_name = for_index.nil? ? self.index_name : for_index.to_s
        return unless respond_to? :_index_content_managers
        manager = _index_content_managers[index_name.to_s]
        raise "Index #{index_name} does not exist" if manager.nil?
        observer ||= AgnosticBackend::Indexable::ObjectObserver.new
        manager.extract_contents_from self, index_name, observer: observer
      end

      def put_to_index(index_name=nil)
        indexable_class = index_name.nil? ?
                            self.class :
                            AgnosticBackend::Indexable.indexable_class(index_name)

        indexable_class.create_indices.map do |index|
          indexer = index.indexer
          indexer.put(self)
        end
      end

      def index_object(index_name)
        put_to_index(index_name)
      end

      private

      def trigger_index_notification
        return unless respond_to? :_index_root_notifiers
        _index_root_notifiers.each do |index_name, block|
          obj = instance_eval &block
          obj = [obj] unless obj.respond_to? :each
          obj.each { |o| o.index_object(index_name) if o.present? }
        end
      end
    end
  end
end