Skip to content

Commit

Permalink
Add implicit partial rendering for AM::Models
Browse files Browse the repository at this point in the history
Fixes rails#265
  • Loading branch information
rwz committed Jun 15, 2015
1 parent ce410ce commit 4d5bf7d
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 40 deletions.
30 changes: 18 additions & 12 deletions Appraisals
Original file line number Diff line number Diff line change
@@ -1,34 +1,40 @@
appraise "rails-3-0" do
gem "test-unit"
gem "railties", "~> 3.0.0"
gem "actionpack", "~> 3.0.0"
gem "railties", "~> 3.0.0"
gem "actionpack", "~> 3.0.0"
gem "activemodel", "~> 3.0.0"
end

appraise "rails-3-1" do
gem "test-unit"
gem "railties", "~> 3.1.0"
gem "actionpack", "~> 3.1.0"
gem "railties", "~> 3.1.0"
gem "actionpack", "~> 3.1.0"
gem "activemodel", "~> 3.1.0"
end

appraise "rails-3-2" do
gem "test-unit"
gem "railties", "~> 3.2.0"
gem "actionpack", "~> 3.2.0"
gem "railties", "~> 3.2.0"
gem "actionpack", "~> 3.2.0"
gem "activemodel", "~> 3.2.0"
end

appraise "rails-4-0" do
gem "railties", "~> 4.0.0"
gem "actionpack", "~> 4.0.0"
gem "railties", "~> 4.0.0"
gem "actionpack", "~> 4.0.0"
gem "activemodel", "~> 4.0.0"
end

appraise "rails-4-1" do
gem "railties", "~> 4.1.0"
gem "actionpack", "~> 4.1.0"
gem "railties", "~> 4.1.0"
gem "actionpack", "~> 4.1.0"
gem "activemodel", "~> 4.1.0"
end

appraise "rails-4-2" do
gem "railties", "~> 4.2.0"
gem "actionpack", "~> 4.2.0"
gem "railties", "~> 4.2.0"
gem "actionpack", "~> 4.2.0"
gem "activemodel", "~> 4.2.0"
end

appraise "rails-edge" do
Expand Down
1 change: 1 addition & 0 deletions gemfiles/rails_3_0.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ gem "pry"
gem "test-unit"
gem "railties", "~> 3.0.0"
gem "actionpack", "~> 3.0.0"
gem "activemodel", "~> 3.0.0"

gemspec :path => "../"
1 change: 1 addition & 0 deletions gemfiles/rails_3_1.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ gem "pry"
gem "test-unit"
gem "railties", "~> 3.1.0"
gem "actionpack", "~> 3.1.0"
gem "activemodel", "~> 3.1.0"

gemspec :path => "../"
1 change: 1 addition & 0 deletions gemfiles/rails_3_2.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,6 @@ gem "pry"
gem "test-unit"
gem "railties", "~> 3.2.0"
gem "actionpack", "~> 3.2.0"
gem "activemodel", "~> 3.2.0"

gemspec :path => "../"
1 change: 1 addition & 0 deletions gemfiles/rails_4_0.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ gem "appraisal"
gem "pry"
gem "railties", "~> 4.0.0"
gem "actionpack", "~> 4.0.0"
gem "activemodel", "~> 4.0.0"

gemspec :path => "../"
1 change: 1 addition & 0 deletions gemfiles/rails_4_1.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ gem "appraisal"
gem "pry"
gem "railties", "~> 4.1.0"
gem "actionpack", "~> 4.1.0"
gem "activemodel", "~> 4.1.0"

gemspec :path => "../"
1 change: 1 addition & 0 deletions gemfiles/rails_4_2.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,6 @@ gem "appraisal"
gem "pry"
gem "railties", "~> 4.2.0"
gem "actionpack", "~> 4.2.0"
gem "activemodel", "~> 4.2.0"

gemspec :path => "../"
78 changes: 50 additions & 28 deletions lib/jbuilder/jbuilder_template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,15 @@ class << self

def initialize(context, *args)
@context = context
super(*args)
super *args
end

def partial!(name_or_options, locals = {})
case name_or_options
when ::Hash
# partial! partial: 'name', foo: 'bar'
options = name_or_options
def partial!(*args)
if args.one? && _is_active_model?(args.first)
_render_active_model_partial args.first
else
# partial! 'name', locals: {foo: 'bar'}
if locals.one? && (locals.keys.first == :locals)
options = locals.merge(partial: name_or_options)
else
options = { partial: name_or_options, locals: locals }
end
# partial! 'name', foo: 'bar'
as = locals.delete(:as)
options[:as] = as if as.present?
options[:collection] = locals[:collection] if locals.key?(:collection)
_render_explicit_partial *args
end

_render_partial_with_options options
end

# Caches the json constructed within the block passed. Has the same signature as the `cache` helper
Expand Down Expand Up @@ -81,18 +68,11 @@ def array!(collection = [], *args)
def set!(name, object = BLANK, *args)
options = args.first

return super unless args.one? && _partial_options?(options)

value = if object.nil?
[]
elsif _is_collection?(object)
_scope{ _render_partial_with_options options.merge(collection: object) }
if args.one? && _partial_options?(options)
_set_inline_partial name, object, options
else
locals = ::Hash[options[:as], object]
_scope{ _render_partial options.merge(locals: locals) }
super
end

super name, value
end

private
Expand Down Expand Up @@ -144,6 +124,48 @@ def _fragment_name_with_digest(key, options)
def _partial_options?(options)
::Hash === options && options.key?(:as) && options.key?(:partial)
end

def _is_active_model?(object)
object.class.respond_to?(:model_name) && object.respond_to?(:to_partial_path)
end

def _set_inline_partial(name, object, options)
value = if object.nil?
[]
elsif _is_collection?(object)
_scope{ _render_partial_with_options options.merge(collection: object) }
else
locals = ::Hash[options[:as], object]
_scope{ _render_partial options.merge(locals: locals) }
end

set! name, value
end

def _render_explicit_partial(name_or_options, locals = {})
case name_or_options
when ::Hash
# partial! partial: 'name', foo: 'bar'
options = name_or_options
else
# partial! 'name', locals: {foo: 'bar'}
if locals.one? && (locals.keys.first == :locals)
options = locals.merge(partial: name_or_options)
else
options = { partial: name_or_options, locals: locals }
end
# partial! 'name', foo: 'bar'
as = locals.delete(:as)
options[:as] = as if as.present?
options[:collection] = locals[:collection] if locals.key?(:collection)
end

_render_partial_with_options options
end

def _render_active_model_partial(object)
@context.render object, json: self
end
end

class JbuilderHandler
Expand Down
30 changes: 30 additions & 0 deletions test/jbuilder_template_test.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require "test_helper"
require "mocha/setup"
require "active_model"
require "action_view"
require "action_view/testing/resolvers"
require "active_support/cache"
Expand All @@ -18,6 +19,22 @@
json.extract! collection, :id, :name
JBUILDER

RACER_PARTIAL = <<-JBUILDER
json.extract! racer, :id, :name
JBUILDER

class Racer
extend ActiveModel::Naming
include ActiveModel::Conversion

def initialize(id, name)
@id, @name = id, name
end

attr_reader :id, :name
end


BlogPost = Struct.new(:id, :body, :author_name)
Collection = Struct.new(:id, :name)
blog_authors = [ "David Heinemeier Hansson", "Pavel Pravosud" ].cycle
Expand All @@ -29,6 +46,7 @@
PARTIALS = {
"_partial.json.jbuilder" => "foo ||= 'hello'; json.content foo",
"_blog_post.json.jbuilder" => BLOG_POST_PARTIAL,
"racers/_racer.json.jbuilder" => RACER_PARTIAL,
"_collection.json.jbuilder" => COLLECTION_PARTIAL
}

Expand Down Expand Up @@ -350,4 +368,16 @@ def assert_collection_rendered(result, context = nil)
assert_equal "post body 1", result["post"]["body"]
assert_equal "David", result["post"]["author"]["first_name"]
end

test "invokes templates implicitly for ActiveModel objects" do
@racer = Racer.new(123, "Chris Harris")

result = jbuild(<<-JBUILDER)
json.partial! @racer
JBUILDER

assert_equal %w[id name], result.keys
assert_equal 123, result["id"]
assert_equal "Chris Harris", result["name"]
end
end

0 comments on commit 4d5bf7d

Please sign in to comment.