locomotivecms/steam

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

Summary

Maintainability
A
1 hr
Test Coverage
module Locomotive::Steam
  module Adapters
    module MongoDB

      class Query

        SYMBOL_OPERATORS = %w(all elem_match exists gt gte in lt lte mod ne near near_sphere nin with_size with_type within_box within_circle within_polygon within_spherical_circle)

        attr_reader :criteria, :sort

        def initialize(scope, localized_attributes, &block)
          @criteria, @sort, @fields, @skip, @limit = {}, nil, nil, nil, nil
          @scope, @localized_attributes = scope, localized_attributes

          apply_default_scope

          instance_eval(&block) if block_given?
        end

        def where(criterion = nil)
          self.tap do
            @criteria.merge!(decode_symbol_operators(criterion)) unless criterion.nil?
          end
        end

        def order_by(*args)
          self.tap do
            @sort = decode_order_by(*args)
          end
        end

        def only(*args)
          self.tap do
            @fields = [*args]
          end
        end

        def offset(offset)
          self.tap { @skip = offset }
        end

        def limit(limit)
          self.tap { @limit = limit }
        end

        def against(collection)
          _query = to_origin
          selector, fields, sort = _query.selector, _query.options[:fields], _query.options[:sort]

          results = collection.find(selector)
          results = results.sort(sort)          if sort
          results = results.projection(fields)  if fields
          results = results.skip(@skip)         if @skip
          results = results.limit(@limit)       if @limit

          results
        end

        def to_origin
          build_origin_query.only(@fields).where(@criteria).order_by(*@sort)
        end

        def key(name, operator)
          :"#{name}".send(operator.to_sym)
        end

        alias :k :key

        private

        def build_origin_query
          ::Origin::Query.new(build_aliases(@localized_attributes, @scope.locale))
        end

        def build_aliases(localized_attributes, locale)
          localized_attributes.inject({}) do |aliases, name|
            aliases.tap do
              aliases[name.to_s] = "#{name}.#{locale}"
            end
          end
        end

        def apply_default_scope
          where(site_id: @scope.site._id) if @scope.site
        end

        def decode_symbol_operators(criterion)
          criterion.dup.tap do |_criterion|
            criterion.each do |key, value|
              next unless key.is_a?(String)

              _key, operator = key.split('.')

              if operator && SYMBOL_OPERATORS.include?(operator)
                _criterion.delete(key)
                _key = _key.to_s.to_sym.public_send(operator.to_sym)
                _criterion[_key] = value
              end
            end
          end
        end

        def decode_order_by(*spec)
          [*spec].compact.map do |arg|
            _decode_order_by(arg)
          end
        end

        def _decode_order_by(arg)
          case arg
          when String
            if arg.include?(',')
              _decode_order_by(arg.split(','))
            else
              arg.strip.split(/[\s|.]/)
            end
          when Array  then arg.map { |_arg| _decode_order_by(_arg) }
          else arg
          end
        end

      end

    end
  end
end