Skip to content

Commit cf8311a

Browse files
authored
Merge pull request #15 from aktsk/release-v1
Release v1.0.0
2 parents bf4f416 + 85cdd24 commit cf8311a

20 files changed

Lines changed: 440 additions & 142 deletions

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@ jobs:
66
build:
77
strategy:
88
matrix:
9-
ruby: ['3.2', '3.3', '3.4']
10-
rails: ["7.1", "7.2", "8.0"]
9+
ruby: ['3.2', '3.3', '3.4', "4.0"]
10+
rails: ["7.1", "7.2", "8.0", "8.1"]
1111

1212
runs-on: ubuntu-latest
1313

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,5 @@ gemfiles/*.lock
99
/spec/reports/
1010
/tmp/
1111
/test/dummy_app/log/*
12+
/test/dummy_app/tmp/*
13+
/vendor/bundle

CHANGELOG.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Changelog
2+
All notable changes to this project will be documented in this file.
3+
4+
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
5+
6+
## [Unreleased]
7+
8+
## [1.0.0] - 2026-01-28
9+
### Breaking
10+
- Templates are no longer executed by invoking lambdas directly; they are compiled into methods.
11+
12+
### Changed
13+
- Cache template methods by code hash to reduce Ruby method cache invalidation.
14+
- Use a renderer subclass that includes helpers instead of extending instances.
15+
16+
### Fixed
17+
- Rename the config key `cache_enabled` to `template_cache_enabled`.
18+
- Add `/vendor/bundle` to `.gitignore`.
19+
20+
## [0.1.0] - 2025-09-10
21+
### Changed
22+
- Default JSON serializer switched from Oj to ActiveSupport::JSON to better align with Rails defaults.
23+
- Development dependencies refreshed and benchmark script fixed for the latest Ruby/Rails stacks.
24+
- README formatting and examples improved for clarity.
25+
26+
### Fixed
27+
- Resolved `MissingTemplate` errors introduced by the Rails 8 upgrade.
28+
- Added coverage for rendering when template/action names differ to avoid regressions.
29+
30+
## [0.0.0] - 2021-11-02
31+
### Added
32+
- Initial SimpleJson renderer, templates (`.simple_json.rb` lambdas), and `SimpleJson::SimpleJsonRenderable` integration.
33+
- Template caching toggle and configurable template paths.
34+
- Rails generator hooks and dummy app scaffolding for getting started.
35+
- Migration helpers for comparing SimpleJson output against existing Jbuilder views.

README.md

Lines changed: 54 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -176,110 +176,80 @@ Rails.application.config.generators.simple_json false
176176

177177
Here're the results of a benchmark (which you can find [here](https://github.com/aktsk/simple_json/blob/master/test/dummy_app/app/controllers/benchmarks_controller.rb) in this repo) rendering a collection to JSON.
178178

179-
### RAILS_ENV=development
180-
181179
```
182180
% ./bin/benchmark.sh
183181
184-
* Rendering 10 partials via render_partial
185-
Warming up --------------------------------------
186-
jb 257.000 i/100ms
187-
jbuilder 108.000 i/100ms
188-
simple_json 2.039k i/100ms
189-
Calculating -------------------------------------
190-
jb 2.611k (± 7.1%) i/s - 13.107k in 5.046110s
191-
jbuilder 1.084k (± 3.5%) i/s - 5.508k in 5.088845s
192-
simple_json 20.725k (± 4.4%) i/s - 103.989k in 5.026914s
193-
194-
Comparison:
195-
simple_json: 20725.5 i/s
196-
jb: 2610.5 i/s - 7.94x (± 0.00) slower
197-
jbuilder: 1083.8 i/s - 19.12x (± 0.00) slower
198-
199-
200-
* Rendering 100 partials via render_partial
201-
Warming up --------------------------------------
202-
jb 88.000 i/100ms
203-
jbuilder 14.000 i/100ms
204-
simple_json 290.000 i/100ms
205-
Calculating -------------------------------------
206-
jb 928.202 (± 5.0%) i/s - 4.664k in 5.037314s
207-
jbuilder 137.980 (± 6.5%) i/s - 700.000 in 5.094658s
208-
simple_json 2.931k (± 5.2%) i/s - 14.790k in 5.060707s
209-
210-
Comparison:
211-
simple_json: 2931.1 i/s
212-
jb: 928.2 i/s - 3.16x (± 0.00) slower
213-
jbuilder: 138.0 i/s - 21.24x (± 0.00) slower
214-
215-
216-
* Rendering 1000 partials via render_partial
217-
Warming up --------------------------------------
218-
jb 11.000 i/100ms
219-
jbuilder 1.000 i/100ms
220-
simple_json 29.000 i/100ms
221-
Calculating -------------------------------------
222-
jb 106.150 (± 5.7%) i/s - 539.000 in 5.094255s
223-
jbuilder 13.012 (± 7.7%) i/s - 65.000 in 5.054016s
224-
simple_json 271.683 (± 5.2%) i/s - 1.363k in 5.030646s
225-
226-
Comparison:
227-
simple_json: 271.7 i/s
228-
jb: 106.1 i/s - 2.56x (± 0.00) slower
229-
jbuilder: 13.0 i/s - 20.88x (± 0.00) slower
230-
```
231-
232-
### RAILS_ENV=production
233-
234-
```
235-
% RAILS_ENV=production ./bin/benchmark.sh
182+
SimpleJson Benchmark
183+
ruby: 4.0.1
184+
rails: 8.1.1
185+
json: 2.15.2
186+
oj: 3.16.11
187+
----------------------
236188
237-
* Rendering 10 partials via render_partial
189+
* Rendering 10 partials via render_to_string
190+
ruby 4.0.1 (2026-01-13 revision e04267a14b) +PRISM [arm64-darwin24]
238191
Warming up --------------------------------------
239-
jb 246.000 i/100ms
240-
jbuilder 97.000 i/100ms
241-
simple_json 1.957k i/100ms
192+
jb 23.000 i/100ms
193+
jbuilder 30.000 i/100ms
194+
simple_json(oj) 40.000 i/100ms
195+
simple_json(AS::json)
196+
46.000 i/100ms
242197
Calculating -------------------------------------
243-
jb 2.611k (± 4.1%) i/s - 13.038k in 5.002304s
244-
jbuilder 972.031 (± 4.7%) i/s - 4.850k in 5.001200s
245-
simple_json 20.383k (± 3.8%) i/s - 101.764k in 4.999989s
198+
jb 298.518 (±23.4%) i/s (3.35 ms/i) - 1.426k in 5.019370s
199+
jbuilder 255.925 (± 4.3%) i/s (3.91 ms/i) - 1.290k in 5.052973s
200+
simple_json(oj) 270.192 (± 3.7%) i/s (3.70 ms/i) - 1.360k in 5.039635s
201+
simple_json(AS::json)
202+
297.476 (±10.1%) i/s (3.36 ms/i) - 1.518k in 5.145803s
246203
247204
Comparison:
248-
simple_json: 20382.8 i/s
249-
jb: 2611.3 i/s - 7.81x (± 0.00) slower
250-
jbuilder: 972.0 i/s - 20.97x (± 0.00) slower
205+
jb: 298.5 i/s
206+
simple_json(AS::json): 297.5 i/s - same-ish: difference falls within error
207+
simple_json(oj): 270.2 i/s - same-ish: difference falls within error
208+
jbuilder: 255.9 i/s - same-ish: difference falls within error
251209
252210
253-
* Rendering 100 partials via render_partial
211+
* Rendering 100 partials via render_to_string
212+
ruby 4.0.1 (2026-01-13 revision e04267a14b) +PRISM [arm64-darwin24]
254213
Warming up --------------------------------------
255-
jb 90.000 i/100ms
256-
jbuilder 11.000 i/100ms
257-
simple_json 280.000 i/100ms
214+
jb 19.000 i/100ms
215+
jbuilder 14.000 i/100ms
216+
simple_json(oj) 15.000 i/100ms
217+
simple_json(AS::json)
218+
26.000 i/100ms
258219
Calculating -------------------------------------
259-
jb 883.446 (± 4.8%) i/s - 4.410k in 5.003438s
260-
jbuilder 119.932 (± 8.3%) i/s - 605.000 in 5.085382s
261-
simple_json 2.886k (± 4.2%) i/s - 14.560k in 5.054327s
220+
jb 186.051 (±12.9%) i/s (5.37 ms/i) - 912.000 in 5.075117s
221+
jbuilder 144.279 (± 2.1%) i/s (6.93 ms/i) - 728.000 in 5.048538s
222+
simple_json(oj) 159.254 (± 1.9%) i/s (6.28 ms/i) - 810.000 in 5.088178s
223+
simple_json(AS::json)
224+
249.690 (± 6.0%) i/s (4.00 ms/i) - 1.248k in 5.017042s
262225
263226
Comparison:
264-
simple_json: 2885.7 i/s
265-
jb: 883.4 i/s - 3.27x (± 0.00) slower
266-
jbuilder: 119.9 i/s - 24.06x (± 0.00) slower
227+
simple_json(AS::json): 249.7 i/s
228+
jb: 186.1 i/s - 1.34x slower
229+
simple_json(oj): 159.3 i/s - 1.57x slower
230+
jbuilder: 144.3 i/s - 1.73x slower
267231
268232
269-
* Rendering 1000 partials via render_partial
233+
* Rendering 1000 partials via render_to_string
234+
ruby 4.0.1 (2026-01-13 revision e04267a14b) +PRISM [arm64-darwin24]
270235
Warming up --------------------------------------
271-
jb 12.000 i/100ms
272-
jbuilder 1.000 i/100ms
273-
simple_json 32.000 i/100ms
236+
jb 4.000 i/100ms
237+
jbuilder 2.000 i/100ms
238+
simple_json(oj) 2.000 i/100ms
239+
simple_json(AS::json)
240+
10.000 i/100ms
274241
Calculating -------------------------------------
275-
jb 124.627 (± 4.8%) i/s - 624.000 in 5.018515s
276-
jbuilder 12.710 (± 7.9%) i/s - 64.000 in 5.073018s
277-
simple_json 314.896 (± 3.2%) i/s - 1.600k in 5.086509s
242+
jb 48.691 (± 2.1%) i/s (20.54 ms/i) - 244.000 in 5.013815s
243+
jbuilder 27.930 (± 3.6%) i/s (35.80 ms/i) - 140.000 in 5.016897s
244+
simple_json(oj) 29.083 (± 6.9%) i/s (34.38 ms/i) - 146.000 in 5.039076s
245+
simple_json(AS::json)
246+
99.792 (± 7.0%) i/s (10.02 ms/i) - 500.000 in 5.037716s
278247
279248
Comparison:
280-
simple_json: 314.9 i/s
281-
jb: 124.6 i/s - 2.53x (± 0.00) slower
282-
jbuilder: 12.7 i/s - 24.78x (± 0.00) slower
249+
simple_json(AS::json): 99.8 i/s
250+
jb: 48.7 i/s - 2.05x slower
251+
simple_json(oj): 29.1 i/s - 3.43x slower
252+
jbuilder: 27.9 i/s - 3.57x slower
283253
```
284254

285255
## Migrating from Jbuilder

gemfiles/rails_8.1.gemfile

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# frozen_string_literal: true
2+
3+
source 'https://rubygems.org'
4+
5+
gemspec path: '../'
6+
7+
gem 'jbuilder'
8+
gem 'rails', '~> 8.1.0'
9+
gem 'selenium-webdriver'

lib/simple_json/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# frozen_string_literal: true
22

33
module SimpleJson
4-
VERSION = '0.1.0'
4+
VERSION = '1.0.0'
55
end

simple_json.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Gem::Specification.new do |spec|
2525
spec.add_development_dependency 'action_args'
2626
spec.add_development_dependency 'bundler'
2727
spec.add_development_dependency 'debug'
28-
spec.add_development_dependency 'rails', '~> 8.0'
28+
spec.add_development_dependency 'rails', '>= 7.1'
2929
spec.add_development_dependency 'rake'
3030
spec.add_development_dependency 'selenium-webdriver'
3131
spec.add_development_dependency 'test-unit-rails'
Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,30 @@
11
# frozen_string_literal: true
22

3-
require 'benchmark/ips'
4-
53
class BenchmarksController < ApplicationController
6-
def index(n = '100')
7-
@comments = 1.upto(n.to_i).map { |i| Comment.new(i, nil, "comment #{i}") }
4+
before_action :prepare_comments
5+
6+
def self.comments
7+
@comments ||= 1.upto(1000).map { |i| Comment.new(i, nil, "comment #{i}") }
8+
end
9+
10+
def jb
11+
end
812

9-
jb = render_to_string 'index_jb'
10-
jbuilder = render_to_string 'index_jbuilder'
13+
def jbuilder
14+
end
1115

16+
def simple_json_oj
1217
SimpleJson.json_module = SimpleJson::Json::Oj
13-
simple_json = render_to_string 'index'
18+
end
1419

20+
def simple_json_as_json
1521
SimpleJson.json_module = ActiveSupport::JSON
16-
simple_json_active_support_json = render_to_string 'index'
17-
18-
raise 'jb != jbuilder' unless jb == jbuilder
19-
raise 'simple_json != jbuilder' unless simple_json == jbuilder
20-
raise 'simple_json_active_support_json != jbuilder' unless simple_json_active_support_json == jbuilder
21-
22-
result = Benchmark.ips do |x|
23-
x.report('jb') { render_to_string 'index_jb' }
24-
x.report('jbuilder') { render_to_string 'index_jbuilder' }
25-
x.report('simple_json(oj)') {
26-
SimpleJson.json_module = SimpleJson::Json::Oj
27-
render_to_string 'index'
28-
}
29-
x.report('simple_json(AS::json)') {
30-
SimpleJson.json_module = ActiveSupport::JSON
31-
render_to_string 'index'
32-
}
33-
x.compare!
34-
end
35-
render plain: result.data.to_s
22+
end
23+
24+
private
25+
26+
def prepare_comments
27+
n = params.fetch(:n, 100).to_i
28+
@comments = self.class.comments.take(n)
3629
end
3730
end
Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,55 @@
11
# frozen_string_literal: true
22

3-
{ body: comment.body }
3+
length = comment.body.length
4+
likes = comment.id * 3
5+
dislikes = comment.id % 4
6+
rating = (likes.to_f / (dislikes.zero? ? 1 : dislikes)).round(2)
7+
position = comment_counter + 1
8+
9+
{
10+
id: comment.id,
11+
body: comment.body,
12+
metrics: {
13+
length: length,
14+
readability: (length / 5.0).round(2),
15+
engagement: {
16+
likes: likes,
17+
dislikes: dislikes,
18+
rating: rating
19+
}
20+
},
21+
author: {
22+
name: "user-#{comment.id}",
23+
active: comment.id.odd?,
24+
badges: (comment.id % 3).zero? ? %w[insightful] : [],
25+
contact: {
26+
email: "user#{comment.id}@example.com",
27+
url: "https://example.com/users/#{comment.id}"
28+
},
29+
settings: {
30+
theme: comment.id.even? ? 'dark' : 'light',
31+
timezone: "UTC+#{(comment.id % 5) - 2}"
32+
}
33+
},
34+
tags: ['bench', "comment-#{comment.id}", (comment.id.even? ? 'even' : nil)].compact,
35+
flags: {
36+
pinned: position == 1,
37+
hidden: (comment.id % 5).zero?
38+
},
39+
metadata: {
40+
position: position,
41+
attachments: (comment.id % 4).zero? ? [{ filename: "attachment-#{comment.id}.txt", size: comment.id * 128 }] : [],
42+
history: [
43+
{ version: 1, updated_by: "user-#{comment.id}", updated_at: '2024-01-01T00:00:00Z' },
44+
{ version: 2, updated_by: "user-#{comment.id}", updated_at: '2024-01-02T00:00:00Z' }
45+
]
46+
},
47+
links: {
48+
self: "/comments/#{comment.id}",
49+
post: "/posts/#{comment.post&.id || 1}"
50+
},
51+
extras: {
52+
mood: %w[happy neutral excited][comment.id % 3],
53+
score: comment.id * position
54+
}
55+
}

0 commit comments

Comments
 (0)