Skip to content

Commit

Permalink
Merge pull request #5199 from rmosolgo/update-ostruct
Browse files Browse the repository at this point in the history
Update default gem usage for Ruby 3.4, fixes #5197
  • Loading branch information
rmosolgo authored Jan 3, 2025
2 parents ca9ff0e + f19c356 commit 3d975bd
Show file tree
Hide file tree
Showing 11 changed files with 66 additions and 39 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ jobs:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: 3.3
ruby-version: 3.4
bundler-cache: true
- run: bundle exec rake rubocop
system_tests:
Expand All @@ -18,12 +18,12 @@ jobs:
- uses: actions/checkout@v4
- uses: ruby/setup-ruby@v1
with:
ruby-version: 3.3
ruby-version: 3.4
bundler-cache: true
- run: bundle exec rake compile
- uses: ruby/setup-ruby@v1
with:
ruby-version: 3.3
ruby-version: 3.4
bundler-cache: true
env:
BUNDLE_GEMFILE: ./spec/dummy/Gemfile
Expand All @@ -43,7 +43,7 @@ jobs:
matrix:
include:
- gemfile: Gemfile
ruby: 3.2
ruby: 3.4
- gemfile: Gemfile
ruby: 2.7 # lowest supported version
- gemfile: gemfiles/rails_7.0.gemfile
Expand All @@ -58,7 +58,7 @@ jobs:
ruby: 3.3
graphql_reject_numbers_followed_by_names: 1
- gemfile: gemfiles/rails_master.gemfile
ruby: 3.3
ruby: 3.4
graphql_reject_numbers_followed_by_names: 1
isolation_level_fiber: 1
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions graphql.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ Gem::Specification.new do |s|

s.add_runtime_dependency "base64"
s.add_runtime_dependency "fiber-storage"
s.add_runtime_dependency "logger"

s.add_development_dependency "benchmark-ips"
s.add_development_dependency "concurrent-ruby", "~>1.0"
Expand Down
4 changes: 1 addition & 3 deletions lib/graphql/subscriptions/serialize.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
# frozen_string_literal: true
require "set"
require "ostruct"

module GraphQL
class Subscriptions
# Serialization helpers for passing subscription data around.
Expand Down Expand Up @@ -148,7 +146,7 @@ def dump_value(obj)
elsif obj.is_a?(Date) || obj.is_a?(Time)
# DateTime extends Date; for TimeWithZone, call `.utc` first.
{ TIMESTAMP_KEY => [obj.class.name, obj.strftime(TIMESTAMP_FORMAT)] }
elsif obj.is_a?(OpenStruct)
elsif defined?(OpenStruct) && obj.is_a?(OpenStruct)
{ OPEN_STRUCT_KEY => dump_value(obj.to_h) }
elsif defined?(ActiveRecord::Relation) && obj.is_a?(ActiveRecord::Relation)
dump_value(obj.to_a)
Expand Down
31 changes: 21 additions & 10 deletions spec/graphql/backtrace_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -137,12 +137,13 @@ def execute_multiplex(multiplex:)
]
assert_equal expected_graphql_backtrace, err.graphql_backtrace

hash_inspect = { message: "Boom" }.inspect
# The message includes the GraphQL context
rendered_table = [
'Loc | Field | Object | Arguments | Result',
'3:13 | Thing.raiseField as boomError | :something | {:message=>"Boom"} | #<RuntimeError: This is broken: Boom>',
'2:11 | Query.field1 | "Root" | {} | {}',
'1:9 | query | "Root" | {"msg"=>"Boom"} | {field1: {...}}',
'Loc | Field | Object | ' + "Arguments".ljust(hash_inspect.size) + ' | Result',
'3:13 | Thing.raiseField as boomError | :something | ' + hash_inspect + ' | #<RuntimeError: This is broken: Boom>',
'2:11 | Query.field1 | "Root" | ' + "{}".ljust(hash_inspect.size) + ' | {}',
'1:9 | query | "Root" | ' + {"msg" => "Boom"}.inspect.ljust(hash_inspect.size) + ' | {field1: {...}}',
].join("\n")

assert_includes err.message, "\n" + rendered_table
Expand Down Expand Up @@ -201,14 +202,18 @@ def execute_multiplex(multiplex:)
backtrace_schema.execute("query { nilInspect { raiseField(message: \"pop!\") } }")
}

hash_inspect = {message: "pop!"}.inspect # `=>` on Ruby < 3.4
rendered_table = [
'Loc | Field | Object | Arguments | Result',
'1:22 | Thing.raiseField | | {:message=>"pop!"} | #<RuntimeError: This is broken: pop!>',
'1:9 | Query.nilInspect | nil | {} | {}',
'1:1 | query | nil | {} | {nilInspect: {...}}',
'Loc | Field | Object | ' + "Arguments".ljust(hash_inspect.size) + ' | Result',
'1:22 | Thing.raiseField | | ' + hash_inspect + ' | #<RuntimeError: This is broken: pop!>',
'1:9 | Query.nilInspect | nil | ' + "{}".ljust(hash_inspect.size) + ' | {}',
'1:1 | query | nil | ' + "{}".ljust(hash_inspect.size) + ' | {nilInspect: {...}}',
'',
''
].join("\n")

assert_includes(err.message, rendered_table)
table = err.message.split("GraphQL Backtrace:\n").last
assert_equal rendered_table, table
end

it "raises original exception instead of a TracedError when error does not occur during resolving" do
Expand All @@ -225,7 +230,13 @@ def execute_multiplex(multiplex:)
# This will get brittle when execution code moves between files
# but I'm not sure how to be sure that the backtrace contains the right stuff!
def assert_backtrace_includes(backtrace, file:, method:)
includes_tag = backtrace.any? { |s| s.include?(file) && s.include?("`" + method) }
includes_tag = if RUBY_VERSION < "3.4"
backtrace.any? { |s| s.include?(file) && s.include?("`" + method) }
elsif method == "block"
backtrace.any? { |s| s.include?(file) && s.include?("'block") }
else
backtrace.any? { |s| s.include?(file) && s.include?("#{method}'") }
end
assert includes_tag, "Backtrace should include #{file} inside method #{method}\n\n#{backtrace.join("\n")}"
end

Expand Down
10 changes: 5 additions & 5 deletions spec/graphql/dataloader/nonblocking_dataloader_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -250,12 +250,12 @@ def self.included(child_class)
let(:scheduler_class) { Libev::Scheduler }
include NonblockingDataloaderAssertions
end
end

describe "with evt" do
require "evt"
let(:scheduler_class) { Evt::Scheduler }
include NonblockingDataloaderAssertions
describe "with evt" do
require "evt"
let(:scheduler_class) { Evt::Scheduler }
include NonblockingDataloaderAssertions
end
end
end
end
2 changes: 1 addition & 1 deletion spec/graphql/execution/errors_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ def non_nullable_array
ctx = { errors: [] }
res = ErrorsTestSchema.execute "{ f1(a1: 1) }", context: ctx, root_value: :abc
assert_equal({ "data" => { "f1" => nil } }, res)
assert_equal ["f1 broke (ErrorsTestSchema::Query.f1, :abc, {:a1=>1})"], ctx[:errors]
assert_equal ["f1 broke (ErrorsTestSchema::Query.f1, :abc, #{{a1: 1}.inspect})"], ctx[:errors]
end

it "rescues errors from lazy code" do
Expand Down
4 changes: 3 additions & 1 deletion spec/graphql/pagination/connections_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,9 @@ def things2
ConnectionErrorTestSchema.execute("{ things2 { name } }")
end

expected_message = if RUBY_VERSION >= "3.3"
expected_message = if RUBY_VERSION >= "3.4"
"undefined method 'no_such_method' for an instance of ConnectionErrorTestSchema::BadThing"
elsif RUBY_VERSION >= "3.3"
"undefined method `no_such_method' for an instance of ConnectionErrorTestSchema::BadThing"
else
"undefined method `no_such_method' for <BadThing!>"
Expand Down
20 changes: 10 additions & 10 deletions spec/graphql/schema/argument_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ def self.resolve_type(type, obj, ctx)

res = SchemaArgumentTest::Schema.execute(query_str)
# Make sure it's getting the renamed symbol:
assert_equal '{:renamed=>"x", :required_with_default_arg=>1}', res["data"]["field"]
assert_equal({renamed: "x", required_with_default_arg: 1}.inspect, res["data"]["field"])
end
end

Expand All @@ -186,7 +186,7 @@ def self.resolve_type(type, obj, ctx)

res = SchemaArgumentTest::Schema.execute(query_str, context: {multiply_by: 3})
# Make sure it's getting the renamed symbol:
assert_equal '{:prepared_arg=>15, :required_with_default_arg=>1}', res["data"]["field"]
assert_equal({ prepared_arg: 15, required_with_default_arg: 1}.inspect, res["data"]["field"])
end

it "calls the method on the provided Proc" do
Expand All @@ -196,7 +196,7 @@ def self.resolve_type(type, obj, ctx)

res = SchemaArgumentTest::Schema.execute(query_str, context: {multiply_by: 3})
# Make sure it's getting the renamed symbol:
assert_equal '{:prepared_by_proc_arg=>15, :required_with_default_arg=>1}', res["data"]["field"]
assert_equal({prepared_by_proc_arg: 15, required_with_default_arg: 1 }.inspect, res["data"]["field"])
end

it "calls the method on the provided callable object" do
Expand All @@ -206,7 +206,7 @@ def self.resolve_type(type, obj, ctx)

res = SchemaArgumentTest::Schema.execute(query_str, context: {multiply_by: 3})
# Make sure it's getting the renamed symbol:
assert_equal '{:prepared_by_callable_arg=>15, :required_with_default_arg=>1}', res["data"]["field"]
assert_equal({prepared_by_callable_arg: 15, required_with_default_arg: 1}.inspect, res["data"]["field"])
end

it "handles exceptions raised by prepare" do
Expand All @@ -215,7 +215,7 @@ def self.resolve_type(type, obj, ctx)
GRAPHQL

res = SchemaArgumentTest::Schema.execute(query_str, context: {multiply_by: 3})
assert_equal({ 'f1' => '{:arg=>"echo", :required_with_default_arg=>1}', 'f2' => nil }, res['data'])
assert_equal({ 'f1' => {arg: "echo", required_with_default_arg: 1}.inspect, 'f2' => nil }, res['data'])
assert_equal(res['errors'][0]['message'], 'boom!')
assert_equal(res['errors'][0]['path'], ['f2'])
end
Expand All @@ -226,7 +226,7 @@ def self.resolve_type(type, obj, ctx)
GRAPHQL

res = SchemaArgumentTest::Schema.execute(query_str, context: {multiply_by: 3})
assert_equal({ 'f1' => '{:arg=>"echo", :required_with_default_arg=>1}', 'f2' => nil }, res['data'])
assert_equal({ 'f1' => {arg: "echo", required_with_default_arg: 1}.inspect, 'f2' => nil }, res['data'])
assert_nil(res['errors'])
end
end
Expand All @@ -238,7 +238,7 @@ def self.resolve_type(type, obj, ctx)
GRAPHQL

res = SchemaArgumentTest::Schema.execute(query_str)
assert_equal '{:required_with_default_arg=>1}', res["data"]["field"]
assert_equal({required_with_default_arg: 1}.inspect, res["data"]["field"])
end

it 'uses provided input value' do
Expand All @@ -247,7 +247,7 @@ def self.resolve_type(type, obj, ctx)
GRAPHQL

res = SchemaArgumentTest::Schema.execute(query_str)
assert_equal '{:required_with_default_arg=>2}', res["data"]["field"]
assert_equal({ required_with_default_arg: 2 }.inspect, res["data"]["field"])
end

it 'respects non-null type' do
Expand All @@ -267,14 +267,14 @@ def self.resolve_type(type, obj, ctx)
GRAPHQL

res = SchemaArgumentTest::Schema.execute(query_str)
assert_equal "{:instrument=>#{Jazz::Models::Instrument.new("Drum Kit", "PERCUSSION").inspect}, :required_with_default_arg=>1}", res["data"]["field"]
assert_equal({instrument: Jazz::Models::Instrument.new("Drum Kit", "PERCUSSION"), required_with_default_arg: 1}.inspect, res["data"]["field"])

query_str2 = <<-GRAPHQL
query { field(instrumentIds: ["Instrument/Organ"]) }
GRAPHQL

res = SchemaArgumentTest::Schema.execute(query_str2)
assert_equal "{:instruments=>[#{Jazz::Models::Instrument.new("Organ", "KEYS").inspect}], :required_with_default_arg=>1}", res["data"]["field"]
assert_equal({instruments: [Jazz::Models::Instrument.new("Organ", "KEYS")], required_with_default_arg: 1}.inspect, res["data"]["field"])
end

it "returns nil when no ID is given and `required: false`" do
Expand Down
9 changes: 6 additions & 3 deletions spec/graphql/schema/input_object_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -509,7 +509,8 @@ def self.object_from_id(id, _ctx)
it "authorizes Hashes returned from prepare:" do
query_str = "{ hashInput(input: { k1: 5, k2: 12 }) }"
res = InputObjectPrepareObjectTest::Schema.execute(query_str)
assert_equal "Hash, {:k1=>5, :k2=>12}", res["data"]["hashInput"]
expected_str = { k1: 5, k2: 12 }.inspect
assert_equal "Hash, #{expected_str}", res["data"]["hashInput"]

query_str = "{ hashInput(input: { k1: 500, k2: 12 }) }"
res = InputObjectPrepareObjectTest::Schema.execute(query_str)
Expand Down Expand Up @@ -685,14 +686,16 @@ def self.resolve_type(type, obj, ctx)

it "handles camelized booleans" do
res = Jazz::Schema.execute("query($input: CamelizedBooleanInput!){ inputObjectCamelization(input: $input) }", variables: { input: { camelizedBoolean: false } })
assert_equal "{:camelized_boolean=>false}", res["data"]["inputObjectCamelization"]
expected_res = { camelized_boolean: false }.inspect
assert_equal expected_res, res["data"]["inputObjectCamelization"]
end
end

describe "when used with default_value" do
it "comes as an instance" do
res = Jazz::Schema.execute("{ defaultValueTest }")
assert_equal "Jazz::InspectableInput -> {:string_value=>\"S\"}", res["data"]["defaultValueTest"]
expected_res = { string_value: "S" }.inspect
assert_equal "Jazz::InspectableInput -> #{expected_res}", res["data"]["defaultValueTest"]
end

it "works with empty objects" do
Expand Down
3 changes: 2 additions & 1 deletion spec/graphql/schema/mutation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -256,7 +256,8 @@ def resolve(**inputs)
assert_equal ["thingId", "thingName"], child_mutation.all_argument_definitions.map(&:graphql_name)
assert_equal ["thingId", "thingName"], schema.mutation.fields["child"].all_argument_definitions.map(&:graphql_name)
res = schema.execute("mutation { child(thingName: \"abc\", thingId: \"123\") { inputs } }")
assert_equal "{:thing_id=>\"123\", :thing_name=>\"abc\"}", res["data"]["child"]["inputs"]
expected_result = { thing_id: "123", thing_name: "abc" }.inspect
assert_equal expected_result, res["data"]["child"]["inputs"]
end

describe "flushing dataloader cache" do
Expand Down
11 changes: 11 additions & 0 deletions spec/graphql_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# frozen_string_literal: true
require "spec_helper"
require "open3"

describe GraphQL do
it "loads without warnings" do
stderr_and_stdout, _status = Open3.capture2e(%|ruby -Ilib -e "require 'bundler/inline'; gemfile(true, quiet: true) { source('https://rubygems.org'); gem('fiber-storage'); gem('graphql', path: './') }; GraphQL.eager_load!"|)
puts stderr_and_stdout
assert_equal "", stderr_and_stdout
end
end

0 comments on commit 3d975bd

Please sign in to comment.