From a72f1bb27633fbf09badf8c0fdfaa56246ddf9f7 Mon Sep 17 00:00:00 2001 From: Florian Hurlbrink Date: Fri, 7 Jan 2022 14:33:05 +0100 Subject: [PATCH] Implement primitive! method Jbuilder returns objects, arrays and null. But according to RFC 8259, Section 3, also numbers, strings and the literals false and true are valid JSON. The method primitive! enables this. --- README.md | 14 +++++++- lib/jbuilder.rb | 15 +++++++++ lib/jbuilder/errors.rb | 7 ++++ test/jbuilder_test.rb | 73 ++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 108 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index be2b3c6e..fa48b5ef 100644 --- a/README.md +++ b/README.md @@ -239,8 +239,20 @@ json.partial! 'sub_template', locals: { user: user } json.partial! 'sub_template', user: user ``` +You can explicitly make Jbuilder object return a primitive value: -You can explicitly make Jbuilder object return null if you want: +``` ruby +json.extract! @post, :id, :title, :content, :published_at +json.author do + if @post.pseudonymous? + json.primitive! @post.author_pseudonym + else + json.first_name @post.author_first_name + json.last_name @post.author_last_name + end +``` + +Returning null is also possible: ``` ruby json.extract! @post, :id, :title, :content, :published_at diff --git a/lib/jbuilder.rb b/lib/jbuilder.rb index 3cf41d8a..c693c7dc 100644 --- a/lib/jbuilder.rb +++ b/lib/jbuilder.rb @@ -257,6 +257,17 @@ def call(object, *attributes, &block) end end + # Returns a primitive JSON + # Example: + # + # json.primitive! "hello" + # + # "hello" + def primitive!(value) + raise PrimitiveError.build(value) unless _is_primitive?(value) + @attributes = value + end + # Returns the nil JSON. def nil! @attributes = nil @@ -354,6 +365,10 @@ def _is_collection?(object) _object_respond_to?(object, :map, :count) && NON_ENUMERABLES.none?{ |klass| klass === object } end + def _is_primitive?(value) + (value.is_a? ::String) || (value.is_a? ::Numeric) || value == true || value == false || value.nil? + end + def _blank?(value=@attributes) BLANK == value end diff --git a/lib/jbuilder/errors.rb b/lib/jbuilder/errors.rb index 386e6da8..0fc1d9fb 100644 --- a/lib/jbuilder/errors.rb +++ b/lib/jbuilder/errors.rb @@ -1,6 +1,13 @@ require 'jbuilder/jbuilder' class Jbuilder + class PrimitiveError < ::StandardError + def self.build(value) + message = "Can't use #{value.inspect} as primitive" + new(message) + end + end + class NullError < ::NoMethodError def self.build(key) message = "Failed to add #{key.to_s.inspect} property to null object" diff --git a/test/jbuilder_test.rb b/test/jbuilder_test.rb index 76569bb6..e08a0e95 100644 --- a/test/jbuilder_test.rb +++ b/test/jbuilder_test.rb @@ -831,6 +831,57 @@ class JbuilderTest < ActiveSupport::TestCase Jbuilder.send(:class_variable_set, '@@ignore_nil', false) end + test 'primitive! with String value' do + result = jbuild do |json| + json.primitive! 'hello' + end + + assert_equal 'hello', result + end + + test 'primitive! with Numeric value' do + result = jbuild do |json| + json.primitive! 13.37 + end + + assert_equal 13.37, result + end + + test 'primitive! with true value' do + result = jbuild do |json| + json.primitive! true + end + + assert_equal true, result + end + + test 'primitive! with false value' do + result = jbuild do |json| + json.primitive! false + end + + assert_equal false, result + end + + test 'primitive! with nil value' do + result = jbuild do |json| + json.primitive! nil + end + + assert_equal nil, result + end + + test 'primitive! in an empty block' do + result = jbuild do |json| + json.author do + json.primitive! 'David Heinemeier Hansson' + end + end + + assert result.key?('author') + assert_equal 'David Heinemeier Hansson', result['author'] + end + test 'nil!' do result = jbuild do |json| json.key 'value' @@ -871,6 +922,14 @@ class JbuilderTest < ActiveSupport::TestCase assert !attributes.present? end + test 'throws PrimitiveError when trying to use hash as primitive' do + assert_raise Jbuilder::PrimitiveError do + jbuild do |json| + json.primitive!({}) + end + end + end + test 'throws ArrayError when trying to add a key to an array' do assert_raise Jbuilder::ArrayError do jbuild do |json| @@ -930,6 +989,20 @@ class JbuilderTest < ActiveSupport::TestCase end end + test "throws MergeError when trying to replace non-empty block with primitive" do + assert_raise Jbuilder::MergeError do + jbuild do |json| + json.author do + json.name 'David' + end + + json.author do + json.primitive! 'David Heinemeier Hansson' + end + end + end + end + if RUBY_VERSION >= "2.2.10" test "respects JSON encoding customizations" do # Active Support overrides Time#as_json for custom formatting.