From ab760649ee148de75be4f295424ded092c58bbb7 Mon Sep 17 00:00:00 2001 From: Arthur Neves Date: Wed, 5 Jun 2013 16:09:05 -0400 Subject: [PATCH] use BigDecimal on filters to have better precision --- lib/liquid/standardfilters.rb | 35 +++++++++++++++++------------ test/liquid/standard_filter_test.rb | 4 ++++ 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/lib/liquid/standardfilters.rb b/lib/liquid/standardfilters.rb index b189c2b..f0d8d79 100644 --- a/lib/liquid/standardfilters.rb +++ b/lib/liquid/standardfilters.rb @@ -1,4 +1,5 @@ require 'cgi' +require 'bigdecimal' module Liquid @@ -210,41 +211,47 @@ module Liquid # addition def plus(input, operand) - to_number(input) + to_number(operand) + apply_operation(input, operand, :+) end # subtraction def minus(input, operand) - to_number(input) - to_number(operand) + apply_operation(input, operand, :-) end # multiplication def times(input, operand) - to_number(input) * to_number(operand) + apply_operation(input, operand, :*) end # division def divided_by(input, operand) - to_number(input) / to_number(operand) + apply_operation(input, operand, :/) end def modulo(input, operand) - to_number(input) % to_number(operand) + apply_operation(input, operand, :%) end private - def to_number(obj) - case obj - when Numeric - obj - when String - (obj.strip =~ /^\d+\.\d+$/) ? obj.to_f : obj.to_i - else - 0 - end + def to_number(obj) + case obj + when Float + BigDecimal.new(obj.to_s) + when Numeric + obj + when String + (obj.strip =~ /^\d+\.\d+$/) ? BigDecimal.new(obj) : obj.to_i + else + 0 end + end + def apply_operation(input, operand, operation) + result = to_number(input).send(operation, to_number(operand)) + result.is_a?(BigDecimal) ? result.to_f : result + end end Template.register_filter(StandardFilters) diff --git a/test/liquid/standard_filter_test.rb b/test/liquid/standard_filter_test.rb index 6f7e16d..4ace73a 100644 --- a/test/liquid/standard_filter_test.rb +++ b/test/liquid/standard_filter_test.rb @@ -164,6 +164,8 @@ class StandardFiltersTest < Test::Unit::TestCase assert_match(/(6\.3)|(6\.(0{13})1)/, Template.parse("{{ '2.1' | times:3 }}").render) assert_template_result "6", "{{ '2.1' | times:3 | replace: '.','-' | plus:0}}" + + assert_template_result "7.25", "{{ 0.0725 | times:100 }}" end def test_divided_by @@ -175,6 +177,8 @@ class StandardFiltersTest < Test::Unit::TestCase assert_template_result "5", "{{ 15 | divided_by:3 }}" assert_template_result "Liquid error: divided by 0", "{{ 5 | divided_by:0 }}" + + assert_template_result "0.5", "{{ 2.0 | divided_by:4 }}" end def test_modulo