simple_json 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []