locomotivecms/steam

View on GitHub
lib/locomotive/steam/adapters/memory/query.rb

Summary

Maintainability
A
25 mins
Test Coverage
require 'forwardable'

module Locomotive::Steam
  module Adapters
    module Memory

      class Query

        include Enumerable
        extend  Forwardable

        def_delegators :all, :each, :last, :to_s, :to_a, :empty?, :size

        alias :length :size
        alias :count :size

        attr_reader :conditions

        def initialize(dataset, locale = nil, &block)
          @dataset    = dataset
          @conditions = []
          @sorting    = nil
          @limit      = nil
          @offset     = 0
          @locale     = locale
          instance_eval(&block) if block_given?
        end

        def where(conditions = {})
          @conditions += conditions.map { |name, value| Condition.new(name, value, @locale) }
          self
        end

        def +(query)
          @conditions += query.conditions
          self
        end

        def order_by(*args)
          @sorting = Order.new(*args)
          self
        end

        def limit(num)
          @limit = num
          self
        end

        def offset(num)
          @offset = num
          self
        end

        def only(*fields)
          self
        end

        def ==(other)
          if other.kind_of? Array
            all == other
          else
            super
          end
        end

        def all
          # TODO: instrumentation here
          # Locomotive::Common::Logger.debug "[dataset][#{@dataset.name}] conditions = #{@conditions.map(&:inspect).join(' AND ')}"
          limited sorted(filtered)
        end

        def sorted(entries)
          return entries if @sorting.blank?

          entries.sort_by { |entry| @sorting.apply_to(entry, @locale) }
        end

        def limited(entries)
          return [] if @limit == 0
          return entries if @offset == 0 && @limit.nil?

          subentries = entries.drop(@offset || 0)
          if @limit.kind_of? Integer
            subentries.take(@limit)
          else
            subentries
          end
        end

        def filtered
          @dataset.all.dup.find_all do |entry|
            accepted = true

            @conditions.each do |_condition|
              unless _condition.matches?(entry)
                accepted = false
                break # no to go further
              end
            end

            accepted
          end
        end # filtered

        def self.key(name, operator)
          "#{name}.#{operator}"
        end

        def key(name, operator)
          self.class.key(name, operator)
        end

        alias :k :key

      end
    end
  end
end