From 570e07fe570a8e813e3024cfbfb48a72d33182ea Mon Sep 17 00:00:00 2001 From: "jingyuan.zhao" Date: Tue, 27 Jan 2026 11:47:00 +0900 Subject: [PATCH 1/3] Define method using class_eval instead of define_method. --- lib/simple_json/simple_json_renderer.rb | 8 ++----- lib/simple_json/simple_json_template.rb | 30 +++++++++++++++++++++---- 2 files changed, 28 insertions(+), 10 deletions(-) diff --git a/lib/simple_json/simple_json_renderer.rb b/lib/simple_json/simple_json_renderer.rb index 1b098d1..9f1c35c 100644 --- a/lib/simple_json/simple_json_renderer.rb +++ b/lib/simple_json/simple_json_renderer.rb @@ -39,9 +39,7 @@ def load_template_from_file(template_path) SimpleJson.template_paths.each do |path| file_path = Rails.root.join("#{path}/#{template_path}.simple_json.rb").to_path - if File.exist?(file_path) - return define_template_method(template_path, file_path) - end + return define_template_method(template_path, file_path) if File.exist?(file_path) end nil @@ -52,9 +50,7 @@ def define_template_method(template_path, file_path) @template_num += 1 method_name = :"template_#{@template_num}" render_methods[template_path] = method_name - define_method(method_name, &SimpleJsonTemplate.new(file_path).lambda) - - return method_name + SimpleJsonTemplate.new(file_path).define_to_class(self, method_name) end def render_methods diff --git a/lib/simple_json/simple_json_template.rb b/lib/simple_json/simple_json_template.rb index e64a9e5..4d4c3ed 100644 --- a/lib/simple_json/simple_json_template.rb +++ b/lib/simple_json/simple_json_template.rb @@ -7,20 +7,42 @@ def initialize(path) @source = File.read(path) end - def lambda - @lambda ||= eval(code, TOPLEVEL_BINDING, @path) # rubocop:disable Security/Eval + def define_to_class(klass, method_name) + method_string = to_method_string(method_name) + klass.class_eval(method_string, @path) + + method_name end + private + def code @code ||= lambda_stringify(@source) end - private - def lambda_stringify(source) return source if source.match?(/^(?:\s*(?:#.*?)?\n)*\s*->/) "-> { #{source} }" end + + def method_string_from_lambda(source, method_name) + replaced = source.sub(/\A(?(?:[ \t]*(?:#.*?)?\n)*)(?[ \t]*)->\s*(?\([^)]*\))?[ \t]*\{?[ \t]*(?[^\n]*)/) do + header = "#{Regexp.last_match(:prefix)}#{Regexp.last_match(:indent)}def #{method_name}#{Regexp.last_match(:args)}" + rest = Regexp.last_match(:rest) + rest.strip.empty? ? header : "#{header}; #{rest.lstrip}" + end + return unless replaced != source + + brace_index = replaced.rindex('}') + return replaced unless brace_index + + replaced[brace_index] = 'end' + replaced + end + + def to_method_string(method_name) + method_string_from_lambda(code, method_name) + end end end From e2520c1292201b630494dd41f81d3ad998be9f47 Mon Sep 17 00:00:00 2001 From: "jingyuan.zhao" Date: Tue, 27 Jan 2026 21:00:59 +0900 Subject: [PATCH 2/3] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c3c0a53..ec5a6dc 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ This project is built on work of [jb](https://github.com/amatsuda/jb). ## Template Syntax -SimpleJson templates are simply lambda objects that return data(Hashes or Arrays) for json. +SimpleJson templates should be written in Ruby lambda format. The template code is converted into a method and then invoked to produce data (Hashes or Arrays) for JSON. ```ruby -> { From c78fd3614adfe0b9975c2a23d7c6ce67a7c29bbc Mon Sep 17 00:00:00 2001 From: "jingyuan.zhao" Date: Wed, 28 Jan 2026 12:44:57 +0900 Subject: [PATCH 3/3] Reuse methods when template cache disabled --- lib/simple_json/simple_json_renderer.rb | 19 ++++++++++++++----- lib/simple_json/simple_json_template.rb | 4 ++-- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/lib/simple_json/simple_json_renderer.rb b/lib/simple_json/simple_json_renderer.rb index 9f1c35c..5961765 100644 --- a/lib/simple_json/simple_json_renderer.rb +++ b/lib/simple_json/simple_json_renderer.rb @@ -46,17 +46,26 @@ def load_template_from_file(template_path) end def define_template_method(template_path, file_path) - @template_num ||= 0 - @template_num += 1 - method_name = :"template_#{@template_num}" - render_methods[template_path] = method_name - SimpleJsonTemplate.new(file_path).define_to_class(self, method_name) + template = SimpleJsonTemplate.new(file_path) + code_hash = template.code.hash + + render_methods[template_path] = render_methods_cache.fetch(code_hash) do + @template_num ||= 0 + @template_num += 1 + method_name = :"template_#{@template_num}" + template.define_to_class(self, method_name) + render_methods_cache[code_hash] = method_name + end end def render_methods @render_methods ||= {} end + def render_methods_cache + @render_methods_cache ||= {} + end + def clear_renderers @render_methods = {} @templates_loaded = false diff --git a/lib/simple_json/simple_json_template.rb b/lib/simple_json/simple_json_template.rb index 4d4c3ed..84befc1 100644 --- a/lib/simple_json/simple_json_template.rb +++ b/lib/simple_json/simple_json_template.rb @@ -14,12 +14,12 @@ def define_to_class(klass, method_name) method_name end - private - def code @code ||= lambda_stringify(@source) end + private + def lambda_stringify(source) return source if source.match?(/^(?:\s*(?:#.*?)?\n)*\s*->/)