Skip to content

Commit

Permalink
Fix Rails 7.1 compatibility.
Browse files Browse the repository at this point in the history
Rails 7.1 has sped up its internal OutputBuffer implementation (what handles << concatenation in templates),
which we rely on tricking a bit. rails/rails#45614

Then a follow up PR the `to_s` call was moved to after calling `view._run` rails/rails#45756

However, we rely on the return value being a Hash (which I'm not sure how to fix yet).

So since `target!` is the return value of our templates, we can wrap that and ensure `to_s` doesn't actually `to_s`, which Rails 7.1
will call for us.

On Rails < 7.1 we must call the compatibility `unwrap_target!` method ourselves to get our inner Hash return value.

This also adds a Rails version matrix so we can detect these things before release in the future.
  • Loading branch information
kaspth committed Oct 9, 2023
1 parent f5ddafc commit 08804ee
Show file tree
Hide file tree
Showing 8 changed files with 52 additions and 9 deletions.
18 changes: 14 additions & 4 deletions .github/workflows/ci.yml
Expand Up @@ -8,17 +8,27 @@ on:
pull_request:

jobs:
tests:
runs-on: ubuntu-latest
name: Ruby ${{ matrix.ruby-version }}
test:
strategy:
fail-fast: false
matrix:
rails-version:
- "7.1"
- "6.1"
- "main"
ruby-version:
- "3.0"
- "3.2"

env:
RAILS_ENV: test
RAILS_VERSION: ${{ matrix.rails-version }}

name: ${{ format('Test rails@{0} ruby@{1}', matrix.rails-version, matrix.ruby-version) }}
runs-on: "ubuntu-latest"

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
with:
Expand Down
10 changes: 10 additions & 0 deletions Gemfile
Expand Up @@ -12,3 +12,13 @@ gem "sqlite3"
gem "standard", "~> 1.3"

gem "mocha"

rails_version = ENV.fetch("RAILS_VERSION", "7.0")

rails_constraint = if rails_version == "main"
{github: "rails/rails"}
else
"~> #{rails_version}.0"
end

gem "rails", rails_constraint
3 changes: 2 additions & 1 deletion Gemfile.lock
Expand Up @@ -3,7 +3,7 @@ PATH
specs:
jbuilder-schema (2.3.1)
jbuilder
rails (>= 5.0.0, < 7.1.0)
rails (>= 5.0.0)

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -207,6 +207,7 @@ PLATFORMS
DEPENDENCIES
jbuilder-schema!
mocha
rails (~> 7.0.8.0)
rake (~> 13.0)
sqlite3
standard (~> 1.3)
Expand Down
2 changes: 1 addition & 1 deletion jbuilder-schema.gemspec
Expand Up @@ -34,7 +34,7 @@ Gem::Specification.new do |spec|
# Uncomment to register a new dependency of your gem
spec.add_dependency "jbuilder"

spec.add_dependency "rails", ">= 5.0.0", "< 7.1.0"
spec.add_dependency "rails", ">= 5.0.0"

# For more information and examples about making a new gem, check out our
# guide at: https://bundler.io/guides/creating_gem.html
Expand Down
4 changes: 3 additions & 1 deletion lib/jbuilder/schema/renderer.rb
Expand Up @@ -37,7 +37,9 @@ def render(object = nil, title: nil, description: nil, assigns: nil, **options)
options[:locals].merge! @default_locals if @default_locals
options[:locals][:__jbuilder_schema_options] = {json: json, object: object, title: title, description: description}

@view_renderer.render(options)
@view_renderer.render(options).then do |result|
result.respond_to?(:unwrap_target!) ? result.unwrap_target! : result
end
end

# Thin wrapper around the regular Jbuilder JSON output render, which also parses it into a hash.
Expand Down
16 changes: 15 additions & 1 deletion lib/jbuilder/schema/template.rb
Expand Up @@ -54,8 +54,22 @@ def initialize(context, json: nil, **options)
@ignore_nil = false
end

class TargetWrapper
def initialize(object)
@object = object
end

# Rails 7.1 calls `to_s` on our `target!` (the return value from our templates).
# To get around that and let our inner Hash through, we add this override.
# `unwrap_target!` is added for backwardscompatibility so we get the inner Hash on Rails < 7.1.
def to_s
@object
end
alias unwrap_target! to_s
end

def target!
schema!
TargetWrapper.new(schema!)
end

def schema!
Expand Down
6 changes: 6 additions & 0 deletions test/fixtures/gem_loading_script.rb
Expand Up @@ -6,10 +6,16 @@
gemfile do
source "https://rubygems.org"

gem "activesupport", ENV["RAILS_VERSION"]

gem "jbuilder"
gem "jbuilder-schema", path: "../jbuilder-schema"
end

# These aren't loaded automatically on Rails 7.1.
require "active_support"
require "active_support/time_with_zone"

user = Struct.new(:id, :name, :email, :created_at, :updated_at).new(1, "John", "john@example.com", Time.now, Time.now)

puts Jbuilder::Schema.renderer(["test/fixtures/app/views/api/v1", "test/fixtures/app/views"]).yaml partial: "api/v1/users/user", object: user
2 changes: 1 addition & 1 deletion test/jbuilder/schema/gem_loading_test.rb
Expand Up @@ -2,7 +2,7 @@

class Jbuilder::Schema::GemLoadingTest < ActiveSupport::TestCase
test "loads when put after jbuilder" do
output = `bundle exec ruby test/fixtures/gem_loading_script.rb`
output = `RAILS_VERSION="#{ActiveSupport.gem_version}" bundle exec ruby test/fixtures/gem_loading_script.rb`

assert_includes output, "type: object"
assert_includes output, "properties:\n id:\n type: integer"
Expand Down

0 comments on commit 08804ee

Please sign in to comment.