diff --git a/ext/json/ext/parser/parser.c b/ext/json/ext/parser/parser.c index 5cba2feb..c2255b3c 100644 --- a/ext/json/ext/parser/parser.c +++ b/ext/json/ext/parser/parser.c @@ -2021,7 +2021,15 @@ static int parser_config_init_i(VALUE key, VALUE val, VALUE data) else if (key == sym_allow_invalid_escape) { config->allow_invalid_escape = RTEST(val); } else if (key == sym_symbolize_names) { config->symbolize_names = RTEST(val); } else if (key == sym_freeze) { config->freeze = RTEST(val); } - else if (key == sym_on_load) { parser_config_wb_write(self, &config->on_load_proc, RTEST(val) ? val : Qfalse); } + else if (key == sym_on_load) { + if (RTEST(val) && !rb_obj_is_proc(val)) { + val = rb_check_funcall(val, rb_intern("to_proc"), 0, NULL); + if (val == Qundef || !rb_obj_is_proc(val)) { + rb_raise(rb_eTypeError, "on_load must be a Proc"); + } + } + parser_config_wb_write(self, &config->on_load_proc, RTEST(val) ? val : Qfalse); + } else if (key == sym_allow_duplicate_key) { config->allow_duplicate_key = RTEST(val); } else if (key == sym_decimal_class) { if (RTEST(val)) { diff --git a/test/json/json_coder_test.rb b/test/json/json_coder_test.rb index 0e5e5e2f..5ff31447 100755 --- a/test/json/json_coder_test.rb +++ b/test/json/json_coder_test.rb @@ -56,6 +56,16 @@ def test_json_coder_load_options assert_equal({a: 1}, coder.load('{"a":1}')) end + def test_json_coder_load_with_on_load_method + on_load = ->(value) { Integer === value ? value + 1 : value }.method(:call) + coder = JSON::Coder.new(on_load: on_load) + assert_equal [2], coder.load('[1]') + end + + def test_json_coder_on_load_invalid_type + assert_raise(TypeError) { JSON::Coder.new(on_load: 'x') } + end + def test_json_coder_dump_NaN_or_Infinity coder = JSON::Coder.new { |o| o.inspect } assert_equal "NaN", coder.load(coder.dump(Float::NAN)) diff --git a/test/json/json_parser_test.rb b/test/json/json_parser_test.rb index 025bf671..be2807d3 100644 --- a/test/json/json_parser_test.rb +++ b/test/json/json_parser_test.rb @@ -38,6 +38,32 @@ def test_parsing assert_equal 'test', parser.parse end + def test_on_load_method + on_load = ->(value) { Integer === value ? value + 1 : value }.method(:call) + assert_equal [2], JSON.parse('[1]', on_load: on_load) + end + + def test_on_load_invalid_type + ['x', Object.new, Time.now].each do |on_load| + assert_raise(TypeError) { JSON.parse('[1]', on_load: on_load) } + end + end + + def test_on_load_invalid_to_proc + on_load = Object.new + def on_load.to_proc + method(:to_proc) + end + assert_raise(TypeError) { JSON.parse('[1]', on_load: on_load) } + end + + def test_on_load_falsy + [nil, false].each do |on_load| + config = JSON::Parser::Config.new(on_load: on_load) + assert_equal [1], config.parse('[1]') + end + end + def test_parser_reset parser = Parser.new('{"a":"b"}') assert_equal({ 'a' => 'b' }, parser.parse) diff --git a/test/json/resumable_parser_test.rb b/test/json/resumable_parser_test.rb index 2777d50c..b663d3a6 100644 --- a/test/json/resumable_parser_test.rb +++ b/test/json/resumable_parser_test.rb @@ -106,6 +106,14 @@ def test_nested_parse_error end end + def test_on_load_method + on_load = ->(value) { Integer === value ? value + 1 : value }.method(:call) + parser = new_parser(on_load: on_load) + parser << '[1]' + assert parser.parse + assert_equal [2], parser.value + end + def test_parse_document_direct @parser << '[true]' assert_equal true, @parser.parse