class Dentaku::AST::Arithmetic
def cast(val, prefer_integer = true)
def cast(val, prefer_integer = true) validate_value(val) numeric(val, prefer_integer) end
def initialize(*)
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
def numeric(val, prefer_integer)
def numeric(val, prefer_integer) v = BigDecimal(val, Float::DIG + 1) v = v.to_i if prefer_integer && v.frac.zero? v rescue ::TypeError # If we got a TypeError BigDecimal or to_i failed; # let value through so ruby things like Time - integer work val end
def operator
def operator raise NotImplementedError end
def type
def type :numeric end
def valid_left?
def valid_left? valid_node?(left) || left.type == :datetime end
def valid_node?(node)
def valid_node?(node) node && (node.type == :numeric || node.dependencies.any?) end
def valid_right?
def valid_right? valid_node?(right) || right.type == :duration || right.type == :datetime end
def validate_format(string)
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
def validate_operation(val)
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
def validate_value(val)
def validate_value(val) if val.is_a?(::String) validate_format(val) else validate_operation(val) end end
def value(context = {})
def value(context = {}) l = cast(left.value(context)) r = cast(right.value(context)) begin 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 end