From fddb7ca648aa8eccab286fa35624337df1695fa6 Mon Sep 17 00:00:00 2001 From: Michael Oberegger Date: Thu, 25 Sep 2025 13:19:38 -0400 Subject: [PATCH 1/5] Optimize array! DSL --- lib/jbuilder.rb | 39 ++++++++++++++++++------------- lib/jbuilder/jbuilder_template.rb | 14 +++++------ 2 files changed, 30 insertions(+), 23 deletions(-) diff --git a/lib/jbuilder.rb b/lib/jbuilder.rb index 1290839..f64d9f7 100644 --- a/lib/jbuilder.rb +++ b/lib/jbuilder.rb @@ -7,6 +7,7 @@ require 'jbuilder/errors' require 'json' require 'active_support/core_ext/hash/deep_merge' +require 'active_support/core_ext/object/blank' class Jbuilder @@key_formatter = nil @@ -32,14 +33,16 @@ def self.encode(...) new(...).target! end - BLANK = Blank.new + BLANK = Blank.new.freeze + EMPTY_ARRAY = [].freeze + private_constant :BLANK, :EMPTY_ARRAY def set!(key, value = BLANK, *args, &block) result = if ::Kernel.block_given? if !_blank?(value) # json.comments @post.comments { |comment| ... } # { "comments": [ { ... }, { ... } ] } - _scope{ array! value, &block } + _scope{ _array value, &block } else # json.comments { ... } # { "comments": ... } @@ -59,7 +62,7 @@ def set!(key, value = BLANK, *args, &block) elsif _is_collection?(value) # json.comments @post.comments, :content, :created_at # { "comments": [ { "content": "hello", "created_at": "..." }, { "content": "world", "created_at": "..." } ] } - _scope{ array! value, *args } + _scope{ _array value, args } else # json.author @post.creator, :name, :email_address # { "author": { "name": "David", "email_address": "david@loudthinking.com" } } @@ -206,18 +209,8 @@ def child! # json.array! [1, 2, 3] # # [1,2,3] - def array!(collection = [], *attributes, &block) - array = if collection.nil? - [] - elsif ::Kernel.block_given? - _map_collection(collection, &block) - elsif attributes.any? - _map_collection(collection) { |element| _extract element, attributes } - else - _format_keys(collection.to_a) - end - - @attributes = _merge_values(@attributes, array) + def array!(collection = EMPTY_ARRAY, *attributes, &block) + _array collection, attributes, &block end # Extracts the mentioned attributes or hash elements from the passed object and turns them into attributes of the JSON. @@ -243,7 +236,7 @@ def extract!(object, *attributes) def call(object, *attributes, &block) if ::Kernel.block_given? - array! object, &block + _array object, &block else _extract object, attributes end @@ -276,6 +269,20 @@ def target! alias_method :method_missing, :set! + def _array(collection = EMPTY_ARRAY, attributes = nil, &block) + array = if collection.nil? + EMPTY_ARRAY + elsif block + _map_collection(collection, &block) + elsif attributes.present? + _map_collection(collection) { |element| _extract element, attributes } + else + _format_keys(collection.to_a) + end + + @attributes = _merge_values(@attributes, array) + end + def _extract(object, attributes) if ::Hash === object _extract_hash_values(object, attributes) diff --git a/lib/jbuilder/jbuilder_template.rb b/lib/jbuilder/jbuilder_template.rb index 855dd67..bf06f0a 100644 --- a/lib/jbuilder/jbuilder_template.rb +++ b/lib/jbuilder/jbuilder_template.rb @@ -119,15 +119,15 @@ def target! @cached_root || super end - def array!(collection = [], *args) + def array!(collection = EMPTY_ARRAY, *args, &block) options = args.first - if args.one? && _partial_options?(options) + if _partial_options?(options) options = options.dup options[:collection] = collection _render_partial_with_options options else - super + _array collection, args, &block end end @@ -151,7 +151,7 @@ def _render_partial_with_options(options) as = options[:as] if as && options.key?(:collection) - collection = options.delete(:collection) || [] + collection = options.delete(:collection) || EMPTY_ARRAY partial = options.delete(:partial) options[:locals][:json] = self collection = EnumerableCompat.new(collection) if collection.respond_to?(:count) && !collection.respond_to?(:size) @@ -169,9 +169,9 @@ def _render_partial_with_options(options) .new(@context.lookup_context, options) { |&block| _scope(&block) } .render_collection_with_partial(collection, partial, @context, nil) - array! if results.respond_to?(:body) && results.body.nil? + _array if results.respond_to?(:body) && results.body.nil? else - array! + _array end else _render_partial options @@ -233,7 +233,7 @@ def _is_active_model?(object) def _set_inline_partial(name, object, options) value = if object.nil? - [] + EMPTY_ARRAY elsif _is_collection?(object) _scope do options[:collection] = object From b63e7b1e70219beb84e34807c593f9d6de8192ad Mon Sep 17 00:00:00 2001 From: Michael Oberegger Date: Thu, 25 Sep 2025 14:13:37 -0400 Subject: [PATCH 2/5] Optimize set! DSL --- lib/jbuilder.rb | 68 ++++++++++++++++--------------- lib/jbuilder/jbuilder_template.rb | 6 +-- 2 files changed, 39 insertions(+), 35 deletions(-) diff --git a/lib/jbuilder.rb b/lib/jbuilder.rb index f64d9f7..37d2af5 100644 --- a/lib/jbuilder.rb +++ b/lib/jbuilder.rb @@ -38,38 +38,7 @@ def self.encode(...) private_constant :BLANK, :EMPTY_ARRAY def set!(key, value = BLANK, *args, &block) - result = if ::Kernel.block_given? - if !_blank?(value) - # json.comments @post.comments { |comment| ... } - # { "comments": [ { ... }, { ... } ] } - _scope{ _array value, &block } - else - # json.comments { ... } - # { "comments": ... } - _merge_block(key){ yield self } - end - elsif args.empty? - if ::Jbuilder === value - # json.age 32 - # json.person another_jbuilder - # { "age": 32, "person": { ... } - _format_keys(value.attributes!) - else - # json.age 32 - # { "age": 32 } - _format_keys(value) - end - elsif _is_collection?(value) - # json.comments @post.comments, :content, :created_at - # { "comments": [ { "content": "hello", "created_at": "..." }, { "content": "world", "created_at": "..." } ] } - _scope{ _array value, args } - else - # json.author @post.creator, :name, :email_address - # { "author": { "name": "David", "email_address": "david@loudthinking.com" } } - _merge_block(key){ _extract value, args } - end - - _set_value key, result + _set(key, value, args, &block) end # Specifies formatting to be applied to the key. Passing in a name of a function @@ -269,6 +238,41 @@ def target! alias_method :method_missing, :set! + def _set(key, value = BLANK, attributes, &block) + result = if block + if _blank?(value) + # json.comments { ... } + # { "comments": ... } + _merge_block key, &block + else + # json.comments @post.comments { |comment| ... } + # { "comments": [ { ... }, { ... } ] } + _scope { _array value, &block } + end + elsif attributes.empty? + if ::Jbuilder === value + # json.age 32 + # json.person another_jbuilder + # { "age": 32, "person": { ... } + _format_keys(value.attributes!) + else + # json.age 32 + # { "age": 32 } + _format_keys(value) + end + elsif _is_collection?(value) + # json.comments @post.comments, :content, :created_at + # { "comments": [ { "content": "hello", "created_at": "..." }, { "content": "world", "created_at": "..." } ] } + _scope { _array value, attributes } + else + # json.author @post.creator, :name, :email_address + # { "author": { "name": "David", "email_address": "david@loudthinking.com" } } + _merge_block(key) { _extract value, attributes } + end + + _set_value key, result + end + def _array(collection = EMPTY_ARRAY, attributes = nil, &block) array = if collection.nil? EMPTY_ARRAY diff --git a/lib/jbuilder/jbuilder_template.rb b/lib/jbuilder/jbuilder_template.rb index bf06f0a..9b4a4e8 100644 --- a/lib/jbuilder/jbuilder_template.rb +++ b/lib/jbuilder/jbuilder_template.rb @@ -131,13 +131,13 @@ def array!(collection = EMPTY_ARRAY, *args, &block) end end - def set!(name, object = BLANK, *args) + def set!(name, object = BLANK, *args, &block) options = args.first - if args.one? && _partial_options?(options) + if _partial_options?(options) _set_inline_partial name, object, options.dup else - super + _set name, object, args, &block end end From 44b42f984fb4eb97bb09fa84b1cf2e395801fbf8 Mon Sep 17 00:00:00 2001 From: Michael Oberegger Date: Thu, 25 Sep 2025 14:14:21 -0400 Subject: [PATCH 3/5] Remove call to one? --- lib/jbuilder/jbuilder_template.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jbuilder/jbuilder_template.rb b/lib/jbuilder/jbuilder_template.rb index 9b4a4e8..53e4c22 100644 --- a/lib/jbuilder/jbuilder_template.rb +++ b/lib/jbuilder/jbuilder_template.rb @@ -52,7 +52,7 @@ def initialize(context, options = nil) # json.comments @post.comments, partial: "comments/comment", as: :comment, cached: true # def partial!(*args) - if args.one? && _is_active_model?(args.first) + if _is_active_model?(args.first) _render_active_model_partial args.first else options = args.extract_options!.dup From b0c88adca6037867ba118f5d8c1423e73ecebcdc Mon Sep 17 00:00:00 2001 From: Michael Oberegger Date: Thu, 25 Sep 2025 14:15:04 -0400 Subject: [PATCH 4/5] Remove call to Kernel.block_given? --- lib/jbuilder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jbuilder.rb b/lib/jbuilder.rb index 37d2af5..ceb35ab 100644 --- a/lib/jbuilder.rb +++ b/lib/jbuilder.rb @@ -204,7 +204,7 @@ def extract!(object, *attributes) end def call(object, *attributes, &block) - if ::Kernel.block_given? + if block _array object, &block else _extract object, attributes From d3740a353d7316494d2a41c0352523a6745a2e57 Mon Sep 17 00:00:00 2001 From: Michael Oberegger Date: Thu, 25 Sep 2025 16:33:31 -0400 Subject: [PATCH 5/5] Default attributes to nil --- lib/jbuilder.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/jbuilder.rb b/lib/jbuilder.rb index ceb35ab..61b7b50 100644 --- a/lib/jbuilder.rb +++ b/lib/jbuilder.rb @@ -238,7 +238,7 @@ def target! alias_method :method_missing, :set! - def _set(key, value = BLANK, attributes, &block) + def _set(key, value = BLANK, attributes = nil, &block) result = if block if _blank?(value) # json.comments { ... }