Skip to content

Commit

Permalink
Merge pull request #426 from jhron/2.3.x
Browse files Browse the repository at this point in the history
feat(core): Added support for multi-template inheritance.
  • Loading branch information
puneetbehl authored Mar 6, 2024
2 parents 4d9c008 + 305222a commit 0991141
Show file tree
Hide file tree
Showing 9 changed files with 114 additions and 21 deletions.
2 changes: 2 additions & 0 deletions docs/src/docs/asciidoc/json/history.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

The current release is {version}. Below is a version history.

For later releases check the release notes at: https://github.com/grails/grails-views/releases

*2.0.2*

* Fix bug where incorrect JSON was generated when all values are null in the nested object.
Expand Down
9 changes: 9 additions & 0 deletions json/grails-app/views/_child2MultipleParents.gson
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import grails.plugin.json.view.Player
import groovy.transform.Field

inherits(template: 'parent2')
inherits(template: 'parent4')

@Field Player player

json g.render(player)
9 changes: 9 additions & 0 deletions json/grails-app/views/_child3MultipleParents.gson
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import grails.plugin.json.view.Player
import groovy.transform.Field

inherits(template: 'parent3')
inherits(template: 'parent4')

@Field Player player

json g.render(player, [includes:'name'])
11 changes: 11 additions & 0 deletions json/grails-app/views/_child4MultipleParents.gson
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import grails.plugin.json.view.Player
import groovy.transform.Field

inherits(template: 'parent2')
inherits(template: 'parent4')

@Field Player player

json {
name player.name
}
7 changes: 7 additions & 0 deletions json/grails-app/views/_parent4.gson
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import groovy.transform.Field

@Field Object object

json {
bar "foo"
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import grails.plugin.json.builder.JsonOutput
import grails.plugin.json.builder.StreamingJsonBuilder
import grails.plugin.json.view.api.JsonView
import grails.plugin.json.view.api.internal.DefaultGrailsJsonViewHelper
import grails.plugin.json.view.api.internal.ParentInfo
import grails.util.GrailsNameUtils
import grails.views.AbstractWritableScript
import grails.views.GrailsViewTemplate
import grails.views.api.GrailsView
import groovy.transform.CompileStatic
import org.grails.buffer.FastStringWriter
Expand Down Expand Up @@ -46,13 +48,20 @@ abstract class JsonViewWritableScript extends AbstractWritableScript implements
* @return
*/
StreamingJsonBuilder json(@DelegatesTo(value = StreamingJsonBuilder.StreamingJsonDelegate, strategy = Closure.DELEGATE_FIRST) Closure callable) {
if(parentTemplate != null) {
if(parentData.size() > 0) {
if (!inline) {
out.write(JsonOutput.OPEN_BRACE)
}
def parentWritable = prepareParentWritable()
parentWritable.writeTo(out)
resetProcessedObjects()
Iterator parentInfoIt = parentData.iterator()
while ( parentInfoIt.hasNext() ) {
ParentInfo parentInfo = parentInfoIt.next()
def parentWritable = prepareParentWritable(parentInfo.parentTemplate, parentInfo.parentModel)
parentWritable.writeTo(out)
resetProcessedObjects()
if ( parentInfoIt.hasNext() ) {
out.write(JsonOutput.COMMA)
}
}
def jsonDelegate = new StreamingJsonBuilder.StreamingJsonDelegate(out, false, generator)
callable.setDelegate(jsonDelegate)
callable.call()
Expand Down Expand Up @@ -107,13 +116,20 @@ abstract class JsonViewWritableScript extends AbstractWritableScript implements
* @return The json builder
*/
StreamingJsonBuilder json(JsonOutput.JsonWritable writable) {
if(parentTemplate != null) {
if(parentData.size() > 0) {
if (!inline) {
out.write(JsonOutput.OPEN_BRACE)
}
def parentWritable = prepareParentWritable()
parentWritable.writeTo(out)
resetProcessedObjects()
Iterator parentInfoIt = parentData.iterator()
while ( parentInfoIt.hasNext() ) {
ParentInfo parentInfo = parentInfoIt.next()
def parentWritable = prepareParentWritable(parentInfo.parentTemplate, parentInfo.parentModel)
parentWritable.writeTo(out)
resetProcessedObjects()
if ( parentInfoIt.hasNext() ) {
out.write(JsonOutput.COMMA)
}
}
writable.setInline(true)
writable.setFirst(false)
writable.writeTo(out)
Expand Down Expand Up @@ -158,7 +174,7 @@ abstract class JsonViewWritableScript extends AbstractWritableScript implements
return json
}

private GrailsView prepareParentWritable() {
private GrailsView prepareParentWritable(GrailsViewTemplate parentTemplate, Map parentModel) {
parentModel.putAll(binding.variables)
for(o in binding.variables.values()) {
if (o != null) {
Expand Down
19 changes: 7 additions & 12 deletions json/src/main/groovy/grails/plugin/json/view/api/JsonView.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import grails.plugin.json.builder.StreamingJsonBuilder
import grails.plugin.json.view.api.internal.DefaultGrailsJsonViewHelper
import grails.plugin.json.view.api.internal.DefaultHalViewHelper
import grails.plugin.json.view.api.internal.DefaultJsonApiViewHelper
import grails.plugin.json.view.api.internal.ParentInfo
import grails.plugin.json.view.api.internal.TemplateRenderer
import grails.plugin.json.view.api.jsonapi.JsonApiIdRenderStrategy
import grails.views.GrailsViewTemplate
Expand All @@ -21,7 +22,6 @@ import groovy.transform.CompileStatic
*/
@CompileStatic
trait JsonView extends GrailsView {

/**
* The default generator
*/
Expand All @@ -37,15 +37,7 @@ trait JsonView extends GrailsView {
*/
StreamingJsonBuilder json

/**
* The parent template if any
*/
GrailsViewTemplate parentTemplate

/**
* The parent model, if any
*/
Map parentModel
Collection<ParentInfo> parentData = []
/**
* Overrides the default helper with new methods specific to JSON building
*/
Expand Down Expand Up @@ -91,8 +83,11 @@ trait JsonView extends GrailsView {
.resolveTemplateUri(getControllerNamespace(), getControllerName(), template.toString())
GrailsViewTemplate parentTemplate = (GrailsViewTemplate)templateEngine.resolveTemplate(templateUri, locale)
if(parentTemplate != null) {
this.parentTemplate = parentTemplate
this.parentModel = model
ParentInfo parentInfo = new ParentInfo(
parentTemplate: parentTemplate,
parentModel: model
)
parentData.add(parentInfo)
}
else {
throw new ViewException("Template not found for name $template")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package grails.plugin.json.view.api.internal

import grails.views.GrailsViewTemplate
import groovy.transform.CompileStatic

@CompileStatic
class ParentInfo {
/**
* The parent template if any
*/
GrailsViewTemplate parentTemplate

/**
* The parent model, if any
*/
Map parentModel
}
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,31 @@ class TemplateInheritanceSpec extends Specification implements JsonViewTest {
result.jsonText == '{"name":"Cantona"}'
}

void "test extending multiple templates"() {
when:

def result = render(template:'child2MultipleParents', model:[player: new Player(name: "Cantona")])
then:
result.jsonText == '{"_links":{"self":{"href":"http://localhost:8080/player","hreflang":"en","type":"application/hal+json"}},"foo":"bar","bar":"foo","name":"Cantona"}'
}

void "test extending multiple templates that uses g.render(..)"() {

when:
def player = new Player(name: "Cantona")
player.id = 1L
def result = render(template:'child3MultipleParents', model:[player: player])
then:
result.jsonText == '{"id":1,"bar":"foo","name":"Cantona"}'
}

void "test extending multiple templates and rendering a JSON block"() {
when:

def result = render(template:'child4MultipleParents', model:[player: new Player(name: "Cantona")])
then:
result.jsonText == '{"_links":{"self":{"href":"http://localhost:8080/player","hreflang":"en","type":"application/hal+json"}},"foo":"bar","bar":"foo","name":"Cantona"}'
}


}

0 comments on commit 0991141

Please sign in to comment.