locomotivecms/steam

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

Summary

Maintainability
A
0 mins
Test Coverage
module Locomotive::Steam
  module Adapters
    module Memory

      class Condition

        class UnsupportedOperator < StandardError; end

        OPERATORS = %i(== eq ne neq matches gt gte lt lte size all in nin).freeze

        attr_reader :field, :operator, :value

        def initialize(operator_and_field, value, locale)
          @locale = locale.try(:to_sym)
          @operator_and_field, @value = operator_and_field, value
          @operator, @field = :==, operator_and_field

          decode_operator_and_field!
        end

        def matches?(entry)
          entry_value = entry_value(entry)

          adapt_operator!(entry_value)

          case @operator
          when :==        then entry_value == @value
          when :eq        then entry_value == @value
          when :ne        then entry_value != @value
          when :neq       then entry_value != @value
          when :matches   then @value =~ entry_value
          when :gt        then entry_value && entry_value > @value
          when :gte       then entry_value && entry_value >= @value
          when :lt        then entry_value && entry_value < @value
          when :lte       then entry_value && entry_value <= @value
          when :size      then entry_value.size == @value
          when :all       then array_contains?([*@value], entry_value)
          when :in, :nin  then value_is_in_entry_value?(entry_value)
          else
            raise UnknownConditionInScope.new("#{@operator} is unknown or not implemented.")
          end
        end

        def inspect
          "#{field}#{operator != :== ? '.' : ' '}#{operator} #{value.inspect}"
        end

        protected

        def entry_value(entry)
          value = entry.send(@field) rescue nil # TODO: not safe to rely on rescue (hidden errors)

          if value.respond_to?(:translations)
            value[@locale]
          else
            value
          end
        end

        def decode_operator_and_field!
          if match = @operator_and_field.match(/^(?<field>[a-z0-9_-]+)\.(?<operator>.*)$/)
            @field    = match[:field].to_sym
            @operator = match[:operator].to_sym
            check_operator!
          end

          @operator = :matches if @value.is_a?(Regexp)
        end

        def adapt_operator!(value)
          case value
          when Array
            @operator = :in if @operator == :==
          end
        end

        def value_is_in_entry_value?(value)
          _matches = if value.is_a?(Array)
            array_contains?([*value], [*@value])
          else
            [*@value].include?(value)
          end
          @operator == :in ? _matches : !_matches
        end

        private

        def check_operator!
          raise UnsupportedOperator.new unless OPERATORS.include?(@operator)
        end

        def array_contains?(source, target)
          if target.size == 0
            source.size == 0
          else
            (source & target).size != 0
          end
        end

      end

    end
  end
end