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
9 changes: 7 additions & 2 deletions lib/json/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -827,7 +827,12 @@ class Coder
#
# puts MyApp::API_JSON_CODER.dump(Time.now.utc) # => "2025-01-21T08:41:44.286Z"
#
def initialize(object_class: nil, array_class: nil, on_load: nil, **options, &as_json)
# The conversion callback can also be passed as +as_json:+. If both are given, the block takes precedence.
# Passing +false+ or +nil+ for +strict:+ raises ArgumentError.
#
def initialize(object_class: nil, array_class: nil, on_load: nil, strict: true, **options, &as_json)
raise ArgumentError, "JSON::Coder requires strict mode" unless strict

if object_class || array_class
on_load = ParserOptions.on_load(on_load, object_class, array_class)
end
Expand All @@ -839,7 +844,7 @@ def initialize(object_class: nil, array_class: nil, on_load: nil, **options, &as
@state = State.new(
**generator_options,
strict: true,
as_json: as_json,
as_json: as_json || generator_options[:as_json],
).freeze
end

Expand Down
17 changes: 15 additions & 2 deletions lib/json/truffle_ruby/generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,16 @@ def initialize(opts = nil)
attr_accessor :array_nl

# This proc converts unsupported types into native JSON types.
attr_accessor :as_json
attr_reader :as_json

def as_json=(value)
raise FrozenError, "can't modify frozen #{self.class}: #{inspect}" if frozen?
value = value.to_proc if !(Proc === value) && value.respond_to?(:to_proc)
unless Proc === value
raise TypeError, "as_json must be a Proc"
end
@as_json = value
end

# This integer returns the maximum level of data structure nesting in
# the generated JSON, max_nesting = 0 if no maximum is checked.
Expand Down Expand Up @@ -335,7 +344,11 @@ def configure(options)
@object_nl = object_nl || ''
@array_nl = array_nl || ''
@allow_nan = allow_nan || false
@as_json = as_json || false
if as_json
self.as_json = as_json
else
@as_json = false
end
@ascii_only = ascii_only || false
self.sort_keys = sort_keys
@depth = depth
Expand Down
26 changes: 26 additions & 0 deletions test/json/json_coder_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,32 @@ def test_json_coder_with_proc
assert_equal %(["[Object object]"]), coder.dump([Object.new])
end

def test_json_coder_with_as_json_option
callback = ->(object, is_key) { 42 }
[callback, callback.method(:call)].each do |as_json|
coder = JSON::Coder.new(as_json: as_json)
assert_equal '[42]', coder.dump([Object.new])
end
end

def test_json_coder_block_overrides_as_json_option
coder = JSON::Coder.new(as_json: ->(object) { 1 }) { 2 }
assert_equal '[2]', coder.dump([Object.new])
end

def test_json_coder_as_json_invalid_type
assert_raise(TypeError) { JSON::Coder.new(as_json: 'x') }
end

def test_json_coder_rejects_disabling_strict_mode
[false, nil].each do |strict|
error = assert_raise(ArgumentError) { JSON::Coder.new(strict: strict) }
assert_equal 'JSON::Coder requires strict mode', error.message
end
assert_raise(JSON::GeneratorError) { JSON::Coder.new.dump(Object.new) }
assert_raise(JSON::GeneratorError) { JSON::Coder.new(strict: true).dump(Object.new) }
end

def test_json_coder_with_proc_with_unsupported_value
coder = JSON::Coder.new do |object, is_key|
assert_equal false, is_key
Expand Down
5 changes: 2 additions & 3 deletions test/json/json_generator_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1086,26 +1086,25 @@ 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_kind_of Proc, state.as_json
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 }
assert_raise(TypeError) { state.configure(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)
Expand Down
Loading