correctly handle BigDecimal Rubyfying

BigDecimal fix spec
This commit is contained in:
Colin Surprenant 2016-03-17 18:31:52 -04:00
parent a1ac7e699f
commit f3c29faeaf
3 changed files with 236 additions and 0 deletions

View file

@ -96,6 +96,14 @@ describe LogStash::Event do
e["[foo]"] = nil
expect(e.to_hash).to include("foo" => nil)
end
# BigDecinal is now natively converted by JRuby, see https://github.com/elastic/logstash/pull/4838
it "should set BigDecimal" do
e = LogStash::Event.new()
e["[foo]"] = BigDecimal.new(1)
expect(e["foo"]).to be_kind_of(BigDecimal)
expect(e["foo"]).to eq(BigDecimal.new(1))
end
end
context "timestamp" do

View file

@ -4,9 +4,11 @@ import com.logstash.ext.JrubyTimestampExtLibrary;
import org.jruby.Ruby;
import org.jruby.RubyArray;
import org.jruby.RubyHash;
import org.jruby.ext.bigdecimal.RubyBigDecimal;
import org.jruby.javasupport.JavaUtil;
import org.jruby.runtime.builtin.IRubyObject;
import java.math.BigDecimal;
import java.util.*;
public final class Rubyfier {
@ -20,6 +22,9 @@ public final class Rubyfier {
if (input instanceof Timestamp) return JrubyTimestampExtLibrary.RubyTimestamp.newRubyTimestamp(runtime, (Timestamp)input);
if (input instanceof Collection) throw new ClassCastException("unexpected Collection type " + input.getClass());
// BigDecimal is not currenly handled by JRuby and this is the type Jackson uses for floats
if (input instanceof BigDecimal) return new RubyBigDecimal(runtime, runtime.getClass("BigDecimal"), (BigDecimal)input);
return JavaUtil.convertJavaToUsableRubyObject(runtime, input);
}
@ -29,6 +34,9 @@ public final class Rubyfier {
if (input instanceof Timestamp) return JrubyTimestampExtLibrary.RubyTimestamp.newRubyTimestamp(runtime, (Timestamp)input);
if (input instanceof Collection) throw new ClassCastException("unexpected Collection type " + input.getClass());
// BigDecimal is not currenly handled by JRuby and this is the type Jackson uses for floats
if (input instanceof BigDecimal) return new RubyBigDecimal(runtime, runtime.getClass("BigDecimal"), (BigDecimal)input);
return input;
}

View file

@ -0,0 +1,220 @@
package com.logstash;
import org.jruby.*;
import org.jruby.ext.bigdecimal.RubyBigDecimal;
import org.jruby.javasupport.JavaUtil;
import org.jruby.runtime.builtin.IRubyObject;
import org.junit.Test;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static org.junit.Assert.*;
public class RubyfierTest {
@Test
public void testDeepWithString() {
Object result = Rubyfier.deep(Ruby.getGlobalRuntime(), "foo");
assertEquals(RubyString.class, result.getClass());
assertEquals("foo", result.toString());
}
@Test
public void testDeepMapWithString()
throws Exception
{
Map data = new HashMap();
data.put("foo", "bar");
RubyHash rubyHash = ((RubyHash)Rubyfier.deep(Ruby.getGlobalRuntime(), data));
// Hack to be able to retrieve the original, unconverted Ruby object from Map
// it seems the only method providing this is internalGet but it is declared protected.
// I know this is bad practice but I think this is practically acceptable.
Method internalGet = RubyHash.class.getDeclaredMethod("internalGet", IRubyObject.class);
internalGet.setAccessible(true);
Object result = internalGet.invoke(rubyHash, JavaUtil.convertJavaToUsableRubyObject(Ruby.getGlobalRuntime(), "foo"));
assertEquals(RubyString.class, result.getClass());
assertEquals("bar", result.toString());
}
@Test
public void testDeepListWithString()
throws Exception
{
List data = new ArrayList();
data.add("foo");
RubyArray rubyArray = ((RubyArray)Rubyfier.deep(Ruby.getGlobalRuntime(), data));
// toJavaArray does not convert inner elemenst to Java types \o/
assertEquals(RubyString.class, rubyArray.toJavaArray()[0].getClass());
assertEquals("foo", rubyArray.toJavaArray()[0].toString());
}
@Test
public void testDeepWithInteger() {
Object result = Rubyfier.deep(Ruby.getGlobalRuntime(), 1);
assertEquals(RubyFixnum.class, result.getClass());
assertEquals(1L, ((RubyFixnum)result).getLongValue());
}
@Test
public void testDeepMapWithInteger()
throws Exception
{
Map data = new HashMap();
data.put("foo", 1);
RubyHash rubyHash = ((RubyHash)Rubyfier.deep(Ruby.getGlobalRuntime(), data));
// Hack to be able to retrieve the original, unconverted Ruby object from Map
// it seems the only method providing this is internalGet but it is declared protected.
// I know this is bad practice but I think this is practically acceptable.
Method internalGet = RubyHash.class.getDeclaredMethod("internalGet", IRubyObject.class);
internalGet.setAccessible(true);
Object result = internalGet.invoke(rubyHash, JavaUtil.convertJavaToUsableRubyObject(Ruby.getGlobalRuntime(), "foo"));
assertEquals(RubyFixnum.class, result.getClass());
assertEquals(1L, ((RubyFixnum)result).getLongValue());
}
@Test
public void testDeepListWithInteger()
throws Exception
{
List data = new ArrayList();
data.add(1);
RubyArray rubyArray = ((RubyArray)Rubyfier.deep(Ruby.getGlobalRuntime(), data));
// toJavaArray does not convert inner elemenst to Java types \o/
assertEquals(RubyFixnum.class, rubyArray.toJavaArray()[0].getClass());
assertEquals(1L, ((RubyFixnum)rubyArray.toJavaArray()[0]).getLongValue());
}
@Test
public void testDeepWithFloat() {
Object result = Rubyfier.deep(Ruby.getGlobalRuntime(), 1.0F);
assertEquals(RubyFloat.class, result.getClass());
assertEquals(1.0D, ((RubyFloat)result).getDoubleValue(), 0);
}
@Test
public void testDeepMapWithFloat()
throws Exception
{
Map data = new HashMap();
data.put("foo", 1.0F);
RubyHash rubyHash = ((RubyHash)Rubyfier.deep(Ruby.getGlobalRuntime(), data));
// Hack to be able to retrieve the original, unconverted Ruby object from Map
// it seems the only method providing this is internalGet but it is declared protected.
// I know this is bad practice but I think this is practically acceptable.
Method internalGet = RubyHash.class.getDeclaredMethod("internalGet", IRubyObject.class);
internalGet.setAccessible(true);
Object result = internalGet.invoke(rubyHash, JavaUtil.convertJavaToUsableRubyObject(Ruby.getGlobalRuntime(), "foo"));
assertEquals(RubyFloat.class, result.getClass());
assertEquals(1.0D, ((RubyFloat)result).getDoubleValue(), 0);
}
@Test
public void testDeepListWithFloat()
throws Exception
{
List data = new ArrayList();
data.add(1.0F);
RubyArray rubyArray = ((RubyArray)Rubyfier.deep(Ruby.getGlobalRuntime(), data));
// toJavaArray does not convert inner elemenst to Java types \o/
assertEquals(RubyFloat.class, rubyArray.toJavaArray()[0].getClass());
assertEquals(1.0D, ((RubyFloat)rubyArray.toJavaArray()[0]).getDoubleValue(), 0);
}
@Test
public void testDeepWithDouble() {
Object result = Rubyfier.deep(Ruby.getGlobalRuntime(), 1.0D);
assertEquals(RubyFloat.class, result.getClass());
assertEquals(1.0D, ((RubyFloat)result).getDoubleValue(), 0);
}
@Test
public void testDeepMapWithDouble()
throws Exception
{
Map data = new HashMap();
data.put("foo", 1.0D);
RubyHash rubyHash = ((RubyHash)Rubyfier.deep(Ruby.getGlobalRuntime(), data));
// Hack to be able to retrieve the original, unconverted Ruby object from Map
// it seems the only method providing this is internalGet but it is declared protected.
// I know this is bad practice but I think this is practically acceptable.
Method internalGet = RubyHash.class.getDeclaredMethod("internalGet", IRubyObject.class);
internalGet.setAccessible(true);
Object result = internalGet.invoke(rubyHash, JavaUtil.convertJavaToUsableRubyObject(Ruby.getGlobalRuntime(), "foo"));
assertEquals(RubyFloat.class, result.getClass());
assertEquals(1.0D, ((RubyFloat)result).getDoubleValue(), 0);
}
@Test
public void testDeepListWithDouble()
throws Exception
{
List data = new ArrayList();
data.add(1.0D);
RubyArray rubyArray = ((RubyArray)Rubyfier.deep(Ruby.getGlobalRuntime(), data));
// toJavaArray does not convert inner elemenst to Java types \o/
assertEquals(RubyFloat.class, rubyArray.toJavaArray()[0].getClass());
assertEquals(1.0D, ((RubyFloat)rubyArray.toJavaArray()[0]).getDoubleValue(), 0);
}
@Test
public void testDeepWithBigDecimal() {
Object result = Rubyfier.deep(Ruby.getGlobalRuntime(), new BigDecimal(1));
assertEquals(RubyBigDecimal.class, result.getClass());
assertEquals(1.0D, ((RubyBigDecimal)result).getDoubleValue(), 0);
}
@Test
public void testDeepMapWithBigDecimal()
throws Exception
{
Map data = new HashMap();
data.put("foo", new BigDecimal(1));
RubyHash rubyHash = ((RubyHash)Rubyfier.deep(Ruby.getGlobalRuntime(), data));
// Hack to be able to retrieve the original, unconverted Ruby object from Map
// it seems the only method providing this is internalGet but it is declared protected.
// I know this is bad practice but I think this is practically acceptable.
Method internalGet = RubyHash.class.getDeclaredMethod("internalGet", IRubyObject.class);
internalGet.setAccessible(true);
Object result = internalGet.invoke(rubyHash, JavaUtil.convertJavaToUsableRubyObject(Ruby.getGlobalRuntime(), "foo"));
assertEquals(RubyBigDecimal.class, result.getClass());
assertEquals(1.0D, ((RubyBigDecimal)result).getDoubleValue(), 0);
}
@Test
public void testDeepListWithBigDecimal()
throws Exception
{
List data = new ArrayList();
data.add(new BigDecimal(1));
RubyArray rubyArray = ((RubyArray)Rubyfier.deep(Ruby.getGlobalRuntime(), data));
// toJavaArray does not convert inner elemenst to Java types \o/
assertEquals(RubyBigDecimal.class, rubyArray.toJavaArray()[0].getClass());
assertEquals(1.0D, ((RubyBigDecimal)rubyArray.toJavaArray()[0]).getDoubleValue(), 0);
}
}