Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
86b3946
Optimize options merging
moberegger May 8, 2025
866d289
Call _set_value directly
moberegger May 9, 2025
c6b9bff
Put template_lookup_options to avoid possible breaking change
moberegger May 12, 2025
ba3a627
Save on more merges
moberegger May 12, 2025
9ca4d05
Save memory allocation when calling render
moberegger May 12, 2025
1245776
Merge branch 'main' into moberegger/optimize_options_merges
moberegger Jul 21, 2025
8f9993a
Stop mutating options in array! method
moberegger Jul 21, 2025
d23ebe2
Add devcontainer
rafaelfranca Jul 21, 2025
7ea07fd
Fix warning about routes.rb not existing
pixeltrix Sep 18, 2024
b58d524
Use the correct path for the env command
pixeltrix Sep 18, 2024
1a86eb8
Add Rails 7.2 to the Appraisals file
pixeltrix Sep 18, 2024
13e033b
Fix method redefinition warning
pixeltrix Sep 18, 2024
f447b60
Fix constant redefinition warning
pixeltrix Sep 18, 2024
9ffacf7
Merge pull request #574 from pixeltrix/fix-warnings-and-version-constant
rafaelfranca Jul 21, 2025
8474b41
Remove _partial micro-optimization
moberegger Jul 21, 2025
7e16adf
Stop mutating options in set! method
moberegger Jul 23, 2025
b7b5abb
Stop mutating options in partial! method
moberegger Jul 23, 2025
6fd6c06
Small _set_inline_partial optimization
moberegger Jul 23, 2025
5f4af71
Merge pull request #591 from moberegger/moberegger/optimize_options_m…
rafaelfranca Jul 29, 2025
30ba7df
Prepare for 2.14.0
rafaelfranca Aug 8, 2025
a6863b5
Ensure that Jbuilder.encode properly forwards arguments to .new
flavorjones Aug 12, 2025
2400fd9
Merge pull request #601 from flavorjones/flavorjones/fix-encode-argum…
rafaelfranca Aug 12, 2025
38339ad
Prepare for 2.14.1
rafaelfranca Aug 12, 2025
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
25 changes: 25 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/ruby
{
"name": "jbuilder",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "ghcr.io/rails/devcontainer/images/ruby:3.4.5",
"features": {
"ghcr.io/devcontainers/features/github-cli:1": {}
}

// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},

// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],

// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "ruby --version",

// Configure tool-specific properties.
// "customizations": {},

// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}
5 changes: 5 additions & 0 deletions .github/workflows/ruby.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,19 @@ jobs:
gemfile:
- "rails_7_0"
- "rails_7_1"
- "rails_7_2"
- "rails_8_0"
- "rails_head"

exclude:
- ruby: '3.0'
gemfile: rails_7_2
- ruby: '3.0'
gemfile: rails_8_0
- ruby: '3.0'
gemfile: rails_head
- ruby: '3.1'
gemfile: rails_7_2
- ruby: '3.1'
gemfile: rails_8_0
- ruby: '3.1'
Expand Down
4 changes: 4 additions & 0 deletions Appraisals
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ appraise "rails-7-1" do
gem "rails", "~> 7.1.0"
end

appraise "rails-7-2" do
gem "rails", "~> 7.2.0"
end

appraise "rails-8-0" do
gem "rails", "~> 8.0.0"
end
Expand Down
2 changes: 1 addition & 1 deletion bin/test
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#!/bin/env bash
#!/usr/bin/env bash
set -e

bundle install
Expand Down
10 changes: 10 additions & 0 deletions gemfiles/rails_7_2.gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# This file was generated by Appraisal

source "https://rubygems.org"

gem "rake"
gem "mocha", require: false
gem "appraisal"
gem "rails", "~> 7.2.0"

gemspec path: "../"
5 changes: 2 additions & 3 deletions lib/jbuilder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
require 'jbuilder/blank'
require 'jbuilder/key_formatter'
require 'jbuilder/errors'
require 'jbuilder/version'
require 'json'
require 'active_support/core_ext/hash/deep_merge'

Expand All @@ -29,8 +28,8 @@ def initialize(
end

# Yields a builder and automatically turns the result into a JSON string
def self.encode(*args, &block)
new(*args, &block).target!
def self.encode(...)
new(...).target!
end

BLANK = Blank.new
Expand Down
2 changes: 1 addition & 1 deletion lib/jbuilder/errors.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# frozen_string_literal: true

require 'jbuilder/jbuilder'
require 'jbuilder/version'

class Jbuilder
class NullError < ::NoMethodError
Expand Down
2 changes: 1 addition & 1 deletion lib/jbuilder/jbuilder.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# frozen_string_literal: true

Jbuilder = Class.new(BasicObject)
require 'jbuilder/version'
50 changes: 19 additions & 31 deletions lib/jbuilder/jbuilder_template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ def partial!(*args)
if args.one? && _is_active_model?(args.first)
_render_active_model_partial args.first
else
_render_explicit_partial(*args)
options = args.extract_options!.dup
options[:partial] = args.first if args.present?
_render_partial_with_options options
end
end

Expand Down Expand Up @@ -121,7 +123,9 @@ def array!(collection = [], *args)
options = args.first

if args.one? && _partial_options?(options)
partial! options.merge(collection: collection)
options = options.dup
options[:collection] = collection
_render_partial_with_options options
else
super
end
Expand All @@ -131,7 +135,7 @@ def set!(name, object = BLANK, *args)
options = args.first

if args.one? && _partial_options?(options)
_set_inline_partial name, object, options
_set_inline_partial name, object, options.dup
else
super
end
Expand All @@ -142,14 +146,14 @@ def set!(name, object = BLANK, *args)
alias_method :method_missing, :set!

def _render_partial_with_options(options)
options.reverse_merge! locals: options.except(:partial, :as, :collection, :cached)
options.reverse_merge! ::JbuilderTemplate.template_lookup_options
options[:locals] ||= options.except(:partial, :as, :collection, :cached)
options[:handlers] ||= ::JbuilderTemplate.template_lookup_options[:handlers]
as = options[:as]

if as && options.key?(:collection)
collection = options.delete(:collection) || []
partial = options.delete(:partial)
options[:locals].merge!(json: self)
options[:locals][:json] = self
collection = EnumerableCompat.new(collection) if collection.respond_to?(:count) && !collection.respond_to?(:size)

if options.has_key?(:layout)
Expand All @@ -175,7 +179,7 @@ def _render_partial_with_options(options)
end

def _render_partial(options)
options[:locals].merge! json: self
options[:locals][:json] = self
@context.render options
end

Expand Down Expand Up @@ -231,34 +235,18 @@ def _set_inline_partial(name, object, options)
value = if object.nil?
[]
elsif _is_collection?(object)
_scope{ _render_partial_with_options options.merge(collection: object) }
else
locals = ::Hash[options[:as], object]
_scope{ _render_partial_with_options options.merge(locals: locals) }
end

set! name, value
end

def _render_explicit_partial(name_or_options, locals = {})
case name_or_options
when ::Hash
# partial! partial: 'name', foo: 'bar'
options = name_or_options
_scope do
options[:collection] = object
_render_partial_with_options options
end
else
# partial! 'name', locals: {foo: 'bar'}
if locals.one? && (locals.keys.first == :locals)
options = locals.merge(partial: name_or_options)
else
options = { partial: name_or_options, locals: locals }
_scope do
options[:locals] = { options[:as] => object }
_render_partial_with_options options
end
# partial! 'name', foo: 'bar'
as = locals.delete(:as)
options[:as] = as if as.present?
options[:collection] = locals[:collection] if locals.key?(:collection)
end

_render_partial_with_options options
_set_value name, value
end

def _render_active_model_partial(object)
Expand Down
4 changes: 2 additions & 2 deletions lib/jbuilder/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

class Jbuilder
VERSION = "2.13.0"
class Jbuilder < BasicObject
VERSION = "2.14.1"
end
7 changes: 7 additions & 0 deletions test/jbuilder_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -936,4 +936,11 @@ class JbuilderTest < ActiveSupport::TestCase
result = JSON.load(Jbuilder.encode { |json| json.time Time.parse("2018-05-13 11:51:00.485 -0400") })
assert_equal "2018-05-13T11:51:00.485-04:00", result["time"]
end

test "encode forwards options to new" do
Jbuilder.encode(key_formatter: 1, ignore_nil: 2) do |json|
assert_equal 1, json.instance_eval{ @key_formatter }
assert_equal 2, json.instance_eval{ @ignore_nil }
end
end
end
6 changes: 3 additions & 3 deletions test/scaffold_api_controller_generator_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

class ScaffoldApiControllerGeneratorTest < Rails::Generators::TestCase
tests Rails::Generators::ScaffoldControllerGenerator
arguments %w(Post title body:text images:attachments --api)
arguments %w(Post title body:text images:attachments --api --skip-routes)
destination File.expand_path('../tmp', __FILE__)
setup :prepare_destination

Expand Down Expand Up @@ -53,7 +53,7 @@ class ScaffoldApiControllerGeneratorTest < Rails::Generators::TestCase
end

test "don't use require and permit if there are no attributes" do
run_generator %w(Post --api)
run_generator %w(Post --api --skip-routes)

assert_file 'app/controllers/posts_controller.rb' do |content|
assert_match %r{def post_params}, content
Expand All @@ -62,7 +62,7 @@ class ScaffoldApiControllerGeneratorTest < Rails::Generators::TestCase
end

test 'handles virtual attributes' do
run_generator ["Message", "content:rich_text", "video:attachment", "photos:attachments"]
run_generator %w(Message content:rich_text video:attachment photos:attachments --skip-routes)

assert_file 'app/controllers/messages_controller.rb' do |content|
if Rails::VERSION::MAJOR >= 8
Expand Down
8 changes: 4 additions & 4 deletions test/scaffold_controller_generator_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
tests Rails::Generators::ScaffoldControllerGenerator
arguments %w(Post title body:text images:attachments)
arguments %w(Post title body:text images:attachments --skip-routes)
destination File.expand_path('../tmp', __FILE__)
setup :prepare_destination

Expand Down Expand Up @@ -67,7 +67,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
end

test 'controller with namespace' do
run_generator %w(Admin::Post --model-name=Post)
run_generator %w(Admin::Post --model-name=Post --skip-routes)
assert_file 'app/controllers/admin/posts_controller.rb' do |content|
assert_instance_method :create, content do |m|
assert_match %r{format\.html \{ redirect_to \[:admin, @post\], notice: "Post was successfully created\." \}}, m
Expand All @@ -84,7 +84,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
end

test "don't use require and permit if there are no attributes" do
run_generator %w(Post)
run_generator %w(Post --skip-routes)

assert_file 'app/controllers/posts_controller.rb' do |content|
assert_match %r{def post_params}, content
Expand All @@ -93,7 +93,7 @@ class ScaffoldControllerGeneratorTest < Rails::Generators::TestCase
end

test 'handles virtual attributes' do
run_generator %w(Message content:rich_text video:attachment photos:attachments)
run_generator %w(Message content:rich_text video:attachment photos:attachments --skip-routes)

assert_file 'app/controllers/messages_controller.rb' do |content|
if Rails::VERSION::MAJOR >= 8
Expand Down
2 changes: 1 addition & 1 deletion test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
ENV["RAILS_ENV"] ||= "test"

class << Rails
def cache
redefine_method :cache do
@cache ||= ActiveSupport::Cache::MemoryStore.new
end
end
Expand Down