simple_json 0.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 6a953753f69b04c130d99f00368fbc35f914b6017ae0dda0a61c9f10444b4b2b
4
+ data.tar.gz: 239878f64920bd695cac483c9b26106442eab3fa0f46e5c7d90e8caa4295de96
5
+ SHA512:
6
+ metadata.gz: 78466fb47ace2acc5c4470b2bc38b0b81d0c0aa0dd0018429259fcbe5be8604d98178f00e58a93b6366a774d0322edf0d349ab8801c3ee0863413da4fc3b6658
7
+ data.tar.gz: f3235154d6594c49fc48ba65180f6ef80048b3d49d57a7f55cff933e95eb5409172dc8e002b0f48ef93db5afb57c2362a2ea1d0d99bf4f3738e3fff2f4718cfc
@@ -0,0 +1,57 @@
1
+ name: Ruby
2
+
3
+ on: [push, pull_request]
4
+
5
+ jobs:
6
+ build:
7
+ strategy:
8
+ matrix:
9
+ ruby_version: ['3.0', '2.7', '2.6']
10
+
11
+ gemfile:
12
+ - gemfiles/rails_61.gemfile
13
+ - gemfiles/rails_60.gemfile
14
+
15
+ include:
16
+ - ruby_version: ruby-head
17
+ gemfile: gemfiles/rails_edge.gemfile
18
+ allow_failures: 'true'
19
+ - ruby_version: '3.0'
20
+ gemfile: gemfiles/rails_edge.gemfile
21
+ allow_failures: 'true'
22
+
23
+ - ruby_version: '2.7'
24
+ gemfile: gemfiles/rails_edge.gemfile
25
+ allow_failures: 'true'
26
+
27
+ - ruby_version: '2.6'
28
+ gemfile: gemfiles/rails_52.gemfile
29
+ - ruby_version: '2.6'
30
+ gemfile: gemfiles/rails_51.gemfile
31
+ - ruby_version: '2.6'
32
+ gemfile: gemfiles/rails_50.gemfile
33
+ - ruby_version: '2.6'
34
+ gemfile: gemfiles/rails_42.gemfile
35
+ bundler_version: '1'
36
+
37
+ - ruby_version: '2.5'
38
+ gemfile: gemfiles/rails_52.gemfile
39
+ - ruby_version: '2.5'
40
+ gemfile: gemfiles/rails_42.gemfile
41
+ bundler_version: '1'
42
+
43
+ runs-on: ubuntu-latest
44
+
45
+ env:
46
+ BUNDLE_GEMFILE: ${{ matrix.gemfile }}
47
+
48
+ steps:
49
+ - uses: actions/checkout@v2
50
+ - uses: ruby/setup-ruby@v1
51
+ with:
52
+ ruby-version: ${{ matrix.ruby_version }}
53
+ bundler: ${{ matrix.bundler_version }}
54
+ bundler-cache: true
55
+ continue-on-error: ${{ matrix.allow_failures == 'true' }}
56
+ - run: bundle exec rake
57
+ continue-on-error: ${{ matrix.allow_failures == 'true' }}
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ gemfiles/*.lock
5
+ /_yardoc/
6
+ /coverage/
7
+ /doc/
8
+ /pkg/
9
+ /spec/reports/
10
+ /tmp/
11
+ /test/dummy_app/log/*
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ # Specify your gem's dependencies in jb.gemspec
6
+ gemspec
7
+
8
+ # For benchmarking
9
+ gem 'action_args'
10
+
11
+ gem 'benchmark-ips'
12
+ gem 'jb'
13
+ gem 'jbuilder'
14
+
15
+ gem 'rubocop'
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Akira Matsuda
4
+ Copyright (c) 2021 Jingyuan Zhao
5
+
6
+ Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ of this software and associated documentation files (the "Software"), to deal
8
+ in the Software without restriction, including without limitation the rights
9
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ copies of the Software, and to permit persons to whom the Software is
11
+ furnished to do so, subject to the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be included in
14
+ all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,263 @@
1
+ # SimpleJson
2
+ A simple & fast solution for Rails JSON rendering.
3
+
4
+ ## Get started
5
+ In Gemfile
6
+ ```ruby
7
+ gem 'simple_json'
8
+ ```
9
+
10
+ In controller
11
+ ```ruby
12
+ class ApplicationController < ActionController::Base
13
+ include SimpleJson::SimpleJsonRenderable
14
+
15
+ ...
16
+ end
17
+ ```
18
+
19
+ In view, create your simple_json template file in `app/views`.
20
+
21
+ ```ruby
22
+ # {controller_name}/{action_name}.simple_json.rb
23
+ {
24
+ key1: @value1,
25
+ key2: helper(@value2),
26
+ key3: partial!("partial_path", param1: param1, param2: param2)
27
+ }
28
+ ```
29
+
30
+ And, partial as well.
31
+
32
+ ```ruby
33
+ ->(param1:, param2:) {
34
+ {
35
+ key1: param1,
36
+ key2: param2,
37
+ }
38
+ }
39
+
40
+ ```
41
+
42
+ That's all!
43
+
44
+ Have fun!
45
+
46
+ ## Special thanks
47
+ This project is built on work of [jb](https://github.com/amatsuda/jb).
48
+
49
+ ## Template Syntax
50
+ SimpleJson templates are simply lambda objects that return data(Hashes or Arrays) for json.
51
+ ```ruby
52
+ -> {
53
+ {
54
+ key: @value,
55
+ }
56
+ }
57
+ ```
58
+ When no parameters specified, `-> {` and `}` can be omitted.
59
+ ```ruby
60
+ {
61
+ key: @value,
62
+ }
63
+ ```
64
+
65
+ Use `partial!` method to call another template in template. Note that path is always required. Also, there is no difference between partials and templates, so that you cannot omit `_` before template name.
66
+ (So, no omitting for template path is allowed.)
67
+
68
+ ```ruby
69
+ # app/views/posts/show.simple_json.rb
70
+ {
71
+ title: @post.title,
72
+ comments: @post.comments.map { |comment| partial!('comments/_comment', comment: comment) }
73
+ }
74
+ ```
75
+
76
+ ```ruby
77
+ # app/views/comments/_comment.simple_json.rb
78
+ ->(comment:) {
79
+ {
80
+ body: comment.body
81
+ }
82
+ }
83
+ ```
84
+
85
+ Cache helpers of jbuilder is similar.
86
+ ```ruby
87
+ cache! key, options do
88
+ data_to_cache
89
+ end
90
+ ```
91
+
92
+ Cache helpers uses `Rails.cache` to cache, so Multiple key, expiration is available. Make sure `perform_caching` is enabled.
93
+ ```ruby
94
+ cache! [key1, key2], expires_in: 10.minutes do
95
+ data_to_cache
96
+ end
97
+ ```
98
+
99
+ `cache_if!` is also available
100
+ ```ruby
101
+ cache_if! boolean, key1, options do
102
+ data_to_cache
103
+ end
104
+ ```
105
+
106
+ You can set key_prefix for caching like this
107
+ ```ruby
108
+ SimpleJson.cache_key_prefix = "MY_PREFIX"
109
+ ```
110
+
111
+ ## configurations
112
+ Load all templates on boot. (For production)
113
+ Templates loaded will not load again, so it is not recommended in development environment.
114
+ ```ruby
115
+ # config/environments/production.rb
116
+ SimpleJson.enable_template_cache
117
+ ```
118
+
119
+ The default path for templates is `app/views`, you can change it by
120
+ ```ruby
121
+ SimpleJson.template_paths.append("app/simple_jsons")
122
+ # or
123
+ SimpleJson.template_paths=["app/views", "app/simple_jsons"]
124
+ ```
125
+ Note that these paths should not be eager loaded cause using .rb as suffix.
126
+
127
+ SimpleJson uses Oj as json serializer by default. Modules with `#encode` and `#decode` method can be used here.
128
+ ```ruby
129
+ SimpleJson.json_module = ActiveSupport::JSON
130
+ ```
131
+
132
+ ## The Generator
133
+ SimpleJson extends the default Rails scaffold generator and adds some simple_json templates. If you don't need them, please configure like so.
134
+ ```rb
135
+ Rails.application.config.generators.simple_json false
136
+ ```
137
+
138
+ ## Benchmarks
139
+ 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.
140
+
141
+ ### RAILS_ENV=development
142
+ ```
143
+ % ./bin/benchmark.sh
144
+
145
+ * Rendering 10 partials via render_partial
146
+ Warming up --------------------------------------
147
+ jb 257.000 i/100ms
148
+ jbuilder 108.000 i/100ms
149
+ simple_json 2.039k i/100ms
150
+ Calculating -------------------------------------
151
+ jb 2.611k (± 7.1%) i/s - 13.107k in 5.046110s
152
+ jbuilder 1.084k (± 3.5%) i/s - 5.508k in 5.088845s
153
+ simple_json 20.725k (± 4.4%) i/s - 103.989k in 5.026914s
154
+
155
+ Comparison:
156
+ simple_json: 20725.5 i/s
157
+ jb: 2610.5 i/s - 7.94x (± 0.00) slower
158
+ jbuilder: 1083.8 i/s - 19.12x (± 0.00) slower
159
+
160
+
161
+ * Rendering 100 partials via render_partial
162
+ Warming up --------------------------------------
163
+ jb 88.000 i/100ms
164
+ jbuilder 14.000 i/100ms
165
+ simple_json 290.000 i/100ms
166
+ Calculating -------------------------------------
167
+ jb 928.202 (± 5.0%) i/s - 4.664k in 5.037314s
168
+ jbuilder 137.980 (± 6.5%) i/s - 700.000 in 5.094658s
169
+ simple_json 2.931k (± 5.2%) i/s - 14.790k in 5.060707s
170
+
171
+ Comparison:
172
+ simple_json: 2931.1 i/s
173
+ jb: 928.2 i/s - 3.16x (± 0.00) slower
174
+ jbuilder: 138.0 i/s - 21.24x (± 0.00) slower
175
+
176
+
177
+ * Rendering 1000 partials via render_partial
178
+ Warming up --------------------------------------
179
+ jb 11.000 i/100ms
180
+ jbuilder 1.000 i/100ms
181
+ simple_json 29.000 i/100ms
182
+ Calculating -------------------------------------
183
+ jb 106.150 (± 5.7%) i/s - 539.000 in 5.094255s
184
+ jbuilder 13.012 (± 7.7%) i/s - 65.000 in 5.054016s
185
+ simple_json 271.683 (± 5.2%) i/s - 1.363k in 5.030646s
186
+
187
+ Comparison:
188
+ simple_json: 271.7 i/s
189
+ jb: 106.1 i/s - 2.56x (± 0.00) slower
190
+ jbuilder: 13.0 i/s - 20.88x (± 0.00) slower
191
+ ```
192
+ ### RAILS_ENV=production
193
+ ```
194
+ % RAILS_ENV=production ./bin/benchmark.sh
195
+
196
+ * Rendering 10 partials via render_partial
197
+ Warming up --------------------------------------
198
+ jb 246.000 i/100ms
199
+ jbuilder 97.000 i/100ms
200
+ simple_json 1.957k i/100ms
201
+ Calculating -------------------------------------
202
+ jb 2.611k (± 4.1%) i/s - 13.038k in 5.002304s
203
+ jbuilder 972.031 (± 4.7%) i/s - 4.850k in 5.001200s
204
+ simple_json 20.383k (± 3.8%) i/s - 101.764k in 4.999989s
205
+
206
+ Comparison:
207
+ simple_json: 20382.8 i/s
208
+ jb: 2611.3 i/s - 7.81x (± 0.00) slower
209
+ jbuilder: 972.0 i/s - 20.97x (± 0.00) slower
210
+
211
+
212
+ * Rendering 100 partials via render_partial
213
+ Warming up --------------------------------------
214
+ jb 90.000 i/100ms
215
+ jbuilder 11.000 i/100ms
216
+ simple_json 280.000 i/100ms
217
+ Calculating -------------------------------------
218
+ jb 883.446 (± 4.8%) i/s - 4.410k in 5.003438s
219
+ jbuilder 119.932 (± 8.3%) i/s - 605.000 in 5.085382s
220
+ simple_json 2.886k (± 4.2%) i/s - 14.560k in 5.054327s
221
+
222
+ Comparison:
223
+ simple_json: 2885.7 i/s
224
+ jb: 883.4 i/s - 3.27x (± 0.00) slower
225
+ jbuilder: 119.9 i/s - 24.06x (± 0.00) slower
226
+
227
+
228
+ * Rendering 1000 partials via render_partial
229
+ Warming up --------------------------------------
230
+ jb 12.000 i/100ms
231
+ jbuilder 1.000 i/100ms
232
+ simple_json 32.000 i/100ms
233
+ Calculating -------------------------------------
234
+ jb 124.627 (± 4.8%) i/s - 624.000 in 5.018515s
235
+ jbuilder 12.710 (± 7.9%) i/s - 64.000 in 5.073018s
236
+ simple_json 314.896 (± 3.2%) i/s - 1.600k in 5.086509s
237
+
238
+ Comparison:
239
+ simple_json: 314.9 i/s
240
+ jb: 124.6 i/s - 2.53x (± 0.00) slower
241
+ jbuilder: 12.7 i/s - 24.78x (± 0.00) slower
242
+ ```
243
+
244
+ ## Migrating from Jbuilder
245
+ When migrating from Jbuilder, you can include `Migratable` in controller for migrating mode.
246
+ ```
247
+ include SimpleJson::SimpleJsonRenderable
248
+ include SimpleJson::Migratable
249
+ ```
250
+
251
+ In migrating mode
252
+ - Comparision will be performed for simple_json and ActionView render(Jbuilder) result.
253
+ - simple_json partials not found will use Jbuilder partial as an alternative.
254
+
255
+ Note that render will be performed twice, so using it in production mode is not recommended.
256
+
257
+ ## Contributing
258
+
259
+ Pull requests are welcome on GitHub at https://github.com/aktsk/simple_json.
260
+
261
+ ## License
262
+
263
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
4
+ require 'rake/testtask'
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << 'test'
8
+ t.libs << 'lib'
9
+ t.test_files = FileList['test/**/*_test.rb']
10
+ end
11
+
12
+ task default: :test
data/bin/benchmark.sh ADDED
@@ -0,0 +1,5 @@
1
+ #!/bin/bash
2
+
3
+ cd $(dirname "$0")/../test/dummy_app
4
+
5
+ BUNDLE_GEMFILE=../../gemfiles/benchmark.gemfile bundle e rails r bin/bench.rb
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require 'bundler/setup'
5
+ require 'jb'
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require 'irb'
15
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gem 'rails'
6
+ gem 'rake'
7
+
8
+ gem 'action_args'
9
+ gem 'benchmark-ips'
10
+ gem 'jb'
11
+ gem 'jbuilder'
12
+ gem 'oj'
13
+ gem 'pry'
@@ -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 'nokogiri', RUBY_VERSION < '2.1' ? '~> 1.6.0' : '>= 1.7'
9
+ gem 'rails', '~> 4.2.0'
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec path: '../'
6
+
7
+ gem 'jbuilder'
8
+ gem 'rails', '~> 5.0.0'
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec path: '../'
6
+
7
+ gem 'jbuilder'
8
+ gem 'rails', '~> 5.1.0'
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec path: '../'
6
+
7
+ gem 'jbuilder'
8
+ gem 'rails', '~> 5.2.0'
@@ -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', '~> 6.0.0'
9
+ gem 'selenium-webdriver'
@@ -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', '~> 6.1.0'
9
+ gem 'selenium-webdriver'
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec path: '../'
6
+
7
+ git_source(:github) do |repo_name|
8
+ repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?('/')
9
+ "https://github.com/#{repo_name}.git"
10
+ end
11
+
12
+ github 'rails/rails' do
13
+ gem 'rails'
14
+ end
15
+
16
+ gem 'selenium-webdriver'
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/rails/scaffold_controller/scaffold_controller_generator'
5
+
6
+ module Rails
7
+ module Generators
8
+ class ScaffoldControllerGenerator
9
+ hook_for :simple_json, type: :boolean, default: true
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators/named_base'
4
+ require 'rails/generators/resource_helpers'
5
+
6
+ module Rails
7
+ module Generators
8
+ class SimpleJsonGenerator < NamedBase # :nodoc:
9
+ include Rails::Generators::ResourceHelpers
10
+
11
+ source_root File.expand_path('../templates', __FILE__)
12
+
13
+ argument :attributes, type: :array, default: [], banner: 'field:type field:type'
14
+
15
+ def create_root_folder
16
+ path = File.join('app/views', controller_file_path)
17
+ empty_directory path unless File.directory?(path)
18
+ end
19
+
20
+ def copy_view_files
21
+ template 'index.simple_json.rb', File.join('app/views', controller_file_path, 'index.simple_json.rb')
22
+ template 'show.simple_json.rb', File.join('app/views', controller_file_path, 'show.simple_json.rb')
23
+ end
24
+
25
+ private
26
+
27
+ def attributes_names
28
+ [:id] + super
29
+ end
30
+
31
+ def attributes_names_with_timestamps
32
+ attributes_names + %w[created_at updated_at]
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,8 @@
1
+ @<%= plural_table_name %>.map do |<%= singular_table_name %>|
2
+ {
3
+ <% attributes_names.each do |attr| -%>
4
+ <%= attr %>: <%= singular_table_name %>.<%= attr %>,
5
+ <% end -%>
6
+ url: <%= singular_table_name %>_url(<%= singular_table_name %>, format: :json)
7
+ }
8
+ end
@@ -0,0 +1,3 @@
1
+ {
2
+ <%= attributes_names_with_timestamps.map {|attr| "#{attr}: @#{singular_table_name}.#{attr}"}.join(",\n ") %>
3
+ }
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'oj'
4
+
5
+ module SimpleJson
6
+ module Json
7
+ class Oj
8
+ def self.encode(json)
9
+ ::Oj.dump(json, mode: :rails)
10
+ end
11
+
12
+ def self.decode(json_string)
13
+ ::Oj.load(json_string, mode: :rails)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleJson
4
+ # The module for migration from jbuilder to simple json.
5
+ # Using this will render view twice, and may cause http response headers change.
6
+ #
7
+ # DO NOT INCLUDE THIS IN PRODUCTION!
8
+ #
9
+ # class XXXController < ActionController::Base
10
+ # include SimpleJson::SimpleJsonRenderable
11
+ # include SimpleJson::Migratable
12
+ #
13
+ # ...
14
+ # end
15
+ module Migratable
16
+ class DifferentViewOutput < RuntimeError; end
17
+
18
+ extend ActiveSupport::Concern
19
+
20
+ def render_json_template(template_name, **options)
21
+ fix_current_time do
22
+ json = simple_renderer.render(template_name)
23
+ result = SimpleJson.json_module.encode(json)
24
+ result_super = render_to_body(options.merge({ skip_simple_json: true }))
25
+
26
+ raise DifferentViewOutput if result != result_super
27
+
28
+ result
29
+ end
30
+ end
31
+
32
+ def simple_renderer
33
+ @simple_renderer ||= SimpleJsonRendererForMigration.new(self).tap do |r|
34
+ r.extend(_helpers) if respond_to?(:_helpers)
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def fix_current_time
41
+ return yield if Time.method_defined? :__current
42
+
43
+ begin
44
+ time = Time.current
45
+ singleton_class = Time.singleton_class
46
+ singleton_class.alias_method :__current, :current
47
+ Time.define_singleton_method(:current) do
48
+ time
49
+ end
50
+ yield
51
+ ensure
52
+ # alternative code for active support under 5.1 for
53
+ # `singleton_class.silence_redefinition_of_method :current`
54
+ singleton_class.alias_method :current, :current
55
+ singleton_class.alias_method :current, :__current
56
+ singleton_class.undef_method :__current
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleJson
4
+ class Railtie < ::Rails::Railtie
5
+ end
6
+ end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleJson
4
+ # The module for overriding rendering with SimpleJson
5
+ #
6
+ # class XXXController < ActionController::Base
7
+ # include SimpleJson::SimpleJsonRenderable
8
+ #
9
+ # ...
10
+ # end
11
+ module SimpleJsonRenderable
12
+ extend ActiveSupport::Concern
13
+
14
+ # monkey-patch for ActionController::ImplicitRender#default_render bypassing template search
15
+ def default_render(*args)
16
+ return super unless request.format.json?
17
+
18
+ @simple_json_template = simple_renderer.renderer(template_name(action_name))
19
+ if @simple_json_template
20
+ render(*args)
21
+ else
22
+ super
23
+ end
24
+ end
25
+
26
+ def render_to_body(options)
27
+ return super unless request.format.json?
28
+ return super if options[:skip_simple_json]
29
+ # use super when any of [:body, :plain, :html] exist in options
30
+ return super if self.class::RENDER_FORMATS_IN_PRIORITY.any? { |key| options.key? key }
31
+
32
+ template_name = template_name(options[:template] || action_name)
33
+ if options.key?(:json)
34
+ process_options(options)
35
+ @rendered_format = 'application/json; charset=utf-8'
36
+ SimpleJson.json_module.encode(options[:json])
37
+ elsif simple_renderer.renderer(template_name)
38
+ process_options(options)
39
+ @rendered_format = 'application/json; charset=utf-8'
40
+ render_json_template(template_name, **options)
41
+ else
42
+ super
43
+ end
44
+ end
45
+
46
+ def process_options(options)
47
+ head options[:status] if options.key?(:status)
48
+ end
49
+
50
+ def rendered_format
51
+ return @rendered_format if defined?(@rendered_format)
52
+
53
+ super
54
+ end
55
+
56
+ def simple_renderer
57
+ @simple_renderer ||= SimpleJsonRenderer.new(self).tap do |r|
58
+ r.extend(_helpers) if respond_to?(:_helpers)
59
+ end
60
+ end
61
+
62
+ def render_json_template(template_name, **_options)
63
+ json = simple_renderer.render(template_name)
64
+ SimpleJson.json_module.encode(json)
65
+ end
66
+
67
+ included do
68
+ if SimpleJson.template_cache_enabled? && !SimpleJsonRenderer.templates_loaded?
69
+ SimpleJsonRenderer.load_all_templates!
70
+ end
71
+ end
72
+
73
+ def template_path
74
+ self.class.name.delete_suffix('Controller').underscore
75
+ end
76
+
77
+ def template_name(name)
78
+ return name if name.include?('/')
79
+
80
+ "#{template_path}/#{name}"
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,112 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleJson
4
+ class SimpleJsonRenderer
5
+ class TemplateNotFound < RuntimeError; end
6
+
7
+ attr_reader :controller
8
+
9
+ @templates_loaded = false
10
+
11
+ class << self
12
+ def templates_loaded?
13
+ @templates_loaded
14
+ end
15
+
16
+ def load_all_templates!
17
+ @renderers = {}
18
+
19
+ SimpleJson.template_paths.each do |path|
20
+ template_files = Rails.root.glob("#{path}/**/*.simple_json.rb")
21
+ template_files.each do |file_path|
22
+ template_path = file_path.relative_path_from(Rails.root.join(path)).to_path.delete_suffix('.simple_json.rb')
23
+ @renderers[template_path] = SimpleJsonTemplate.new(file_path.to_path).renderer
24
+ end
25
+ end
26
+ @templates_loaded = true
27
+ end
28
+
29
+ def load_template(template_path)
30
+ if SimpleJson.template_cache_enabled?
31
+ load_all_templates! unless templates_loaded?
32
+ renderers[template_path]
33
+ else
34
+ load_template_from_file(template_path)
35
+ end
36
+ end
37
+
38
+ def load_template_from_file(template_path)
39
+ SimpleJson.template_paths.each do |path|
40
+ file_path = Rails.root.join("#{path}/#{template_path}.simple_json.rb").to_path
41
+ return SimpleJsonTemplate.new(file_path).renderer if File.exist?(file_path)
42
+ end
43
+
44
+ nil
45
+ end
46
+
47
+ def renderers
48
+ @renderers ||= {}
49
+ end
50
+
51
+ def clear_renderers
52
+ @renderers = {}
53
+ @templates_loaded = false
54
+ end
55
+ end
56
+
57
+ def initialize(controller)
58
+ @controller = controller
59
+ @_assigns = controller.view_assigns.each { |key, value| instance_variable_set("@#{key}", value) }
60
+ end
61
+
62
+ def renderer(template_path)
63
+ renderers[template_path] || self.class.load_template(template_path).tap do |renderer|
64
+ renderers[template_path] = renderer
65
+ end
66
+ end
67
+
68
+ def renderers
69
+ @renderers ||= {}
70
+ end
71
+
72
+ def render(template_name, **params)
73
+ if !params.empty?
74
+ instance_exec(**params, &renderer(template_name))
75
+ else
76
+ instance_exec(&renderer(template_name))
77
+ end
78
+ end
79
+
80
+ def partial!(template_name, **params)
81
+ raise TemplateNotFound, "#{template_name} not found" unless renderer(template_name)
82
+
83
+ render(template_name, **params)
84
+ end
85
+
86
+ def cache!(key, **options, &block)
87
+ if controller.perform_caching
88
+ key = Array.wrap(key).unshift(SimpleJson.cache_key_prefix)
89
+ Rails.cache.fetch(key, options, &block)
90
+ else
91
+ yield
92
+ end
93
+ end
94
+
95
+ def cache_if!(condition, *args, **options, &block)
96
+ condition ? cache!(*args, **options, &block) : yield
97
+ end
98
+
99
+ if ENV['NEWRELIC_MONITOR_MODE'] == 'true' && defined?(::NewRelic::Agent)
100
+ include ::NewRelic::Agent::MethodTracer
101
+
102
+ def render_with_tracing(template_name, params)
103
+ self.class.trace_execution_scoped("View/#{template_name}.simple_json.rb/Rendering") do
104
+ render_without_tracing(template_name, params)
105
+ end
106
+ end
107
+
108
+ alias render_without_tracing render
109
+ alias render render_with_tracing
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleJson
4
+ class SimpleJsonRendererForMigration < SimpleJsonRenderer
5
+ @templates_loaded = false
6
+
7
+ def partial!(template_name, **params)
8
+ if renderer(template_name)
9
+ render(template_name, **params)
10
+ else
11
+ warn_template_not_exist(template_name)
12
+
13
+ if @controller.respond_to?(:helpers)
14
+ result = @controller.helpers.render(template_name.gsub('/_', '/'), params)
15
+ else
16
+ @controller_helper_proxy ||= @controller.view_context
17
+ result = @controller_helper_proxy.render(template_name.gsub('/_', '/'), params)
18
+ end
19
+
20
+ if result.is_a?(String)
21
+ SimpleJson.json_module.decode result
22
+ else
23
+ result
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def warn_template_not_exist(template_name)
31
+ @template_not_exist_warning ||= {}
32
+ return if @template_not_exist_warning[template_name]
33
+
34
+ warn "simple_json template '#{template_name}' not exist!"
35
+ @template_not_exist_warning[template_name] = true
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleJson
4
+ class SimpleJsonTemplate
5
+ def initialize(path)
6
+ @path = path
7
+ @source = File.read(path)
8
+ end
9
+
10
+ def renderer
11
+ @renderer ||= eval(code, TOPLEVEL_BINDING, @path) # rubocop:disable Security/Eval
12
+ end
13
+
14
+ def code
15
+ @code ||= lambda_stringify(@source)
16
+ end
17
+
18
+ private
19
+
20
+ def lambda_stringify(source)
21
+ return source if source.match?(/^(?:\s*(?:#.*?)?\n)*\s*->/)
22
+
23
+ "-> { #{source} }"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module SimpleJson
4
+ VERSION = '0.0.0'
5
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'simple_json/version'
4
+
5
+ require 'simple_json/simple_json_renderable'
6
+ require 'simple_json/simple_json_renderer'
7
+ require 'simple_json/simple_json_template'
8
+
9
+ require 'simple_json/migratable'
10
+ require 'simple_json/simple_json_renderer_for_migration'
11
+ require 'simple_json/json'
12
+
13
+ module SimpleJson
14
+ @config = {
15
+ cache_enabled: false,
16
+ template_paths: ['app/views'],
17
+ cache_key_prefix: 'simple_json/views',
18
+ default_json_module: Json::Oj
19
+ }
20
+
21
+ class << self
22
+ attr_accessor :config
23
+
24
+ def enable_template_cache
25
+ config[:template_cache_enabled] = true
26
+ end
27
+
28
+ def disable_template_cache
29
+ config[:template_cache_enabled] = false
30
+ SimpleJsonRenderer.clear_renderers
31
+ end
32
+
33
+ def template_cache_enabled?
34
+ config[:template_cache_enabled]
35
+ end
36
+
37
+ def template_paths
38
+ config[:template_paths]
39
+ end
40
+
41
+ def template_paths=(template_paths)
42
+ config[:template_paths] = template_paths
43
+ end
44
+
45
+ def cache_key_prefix
46
+ config[:cache_key_prefix]
47
+ end
48
+
49
+ def cache_key_prefix=(cache_key_prefix)
50
+ config[:cache_key_prefix] = cache_key_prefix
51
+ end
52
+
53
+ def json_module
54
+ @json_module ||= config[:default_json_module]
55
+ end
56
+
57
+ attr_writer :json_module
58
+ end
59
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'simple_json/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'simple_json'
9
+ spec.version = SimpleJson::VERSION
10
+ spec.authors = ['Jingyuan Zhao']
11
+ spec.email = ['jingyuan.zhao@aktsk.jp']
12
+
13
+ spec.summary = 'Faster and simpler Jbuilder alternative'
14
+ spec.description = 'Faster and simpler JSON renderer for Rails'
15
+ spec.homepage = 'https://github.com/aktsk/simple_json'
16
+ spec.license = 'MIT'
17
+
18
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+ spec.bindir = 'exe'
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ['lib']
22
+
23
+ spec.add_dependency 'oj', '~> 3.13'
24
+
25
+ spec.add_development_dependency 'action_args'
26
+ spec.add_development_dependency 'bundler'
27
+ spec.add_development_dependency 'byebug'
28
+ spec.add_development_dependency 'rails'
29
+ spec.add_development_dependency 'rake'
30
+ spec.add_development_dependency 'selenium-webdriver'
31
+ spec.add_development_dependency 'test-unit-rails'
32
+
33
+ spec.required_ruby_version = '>= 2.5.0'
34
+ end
metadata ADDED
@@ -0,0 +1,186 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simple_json
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Jingyuan Zhao
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2021-11-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: oj
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.13'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: action_args
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: byebug
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rails
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: selenium-webdriver
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: test-unit-rails
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: Faster and simpler JSON renderer for Rails
126
+ email:
127
+ - jingyuan.zhao@aktsk.jp
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".github/workflows/main.yml"
133
+ - ".gitignore"
134
+ - Gemfile
135
+ - LICENSE.txt
136
+ - README.md
137
+ - Rakefile
138
+ - bin/benchmark.sh
139
+ - bin/console
140
+ - bin/setup
141
+ - gemfiles/benchmark.gemfile
142
+ - gemfiles/rails_42.gemfile
143
+ - gemfiles/rails_50.gemfile
144
+ - gemfiles/rails_51.gemfile
145
+ - gemfiles/rails_52.gemfile
146
+ - gemfiles/rails_60.gemfile
147
+ - gemfiles/rails_61.gemfile
148
+ - gemfiles/rails_edge.gemfile
149
+ - lib/generators/rails/scaffold_controller_generator.rb
150
+ - lib/generators/rails/simple_json_generator.rb
151
+ - lib/generators/rails/templates/index.simple_json.rb
152
+ - lib/generators/rails/templates/show.simple_json.rb
153
+ - lib/simple_json.rb
154
+ - lib/simple_json/json.rb
155
+ - lib/simple_json/migratable.rb
156
+ - lib/simple_json/railtie.rb
157
+ - lib/simple_json/simple_json_renderable.rb
158
+ - lib/simple_json/simple_json_renderer.rb
159
+ - lib/simple_json/simple_json_renderer_for_migration.rb
160
+ - lib/simple_json/simple_json_template.rb
161
+ - lib/simple_json/version.rb
162
+ - simple_json.gemspec
163
+ homepage: https://github.com/aktsk/simple_json
164
+ licenses:
165
+ - MIT
166
+ metadata: {}
167
+ post_install_message:
168
+ rdoc_options: []
169
+ require_paths:
170
+ - lib
171
+ required_ruby_version: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - ">="
174
+ - !ruby/object:Gem::Version
175
+ version: 2.5.0
176
+ required_rubygems_version: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ requirements: []
182
+ rubygems_version: 3.0.3
183
+ signing_key:
184
+ specification_version: 4
185
+ summary: Faster and simpler Jbuilder alternative
186
+ test_files: []