Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions ext/json/ext/generator/generator.c
Original file line number Diff line number Diff line change
Expand Up @@ -1550,6 +1550,18 @@ static VALUE cState_as_json(VALUE self)
return state->as_json;
}

static VALUE as_json_config(VALUE value)
{
if (rb_obj_is_proc(value)) {
return value;
}
VALUE proc = rb_check_funcall(value, rb_intern("to_proc"), 0, NULL);
if (proc == Qundef || !rb_obj_is_proc(proc)) {
rb_raise(rb_eTypeError, "as_json must be a Proc");
}
return proc;
}

/*
* call-seq: as_json=(as_json)
*
Expand All @@ -1559,7 +1571,7 @@ static VALUE cState_as_json_set(VALUE self, VALUE as_json)
{
rb_check_frozen(self);
GET_STATE(self);
RB_OBJ_WRITE(self, &state->as_json, rb_convert_type(as_json, T_DATA, "Proc", "to_proc"));
RB_OBJ_WRITE(self, &state->as_json, as_json_config(as_json));
return Qnil;
}

Expand Down Expand Up @@ -1879,7 +1891,7 @@ static int configure_state_i(VALUE key, VALUE val, VALUE _arg)
else if (key == sym_strict) { state->strict = RTEST(val); }
else if (key == sym_allow_duplicate_key) { state->allow_duplicate_key = RTEST(val); }
else if (key == sym_as_json) {
VALUE proc = RTEST(val) ? rb_convert_type(val, T_DATA, "Proc", "to_proc") : Qfalse;
VALUE proc = RTEST(val) ? as_json_config(val) : Qfalse;
state->as_json_single_arg = proc && rb_proc_arity(proc) == 1;
state_write_value(data, &state->as_json, proc);
}
Expand Down
38 changes: 38 additions & 0 deletions test/json/json_generator_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1035,6 +1035,44 @@ def test_json_generate_as_json_convert_to_proc
assert_equal object.object_id.to_json, JSON.generate(object, strict: true, as_json: -> (o, is_key) { o.object_id })
end

def test_json_generate_as_json_method
object = Object.new
as_json = -> (o, is_key) { o.object_id }.method(:call)
assert_equal object.object_id.to_json, JSON.generate(object, strict: true, as_json: as_json)
end

def test_state_as_json_method
object = Object.new
state = JSON.state.new(strict: true)
state.as_json = -> (o, is_key) { o.object_id }.method(:call)
assert_equal object.object_id.to_json, state.generate(object)
end

def test_json_generate_as_json_invalid_type
omit 'TruffleRuby does not convert as_json to Proc' if RUBY_ENGINE == 'truffleruby'
[Object.new, Time.now].each do |as_json|
assert_raise(TypeError) { JSON.generate(Object.new, strict: true, as_json: as_json) }
end
end

def test_state_as_json_invalid_type
omit 'TruffleRuby does not convert as_json to Proc' if RUBY_ENGINE == 'truffleruby'
state = JSON.state.new(strict: true)
[Object.new, Time.now].each do |as_json|
assert_raise(TypeError) { state.as_json = as_json }
end
end

def test_as_json_to_proc_returns_invalid_type
omit 'TruffleRuby does not convert as_json to Proc' if RUBY_ENGINE == 'truffleruby'
as_json = Object.new
def as_json.to_proc
method(:to_proc)
end
assert_raise(TypeError) { JSON.generate(Object.new, strict: true, as_json: as_json) }
assert_raise(TypeError) { JSON.state.new.as_json = as_json }
end

def test_as_json_nan_does_not_call_to_json
def (obj = Object.new).to_json(*)
"null"
Expand Down
Loading