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.