Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
b928550
Ensure cache helpers exists
excid3 Sep 23, 2024
7e1f2ef
Optimize _is_collection? method
moberegger May 8, 2025
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
a9d1297
Prevent memory allocation on symbolized keys
moberegger May 28, 2025
776fa48
Update gemspec for Rails 7
moberegger May 28, 2025
91f8664
Update gemspec for Ruby 3.0
moberegger May 28, 2025
84ac767
Cleanup test cases for Rails 7
moberegger May 28, 2025
a4dc35e
Cleanup Railtie for Rails 7
moberegger May 28, 2025
c6aebce
Cleanup test cases for Ruby 3.0
moberegger May 28, 2025
e46bf9a
Refactor CollectionRenderer for Rails 7+ support
moberegger May 28, 2025
0409614
Cleanup one more spot in tests
moberegger May 29, 2025
7c58bcc
Optimize key formatter
moberegger May 30, 2025
b524e6c
Reuse key cache across template renders
moberegger May 30, 2025
47824af
Save on some memory allocation during initialization
moberegger May 30, 2025
25f48f4
Optimize extract to save on memory allocation
moberegger May 30, 2025
0622464
Back to fetch
moberegger Jun 2, 2025
a6f1eed
Use mutex for thread safety
moberegger Jun 2, 2025
964ef0b
Optimize jbuilder initialization
moberegger Jun 2, 2025
34814e7
Repurpose test to validate default formatter cache is used
moberegger Jun 3, 2025
5ea899b
Invert options nil check
moberegger Jun 3, 2025
be609e7
Cleanup initializer
moberegger Jun 3, 2025
e0d60fd
Save on call to ::Kernal.block_given?
moberegger Jun 4, 2025
a929dd6
Add frozen_string_literal
moberegger Jun 5, 2025
8246923
Merge pull request #590 from moberegger/moberegger/optimize_is_collec…
rafaelfranca Jul 18, 2025
324421b
Merge pull request #599 from affinity/moberegger/add-frozen-string-li…
rafaelfranca Jul 18, 2025
06f8eb9
Merge pull request #593 from affinity/moberegger/optimize_key_method
rafaelfranca Jul 18, 2025
6445930
Merge pull request #597 from affinity/moberegger/optimize_key_formatter
rafaelfranca Jul 18, 2025
535327a
Merge pull request #598 from affinity/moberegger/optimize_extract
rafaelfranca Jul 18, 2025
9013595
Merge pull request #575 from excid3/fix-caching-with-api-controllers
rafaelfranca Jul 18, 2025
ce9bfec
Update lib/jbuilder/railtie.rb
moberegger Jul 21, 2025
42006d6
Merge branch 'main' into remove_rails_6_support
moberegger Jul 21, 2025
64204f4
Optimize method_missing via alias_method
moberegger Jul 21, 2025
2e244c4
Merge pull request #600 from affinity/moberegger/method_missing
rafaelfranca Jul 21, 2025
f966bbf
Merge pull request #594 from affinity/remove_rails_6_support
rafaelfranca Jul 21, 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
6 changes: 6 additions & 0 deletions Appraisals
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

appraise "rails-7-0" do
gem "rails", "~> 7.0.0"
gem "concurrent-ruby", "< 1.3.5" # to avoid problem described in https://github.com/rails/rails/pull/54264
Expand All @@ -7,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: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

source "https://rubygems.org"

gemspec
Expand Down
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

require "bundler/setup"
require "bundler/gem_tasks"
require "rake/testtask"
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: "../"
8 changes: 5 additions & 3 deletions jbuilder.gemspec
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

require_relative "lib/jbuilder/version"

Gem::Specification.new do |s|
Expand All @@ -9,10 +11,10 @@ Gem::Specification.new do |s|
s.homepage = 'https://github.com/rails/jbuilder'
s.license = 'MIT'

s.required_ruby_version = '>= 2.2.2'
s.required_ruby_version = '>= 3.0.0'

s.add_dependency 'activesupport', '>= 5.0.0'
s.add_dependency 'actionview', '>= 5.0.0'
s.add_dependency 'activesupport', '>= 7.0.0'
s.add_dependency 'actionview', '>= 7.0.0'

if RUBY_ENGINE == 'rbx'
s.add_development_dependency('racc')
Expand Down
2 changes: 2 additions & 0 deletions lib/generators/rails/jbuilder_generator.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

require 'rails/generators/named_base'
require 'rails/generators/resource_helpers'

Expand Down
2 changes: 2 additions & 0 deletions lib/generators/rails/scaffold_controller_generator.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

require 'rails/generators'
require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator'

Expand Down
75 changes: 40 additions & 35 deletions lib/jbuilder.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
# frozen_string_literal: true

require 'active_support'
require 'jbuilder/jbuilder'
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 @@ -12,19 +13,23 @@ class Jbuilder
@@ignore_nil = false
@@deep_format_keys = false

def initialize(options = {})
def initialize(
key_formatter: @@key_formatter,
ignore_nil: @@ignore_nil,
deep_format_keys: @@deep_format_keys,
&block
)
@attributes = {}
@key_formatter = key_formatter
@ignore_nil = ignore_nil
@deep_format_keys = deep_format_keys

@key_formatter = options.fetch(:key_formatter){ @@key_formatter ? @@key_formatter.clone : nil}
@ignore_nil = options.fetch(:ignore_nil, @@ignore_nil)
@deep_format_keys = options.fetch(:deep_format_keys, @@deep_format_keys)

yield self if ::Kernel.block_given?
yield self if block
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 Expand Up @@ -58,20 +63,12 @@ def set!(key, value = BLANK, *args, &block)
else
# json.author @post.creator, :name, :email_address
# { "author": { "name": "David", "email_address": "[email protected]" } }
_merge_block(key){ extract! value, *args }
_merge_block(key){ _extract value, args }
end

_set_value key, result
end

def method_missing(*args, &block)
if ::Kernel.block_given?
set!(*args, &block)
else
set!(*args)
end
end

# Specifies formatting to be applied to the key. Passing in a name of a function
# will cause that function to be called on the key. So :upcase will upper case
# the key. You can also pass in lambdas for more complex transformations.
Expand Down Expand Up @@ -100,13 +97,13 @@ def method_missing(*args, &block)
#
# { "_first_name": "David" }
#
def key_format!(*args)
@key_formatter = KeyFormatter.new(*args)
def key_format!(...)
@key_formatter = KeyFormatter.new(...)
end

# Same as the instance method key_format! except sets the default.
def self.key_format(*args)
@@key_formatter = KeyFormatter.new(*args)
def self.key_format(...)
@@key_formatter = KeyFormatter.new(...)
end

# If you want to skip adding nil values to your JSON hash. This is useful
Expand Down Expand Up @@ -215,7 +212,7 @@ def array!(collection = [], *attributes, &block)
elsif ::Kernel.block_given?
_map_collection(collection, &block)
elsif attributes.any?
_map_collection(collection) { |element| extract! element, *attributes }
_map_collection(collection) { |element| _extract element, attributes }
else
_format_keys(collection.to_a)
end
Expand All @@ -241,18 +238,14 @@ def array!(collection = [], *attributes, &block)
#
# json.(@person, :name, :age)
def extract!(object, *attributes)
if ::Hash === object
_extract_hash_values(object, attributes)
else
_extract_method_values(object, attributes)
end
_extract object, attributes
end

def call(object, *attributes, &block)
if ::Kernel.block_given?
array! object, &block
else
extract! object, *attributes
_extract object, attributes
end
end

Expand Down Expand Up @@ -281,6 +274,16 @@ def target!

private

alias_method :method_missing, :set!

def _extract(object, attributes)
if ::Hash === object
_extract_hash_values(object, attributes)
else
_extract_method_values(object, attributes)
end
end

def _extract_hash_values(object, attributes)
attributes.each{ |key| _set_value key, _format_keys(object.fetch(key)) }
end
Expand Down Expand Up @@ -311,7 +314,13 @@ def _merge_values(current_value, updates)
end

def _key(key)
@key_formatter ? @key_formatter.format(key) : key.to_s
if @key_formatter
@key_formatter.format(key)
elsif key.is_a?(::Symbol)
key.name
else
key.to_s
end
end

def _format_keys(hash_or_array)
Expand Down Expand Up @@ -350,16 +359,12 @@ def _scope
end

def _is_collection?(object)
_object_respond_to?(object, :map, :count) && !(::Struct === object)
object.respond_to?(:map) && object.respond_to?(:count) && !(::Struct === object)
end

def _blank?(value=@attributes)
BLANK == value
end

def _object_respond_to?(object, *methods)
methods.all?{ |m| object.respond_to?(m) }
end
end

require 'jbuilder/railtie' if defined?(Rails)
2 changes: 2 additions & 0 deletions lib/jbuilder/blank.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# frozen_string_literal: true

class Jbuilder
class Blank
def ==(other)
Expand Down
Loading