class Dentaku::AST::Arithmetic

Constants

DECIMAL
INTEGER

Public Class Methods

new(*) click to toggle source
Calls superclass method
# File lib/dentaku/ast/arithmetic.rb, line 12
def initialize(*)
  super

  unless valid_left?
    raise NodeError.new(:numeric, left.type, :left),
          "#{self.class} requires numeric operands"
  end

  unless valid_right?
    raise NodeError.new(:numeric, right.type, :right),
          "#{self.class} requires numeric operands"
  end
end

Public Instance Methods

operator() click to toggle source
# File lib/dentaku/ast/arithmetic.rb, line 30
def operator
  raise NotImplementedError
end
type() click to toggle source
# File lib/dentaku/ast/arithmetic.rb, line 26
def type
  :numeric
end
value(context = {}) click to toggle source
# File lib/dentaku/ast/arithmetic.rb, line 34
def value(context = {})
  calculate(left.value(context), right.value(context))
end

Private Instance Methods

calculate(left_value, right_value) click to toggle source
# File lib/dentaku/ast/arithmetic.rb, line 40
def calculate(left_value, right_value)
  l = cast(left_value)
  r = cast(right_value)

  l.public_send(operator, r)
rescue ::TypeError => e
  # Right cannot be converted to a suitable type for left. e.g. [] + 1
  raise Dentaku::ArgumentError.for(:incompatible_type, value: r, for: l.class), e.message
end
cast(val) click to toggle source
# File lib/dentaku/ast/arithmetic.rb, line 50
def cast(val)
  validate_value(val)
  numeric(val)
end
datetime?(val) click to toggle source
# File lib/dentaku/ast/arithmetic.rb, line 69
def datetime?(val)
  # val is a Date, Time, or DateTime
  return true if val.respond_to?(:strftime)

  val.to_s =~ Dentaku::TokenScanner::DATE_TIME_REGEXP
end
decimal(val) click to toggle source
# File lib/dentaku/ast/arithmetic.rb, line 63
def decimal(val)
  BigDecimal(val.to_s, Float::DIG + 1)
rescue # return as is, in case value can't be coerced to big decimal
  val
end
numeric(val) click to toggle source
# File lib/dentaku/ast/arithmetic.rb, line 55
def numeric(val)
  case val.to_s
  when DECIMAL then decimal(val)
  when INTEGER then val.to_i
  else val
  end
end
valid_left?() click to toggle source
# File lib/dentaku/ast/arithmetic.rb, line 80
def valid_left?
  valid_node?(left) || left.type == :datetime
end
valid_node?(node) click to toggle source
# File lib/dentaku/ast/arithmetic.rb, line 76
def valid_node?(node)
  node && (node.type == :numeric || node.type == :integer || node.dependencies.any?)
end
valid_right?() click to toggle source
# File lib/dentaku/ast/arithmetic.rb, line 84
def valid_right?
  valid_node?(right) || right.type == :duration || right.type == :datetime
end
validate_format(string) click to toggle source
# File lib/dentaku/ast/arithmetic.rb, line 103
def validate_format(string)
  unless string =~ /\A-?\d*(\.\d+)?\z/ && !string.empty?
    raise Dentaku::ArgumentError.for(:invalid_value, value: string, for: BigDecimal),
          "String input '#{string}' is not coercible to numeric"
  end
end
validate_operation(val) click to toggle source
# File lib/dentaku/ast/arithmetic.rb, line 96
def validate_operation(val)
  unless val.respond_to?(operator)
    raise Dentaku::ArgumentError.for(:invalid_operator, operation: self.class, operator: operator),
          "#{ self.class } requires operands that respond to #{operator}"
  end
end
validate_value(val) click to toggle source
# File lib/dentaku/ast/arithmetic.rb, line 88
def validate_value(val)
  if val.is_a?(::String)
    validate_format(val)
  else
    validate_operation(val)
  end
end