Skip to content

Commit

Permalink
Prevent loading both json/ext and json/pure
Browse files Browse the repository at this point in the history
Fix: #650
Ref: #646

`json_pure` currently doesn't work well at all because `json/ext` is
a default gem, so if you have code requiring `json/pure` and some other
code requiring `json`, you end up with both loaded.

If the `json` and `json_pure` versions match, it's not too bad,
but if they don't match, you might run into issues with private
APIs no longer matching.
  • Loading branch information
byroot committed Nov 5, 2024
1 parent dcd8292 commit dcd79af
Show file tree
Hide file tree
Showing 10 changed files with 76 additions and 59 deletions.
2 changes: 2 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

begin
require 'rubygems/package_task'
rescue LoadError
Expand Down
6 changes: 5 additions & 1 deletion json.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,11 @@ spec = Gem::Specification.new do |s|
"LEGAL",
"README.md",
"json.gemspec",
*Dir["lib/**/*.rb"],
*(
Dir["lib/**/*.rb"] -
# We keep lib/json/pure/*.rb on purpose for TruffleRuby, but not the entry points.
Dir["lib/json/pure.rb"] - Dir["lib/json_pure.rb"]
),
]

if java_ext
Expand Down
35 changes: 13 additions & 22 deletions json_pure.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -16,36 +16,27 @@ Gem::Specification.new do |s|

s.extra_rdoc_files = ["README.md"]
s.rdoc_options = ["--title", "JSON implementation for ruby", "--main", "README.md"]

s.files = [
"CHANGES.md",
"COPYING",
"BSDL",
"LEGAL",
"README.md",
"json.gemspec",
*(Dir["lib/**/*.rb"] - Dir["lib/json/ext/**/*.rb"]),
]

s.files = [
"CHANGES.md",
"COPYING",
"BSDL",
"LEGAL",
"README.md",
"json_pure.gemspec",
"lib/json.rb",
"lib/json/add/bigdecimal.rb",
"lib/json/add/complex.rb",
"lib/json/add/core.rb",
"lib/json/add/date.rb",
"lib/json/add/date_time.rb",
"lib/json/add/exception.rb",
"lib/json/add/ostruct.rb",
"lib/json/add/range.rb",
"lib/json/add/rational.rb",
"lib/json/add/regexp.rb",
"lib/json/add/set.rb",
"lib/json/add/struct.rb",
"lib/json/add/symbol.rb",
"lib/json/add/time.rb",
"lib/json/common.rb",
"lib/json/ext.rb",
"lib/json/generic_object.rb",
"lib/json/pure.rb",
"lib/json/pure/generator.rb",
"lib/json/pure/parser.rb",
"lib/json/version.rb",
*Dir["lib/**/*.rb"],
]

s.homepage = "https://ruby.github.io/json"
s.metadata = {
'bug_tracker_uri' => 'https://github.com/ruby/json/issues',
Expand Down
1 change: 1 addition & 0 deletions lib/json/common.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#frozen_string_literal: true

require 'json/version'

module JSON
Expand Down
37 changes: 19 additions & 18 deletions lib/json/ext.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,25 @@

require 'json/common'

module JSON
# This module holds all the modules/classes that implement JSON's
# functionality as C extensions.
module Ext
if RUBY_ENGINE == 'truffleruby'
require 'json/ext/parser'
require 'json/pure'
$DEBUG and warn "Using Ext extension for JSON parser and Pure library for JSON generator."
JSON.parser = Parser
JSON.generator = JSON::Pure::Generator
else
require 'json/ext/parser'
require 'json/ext/generator'
$DEBUG and warn "Using Ext extension for JSON."
JSON.parser = Parser
JSON.generator = Generator
end
unless defined?(::JSON::JSON_LOADED)
$LOADED_FEATURES << File.expand_path("../pure.rb", __FILE__)

if RUBY_ENGINE == 'truffleruby'
require 'json/ext/parser'
# We use require_relative to make sure we're loading the same version.
# Otherwise if the Gemfile include conflicting versions of `json` and `json_pure`
# it may break.
require_relative 'pure/generator'
$DEBUG and warn "Using Ext extension for JSON parser and Pure library for JSON generator."
JSON.parser = JSON::Ext::Parser
JSON.generator = JSON::Pure::Generator
else
require 'json/ext/parser'
require 'json/ext/generator'
$DEBUG and warn "Using Ext extension for JSON."
JSON.parser = JSON::Ext::Parser
JSON.generator = JSON::Ext::Generator
end

JSON_LOADED = true unless defined?(::JSON::JSON_LOADED)
::JSON::JSON_LOADED = true
end
Empty file removed lib/json/ext/.keep
Empty file.
1 change: 1 addition & 0 deletions lib/json/generic_object.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# frozen_string_literal: true

begin
require 'ostruct'
rescue LoadError
Expand Down
20 changes: 9 additions & 11 deletions lib/json/pure.rb
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
# frozen_string_literal: true

require 'json/common'

module JSON
# This module holds all the modules/classes that implement JSON's
# functionality in pure ruby.
module Pure
require 'json/pure/parser'
require 'json/pure/generator'
$DEBUG and warn "Using Pure library for JSON."
JSON.parser = Parser
JSON.generator = Generator
end
unless defined?(::JSON::JSON_LOADED)
require 'json/pure/parser'
require 'json/pure/generator'
$LOADED_FEATURES << File.expand_path("../ext.rb", __FILE__)
$DEBUG and warn "Using Pure library for JSON."
JSON.parser = JSON::Pure::Parser
JSON.generator = JSON::Pure::Generator

JSON_LOADED = true unless defined?(::JSON::JSON_LOADED)
JSON::JSON_LOADED = true
end
3 changes: 3 additions & 0 deletions lib/json_pure.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# frozen_string_literal: true

require 'json/pure'
30 changes: 23 additions & 7 deletions test/json/test_helper.rb
Original file line number Diff line number Diff line change
@@ -1,23 +1,39 @@
# frozen_string_literal: true

case ENV['JSON']
when 'pure'
$LOAD_PATH.unshift(File.expand_path('../../../lib', __FILE__))
$stderr.puts("Testing JSON::Pure")
require 'json/pure'
begin
require 'json/ext'
rescue LoadError
# Ensure `json/ext` can't be loaded after `json/pure`
end
if [JSON.generator, JSON.parser].map(&:name) != ["JSON::Pure::Generator", "JSON::Pure::Parser"]
abort "Expected JSON::Pure to be loaded, got: #{[JSON.generator, JSON.parser]}"
end
when 'ext'
$stderr.puts("Testing JSON::Ext")
$LOAD_PATH.unshift(File.expand_path('../../../ext', __FILE__), File.expand_path('../../../lib', __FILE__))
require 'json/ext'
require 'json/pure'

expected = if RUBY_ENGINE == 'truffleruby'
["JSON::Pure::Generator", "JSON::Ext::Parser"]
else
["JSON::Ext::Generator", "JSON::Ext::Parser"]
end

if [JSON.generator, JSON.parser].map(&:name) != expected
abort "Expected JSON::Ext to be loaded, got: #{[JSON.generator, JSON.parser]}"
end
else
$LOAD_PATH.unshift(File.expand_path('../../../ext', __FILE__), File.expand_path('../../../lib', __FILE__))
$stderr.puts("Testing JSON")
require 'json'
end

$stderr.puts("Testing #{JSON.generator} and #{JSON.parser}")

require 'test/unit'
begin
require 'byebug'
rescue LoadError
end

if GC.respond_to?(:verify_compaction_references)
# This method was added in Ruby 3.0.0. Calling it this way asks the GC to
Expand Down

0 comments on commit dcd79af

Please sign in to comment.