Skip to content

Commit ef9944c

Browse files
committed
Refactor Grace Scaffolding Core and Plugin
Closes gh-1213
1 parent 1c80ba8 commit ef9944c

70 files changed

Lines changed: 1360 additions & 617 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

grace-plugin-scaffolding/src/main/groovy/grails/plugin/scaffolding/ScaffoldingAutoConfiguration.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2024 the original author or authors.
2+
* Copyright 2024-2025 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
2727
import grails.config.Settings;
2828
import grails.core.GrailsApplication;
2929
import grails.util.Environment;
30+
3031
import org.grails.gsp.GroovyPagesTemplateEngine;
3132
import org.grails.web.gsp.io.GrailsConventionGroovyPageLocator;
3233
import org.grails.web.servlet.view.GroovyPageViewResolver;
@@ -36,7 +37,7 @@
3637
* {@link EnableAutoConfiguration Auto-Configure} for Scaffolding
3738
*
3839
* @author Michael Yan
39-
* @since 6.1
40+
* @since 2024.0.0
4041
*/
4142
@AutoConfiguration
4243
@AutoConfigureOrder(-10)

grace-plugin-scaffolding/src/main/groovy/grails/plugin/scaffolding/ScaffoldingGrailsPlugin.groovy

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ package grails.plugin.scaffolding
1717

1818
import groovy.transform.CompileStatic
1919

20-
import grails.plugins.*
20+
import grails.plugins.Plugin
2121
import grails.util.GrailsUtil
2222

2323
/**
@@ -28,10 +28,8 @@ import grails.util.GrailsUtil
2828
class ScaffoldingGrailsPlugin extends Plugin {
2929

3030
def version = GrailsUtil.getGrailsVersion()
31-
def grailsVersion = "2023.0.0 > *"
32-
def title = "Scaffolding Plugin"
33-
def author = "Michael Yan"
34-
def authorEmail = "rain@rainboyan.com"
31+
def grailsVersion = '2023.0.0 > *'
32+
def title = 'Scaffolding Plugin'
3533
def description = 'Plugin that generates scaffolded controllers and views for a Grace application.'
3634

3735
}

grace-plugin-scaffolding/src/main/groovy/grails/plugin/scaffolding/ScaffoldingViewResolver.groovy

Lines changed: 60 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,11 @@
1515
*/
1616
package grails.plugin.scaffolding
1717

18-
import grails.codegen.model.ModelBuilder
19-
import grails.io.IOUtils
20-
import grails.util.BuildSettings
21-
import grails.util.Environment
18+
import java.util.concurrent.ConcurrentHashMap
19+
2220
import groovy.text.GStringTemplateEngine
2321
import groovy.text.Template
2422
import groovy.transform.CompileStatic
25-
import org.grails.buffer.*
26-
import org.grails.web.servlet.mvc.GrailsWebRequest
27-
import org.grails.web.servlet.view.GroovyPageView
28-
import org.grails.web.servlet.view.GroovyPageViewResolver
2923
import org.springframework.context.ResourceLoaderAware
3024
import org.springframework.core.io.ByteArrayResource
3125
import org.springframework.core.io.FileSystemResource
@@ -34,7 +28,17 @@ import org.springframework.core.io.ResourceLoader
3428
import org.springframework.core.io.UrlResource
3529
import org.springframework.web.servlet.View
3630

37-
import java.util.concurrent.ConcurrentHashMap
31+
import grails.codegen.model.Model
32+
import grails.codegen.model.ModelBuilder
33+
import grails.core.GrailsControllerClass
34+
import grails.io.IOUtils
35+
import grails.util.BuildSettings
36+
import grails.util.Environment
37+
38+
import org.grails.buffer.FastStringWriter
39+
import org.grails.web.servlet.mvc.GrailsWebRequest
40+
import org.grails.web.servlet.view.GroovyPageView
41+
import org.grails.web.servlet.view.GroovyPageViewResolver
3842

3943
/**
4044
* @author Graeme Rocher
@@ -49,7 +53,7 @@ class ScaffoldingViewResolver extends GroovyPageViewResolver implements Resource
4953

5054
protected String buildCacheKey(String viewName) {
5155
String viewCacheKey = groovyPageLocator.resolveViewFormat(viewName)
52-
String currentControllerKeyPrefix = resolveCurrentControllerKeyPrefixes(viewName.startsWith("/"))
56+
String currentControllerKeyPrefix = resolveCurrentControllerKeyPrefixes(viewName.startsWith('/'))
5357
if (currentControllerKeyPrefix != null) {
5458
viewCacheKey = currentControllerKeyPrefix + ':' + viewCacheKey
5559
}
@@ -58,61 +62,60 @@ class ScaffoldingViewResolver extends GroovyPageViewResolver implements Resource
5862

5963
@Override
6064
protected View loadView(String viewName, Locale locale) throws Exception {
61-
def view = super.loadView(viewName, locale)
62-
if(view == null) {
65+
View view = super.loadView(viewName, locale)
66+
67+
if (view == null) {
6368
String cacheKey = buildCacheKey(viewName)
6469
view = generatedViewCache.get(cacheKey)
65-
if(view != null) {
70+
if (view != null) {
6671
return view
6772
}
68-
else {
69-
def webR = GrailsWebRequest.lookup()
70-
def controllerClass = webR.controllerClass
71-
72-
def scaffoldValue = controllerClass?.getPropertyValue("scaffold")
73-
if(scaffoldValue instanceof Class) {
74-
def shortViewName = viewName.substring(viewName.lastIndexOf('/') + 1)
75-
Resource res = null
76-
77-
if(Environment.isDevelopmentMode()) {
78-
res = new FileSystemResource(new File(BuildSettings.BASE_DIR, "src/main/templates/scaffolding/${shortViewName}.gsp"))
79-
}
80-
81-
if(!res?.exists()) {
82-
def url = IOUtils.findResourceRelativeToClass(controllerClass.clazz, "/templates/scaffolding/${shortViewName}.gsp")
83-
res = url ? new UrlResource(url) : null
84-
}
85-
86-
if(!res.exists()) {
87-
res = resourceLoader.getResource("classpath:META-INF/templates/scaffolding/${shortViewName}.gsp")
88-
}
89-
90-
if(res.exists()) {
91-
def model = model((Class)scaffoldValue)
92-
def viewGenerator = new GStringTemplateEngine()
93-
Template t = viewGenerator.createTemplate(res.URL)
94-
95-
def contents = new FastStringWriter()
96-
t.make(model.asMap()).writeTo(contents)
97-
98-
def template = templateEngine.createTemplate(new ByteArrayResource(contents.toString().getBytes(templateEngine.gspEncoding), "view:$cacheKey"))
99-
view = new GroovyPageView()
100-
view.setServletContext(getServletContext())
101-
view.setTemplate(template)
102-
view.setApplicationContext(getApplicationContext())
103-
view.setTemplateEngine(templateEngine)
104-
view.afterPropertiesSet()
105-
generatedViewCache.put(cacheKey, view)
106-
return view
107-
}
108-
else {
109-
return view
110-
}
11173

74+
GrailsWebRequest webR = GrailsWebRequest.lookup()
75+
GrailsControllerClass controllerClass = webR.controllerClass
76+
77+
def scaffoldValue = controllerClass?.getPropertyValue('scaffold')
78+
if (scaffoldValue instanceof Class) {
79+
String shortViewName = viewName.substring(viewName.lastIndexOf('/') + 1)
80+
Resource res = null
81+
82+
if (Environment.isDevelopmentMode()) {
83+
res = new FileSystemResource(new File(BuildSettings.BASE_DIR, "src/main/templates/scaffolding/${shortViewName}.gsp"))
84+
}
85+
86+
if (!res?.exists()) {
87+
URL url = IOUtils.findResourceRelativeToClass(controllerClass.clazz, "/templates/scaffolding/${shortViewName}.gsp")
88+
res = url ? new UrlResource(url) : null
11289
}
11390

91+
if (!res.exists()) {
92+
res = resourceLoader.getResource("classpath:META-INF/templates/scaffolding/${shortViewName}.gsp")
93+
}
94+
95+
if (res.exists()) {
96+
Model model = model((Class) scaffoldValue)
97+
def viewGenerator = new GStringTemplateEngine()
98+
Template t = viewGenerator.createTemplate(res.URL)
99+
100+
FastStringWriter contents = new FastStringWriter()
101+
t.make(model.asMap()).writeTo(contents)
102+
103+
Resource resource = new ByteArrayResource(contents.toString().getBytes(templateEngine.gspEncoding), "view:$cacheKey")
104+
Template template = templateEngine.createTemplate(resource)
105+
view = new GroovyPageView()
106+
view.setServletContext(getServletContext())
107+
view.setTemplate(template)
108+
view.setApplicationContext(getApplicationContext())
109+
view.setTemplateEngine(templateEngine)
110+
view.afterPropertiesSet()
111+
generatedViewCache.put(cacheKey, view)
112+
return view
113+
}
114+
115+
return view
114116
}
115117
}
116118
return view
117119
}
118-
}
120+
121+
}

grace-plugin-scaffolding/src/main/groovy/org/grails/compiler/scaffolding/ScaffoldingControllerInjector.groovy

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ package org.grails.compiler.scaffolding
1818
import groovy.transform.CompileStatic
1919
import org.codehaus.groovy.ast.ClassNode
2020
import org.codehaus.groovy.ast.GenericsType
21+
import org.codehaus.groovy.ast.PropertyNode
2122
import org.codehaus.groovy.ast.expr.ClassExpression
23+
import org.codehaus.groovy.ast.expr.Expression
2224
import org.codehaus.groovy.ast.tools.GenericsUtils
2325
import org.codehaus.groovy.classgen.GeneratorContext
2426
import org.codehaus.groovy.control.SourceUnit
@@ -43,7 +45,7 @@ import org.grails.plugins.web.rest.transform.ResourceTransform
4345
@CompileStatic
4446
class ScaffoldingControllerInjector implements GrailsArtefactClassInjector {
4547

46-
public static final String PROPERTY_SCAFFOLD = "scaffold"
48+
public static final String PROPERTY_SCAFFOLD = 'scaffold'
4749

4850
final String[] artefactTypes = [ControllerArtefactHandler.TYPE] as String[]
4951

@@ -54,20 +56,20 @@ class ScaffoldingControllerInjector implements GrailsArtefactClassInjector {
5456

5557
@Override
5658
void performInjection(SourceUnit source, ClassNode classNode) {
57-
def propertyNode = classNode.getProperty(PROPERTY_SCAFFOLD)
59+
PropertyNode propertyNode = classNode.getProperty(PROPERTY_SCAFFOLD)
5860

59-
def expression = propertyNode?.getInitialExpression()
61+
Expression expression = propertyNode?.getInitialExpression()
6062
if (expression instanceof ClassExpression) {
6163
ClassNode superClassNode = GenericsUtils.makeClassSafe(RestfulController)
62-
def currentSuperClass = classNode.getSuperClass()
64+
ClassNode currentSuperClass = classNode.getSuperClass()
6365
if (currentSuperClass.equals(GrailsASTUtils.OBJECT_CLASS_NODE)) {
64-
def domainClass = ((ClassExpression) expression).getType()
66+
ClassNode domainClass = ((ClassExpression) expression).getType()
6567
superClassNode.setGenericsTypes(new GenericsType(domainClass))
6668
classNode.setUsingGenerics(true)
6769
classNode.setSuperClass(superClassNode)
6870
new ResourceTransform().addConstructor(classNode, domainClass, false)
6971
}
70-
else if(!currentSuperClass.isDerivedFrom(superClassNode)) {
72+
else if (!currentSuperClass.isDerivedFrom(superClassNode)) {
7173
GrailsASTUtils.error(source, classNode, "Scaffolded controllers (${classNode.name})" +
7274
" cannot extend other classes: ${currentSuperClass.getName()}", true)
7375
}

grace-plugin-scaffolding/src/main/groovy/org/grails/scaffolding/ScaffoldingBeanConfiguration.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
* {@link EnableAutoConfiguration Auto-Configure} for Scaffolding
4646
*
4747
* @author Michael Yan
48-
* @since 6.0
48+
* @since 2024.0.0
4949
*/
5050
@AutoConfiguration
5151
@AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
@@ -97,7 +97,8 @@ public DomainOutputRendererRegistry domainOutputRendererRegistry() {
9797

9898
@Bean
9999
@ConditionalOnMissingBean
100-
public DomainRendererRegisterer domainRendererRegisterer(DomainInputRendererRegistry domainInputRendererRegistry, DomainOutputRendererRegistry domainOutputRendererRegistry,
100+
public DomainRendererRegisterer domainRendererRegisterer(DomainInputRendererRegistry domainInputRendererRegistry,
101+
DomainOutputRendererRegistry domainOutputRendererRegistry,
101102
ObjectProvider<LinkGenerator> grailsLinkGenerator) {
102103
return new DomainRendererRegisterer(domainInputRendererRegistry, domainOutputRendererRegistry, grailsLinkGenerator.getIfAvailable());
103104
}

grace-scaffolding-core/src/main/groovy/org/grails/scaffolding/markup/ContextMarkupRenderer.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ public interface ContextMarkupRenderer {
9191
Closure<MarkupBuilder> outputContext(DomainProperty property, Closure content);
9292

9393
/**
94-
* Defines the context for rendering a the output of an embedded domain class property
94+
* Defines the context for rendering the output of an embedded domain class property
9595
*
9696
* @param property The domain property to be rendered
9797
* @param content The content to be rendered
@@ -100,7 +100,7 @@ public interface ContextMarkupRenderer {
100100
Closure<MarkupBuilder> embeddedOutputContext(DomainProperty property, Closure content);
101101

102102
/**
103-
* Defines the context for rendering a the input of an embedded domain class property
103+
* Defines the context for rendering the input of an embedded domain class property
104104
*
105105
* @param property The domain property to be rendered
106106
* @param content The content to be rendered

grace-scaffolding-core/src/main/groovy/org/grails/scaffolding/markup/ContextMarkupRendererImpl.groovy

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,32 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
116
package org.grails.scaffolding.markup
217

18+
import groovy.transform.CompileStatic
319
import groovy.xml.MarkupBuilder
20+
import org.springframework.context.MessageSource
421

5-
import org.grails.scaffolding.model.property.DomainProperty
622
import grails.util.GrailsNameUtils
7-
import groovy.transform.CompileStatic
823
import org.grails.datastore.mapping.model.PersistentEntity
9-
import org.springframework.context.MessageSource
24+
import org.grails.scaffolding.model.property.DomainProperty
1025

1126
/**
1227
* @see {@link ContextMarkupRenderer}
1328
* @author James Kleeh
29+
* @since 2024.0.0
1430
*/
1531
class ContextMarkupRendererImpl implements ContextMarkupRenderer {
1632

@@ -31,15 +47,12 @@ class ContextMarkupRendererImpl implements ContextMarkupRenderer {
3147
if (property.labelKeys) {
3248
labelText = resolveMessage(property.labelKeys, property.defaultLabel)
3349
}
34-
if (!labelText) {
35-
labelText = property.defaultLabel
36-
}
37-
labelText
50+
labelText ?: property.defaultLabel
3851
}
3952

4053
@CompileStatic
4154
protected String resolveMessage(List<String> keysInPreferenceOrder, String defaultMessage) {
42-
def message = keysInPreferenceOrder.findResult { key ->
55+
String message = keysInPreferenceOrder.findResult { key ->
4356
this.messageSource.getMessage(key, [].toArray(), defaultMessage, Locale.default) ?: null
4457
}
4558
message ?: defaultMessage
@@ -79,7 +92,7 @@ class ContextMarkupRendererImpl implements ContextMarkupRenderer {
7992
@Override
8093
Closure<MarkupBuilder> inputContext(PersistentEntity domainClass, Closure content) {
8194
return { ->
82-
fieldset([class: "form"], content)
95+
fieldset([class: 'form'], content)
8396
}
8497
}
8598

@@ -113,8 +126,8 @@ class ContextMarkupRendererImpl implements ContextMarkupRenderer {
113126
Closure<MarkupBuilder> outputContext(DomainProperty property, Closure content) {
114127
return { ->
115128
li(class: 'fieldcontain') {
116-
span([id: "${property.pathFromRoot}-label", class: "property-label"], getLabelText(property))
117-
div([class: "property-value", "aria-labelledby": "${property.pathFromRoot}-label"], content)
129+
span([id: "${property.pathFromRoot}-label", class: 'property-label'], getLabelText(property))
130+
div([class: 'property-value', 'aria-labelledby': "${property.pathFromRoot}-label"], content)
118131
}
119132
}
120133
}

grace-scaffolding-core/src/main/groovy/org/grails/scaffolding/markup/DomainMarkupRenderer.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,18 @@
1+
/*
2+
* Copyright 2012-2025 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
116
package org.grails.scaffolding.markup;
217

318
import org.grails.datastore.mapping.model.PersistentEntity;
@@ -6,6 +21,7 @@
621
* Used to output markup that represents a given domain class.
722
*
823
* @author James Kleeh
24+
* @since 2024.0.0
925
*/
1026
public interface DomainMarkupRenderer {
1127

0 commit comments

Comments
 (0)