Skip to content

Commit f0d87f8

Browse files
committed
Grace generate command
Provide some generators below, - controller - domain - interceptor - service - taglib Closes gh-771
1 parent 10004bb commit f0d87f8

19 files changed

+637
-0
lines changed
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
/*
2+
* Copyright 2022-2024 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+
*/
16+
package org.grails.cli.command.generate
17+
18+
import groovy.transform.CompileStatic
19+
20+
import grails.build.logging.GrailsConsole
21+
import grails.cli.generator.Generator
22+
import org.grails.build.parsing.CommandLine
23+
import org.grails.cli.profile.CommandDescription
24+
import org.grails.cli.profile.ExecutionContext
25+
import org.grails.cli.profile.ProjectCommand
26+
27+
/**
28+
* Generate command uses Groovy templates to create everything you need.
29+
*
30+
* @author Michael Yan
31+
* @since 2023.2.0
32+
*/
33+
@CompileStatic
34+
class GenerateCommand implements ProjectCommand {
35+
36+
static final String USAGE = 'grace generate GENERATOR [args] [options]'
37+
static final String EXAMPLES = '''
38+
# Generate Grace artefacts
39+
$ grace generate domain Post title:string
40+
$ grace generate controller Post index create
41+
'''
42+
43+
final String name = 'generate'
44+
final CommandDescription description = new CommandDescription(name, 'Generate all for you need', USAGE, ['g'])
45+
46+
GenerateCommand() {
47+
this.description.flag([name: 'help', aliases: '-h', type: 'boolean', description: "Print generator's options and usage", required: false])
48+
.flag([name: 'force', aliases: '-f', type: 'boolean', description: 'Overwrite files that already exist', required: false])
49+
.flag([name: 'skip', aliases: '-s', type: 'boolean', description: 'Skip files that already exist', required: false])
50+
.flag([name: 'quiet', aliases: '-q', type: 'boolean', description: 'Suppress status output', required: false])
51+
}
52+
53+
@Override
54+
boolean handle(ExecutionContext executionContext) {
55+
CommandLine commandLine = executionContext.commandLine
56+
57+
if (commandLine.hasOption(CommandLine.HELP_ARGUMENT) || commandLine.hasOption('h')) {
58+
if (commandLine.remainingArgs) {
59+
String generatorName = commandLine.remainingArgs[0]
60+
Generator generator = loadedGenerators().find { it.name == generatorName }
61+
if (generator) {
62+
return generator.help(commandLine)
63+
}
64+
else {
65+
noGenerator(generatorName, executionContext.console)
66+
printCommandHelp(executionContext.console)
67+
}
68+
}
69+
else {
70+
printCommandHelp(executionContext.console)
71+
}
72+
return true
73+
}
74+
75+
if (!commandLine.remainingArgs) {
76+
printCommandHelp(executionContext.console)
77+
return false
78+
}
79+
80+
String generatorName = commandLine.remainingArgs[0]
81+
Generator generator = loadedGenerators().find { it.name == generatorName }
82+
if (generator) {
83+
return generator.generate(commandLine)
84+
}
85+
else {
86+
noGenerator(generatorName, executionContext.console)
87+
printCommandHelp(executionContext.console)
88+
}
89+
90+
true
91+
}
92+
93+
void printCommandHelp(GrailsConsole console) {
94+
console.out.println('Usage:')
95+
console.out.println(' ' + USAGE)
96+
console.out.println()
97+
98+
if (!this.description.getFlags().isEmpty()) {
99+
console.out.println('General options:')
100+
}
101+
this.description.getFlags().each {f ->
102+
def flag = new StringBuilder()
103+
flag << ' ' << (f.aliases ? "$f.aliases, " : ' ')
104+
if (f.type == 'boolean') {
105+
flag << "[--${f.name}]".padRight(30)
106+
}
107+
else {
108+
flag << "[--${f.name}=${f.banner}]".padRight(30)
109+
}
110+
flag << '# ' + f.description
111+
console.out.println(flag)
112+
}
113+
if (!this.description.getFlags().isEmpty()) {
114+
console.out.println()
115+
}
116+
117+
console.out.println('Please choose a generator below:')
118+
console.out.println()
119+
loadedGenerators().each { Generator generator ->
120+
println ' ' + generator.name
121+
}
122+
console.out.println()
123+
console.out.println('Example:')
124+
console.out.println(EXAMPLES)
125+
}
126+
127+
void noGenerator(String generatorName, GrailsConsole console) {
128+
console.error("Generator [$generatorName] not found, please choose the right generator!")
129+
console.out.println()
130+
}
131+
132+
private static Collection<Generator> loadedGenerators() {
133+
List<Generator> allGenerators = new ArrayList<>()
134+
ServiceLoader.load(Generator).forEach { Generator generator ->
135+
allGenerators.add(generator)
136+
}
137+
allGenerators.sort(true) {
138+
it.name
139+
}
140+
}
141+
142+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* Copyright 2022-2024 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+
*/
16+
package org.grails.cli.generator
17+
18+
import groovy.transform.CompileStatic
19+
20+
import grails.cli.generator.AbstractGenerator
21+
import org.grails.build.parsing.CommandLine
22+
import org.grails.config.CodeGenConfig
23+
24+
/**
25+
* @author Michael Yan
26+
* @since 2023.2.0
27+
*/
28+
@CompileStatic
29+
class ControllerGenerator extends AbstractGenerator {
30+
31+
@Override
32+
boolean generate(CommandLine commandLine) {
33+
String[] args = commandLine.remainingArgs.toArray(new String[0])
34+
if (args.size() < 2) {
35+
return
36+
}
37+
38+
boolean overwrite = commandLine.hasOption('force') || commandLine.hasOption('f')
39+
CodeGenConfig config = loadApplicationConfig()
40+
String className = args[1].capitalize()
41+
String propertyName = className.uncapitalize()
42+
String[] actionNames = args.size() >= 3 ? args[2..-1] : ['index']
43+
String defaultPackage = config.getProperty('grails.codegen.defaultPackage')
44+
String packagePath = defaultPackage.replace('.', '/')
45+
46+
Map<String, Object> model = new HashMap<>()
47+
model['packageName'] = defaultPackage
48+
model['className'] = className
49+
model['propertyName'] = propertyName
50+
model['actions'] = actionNames
51+
52+
String controllerFile = 'app/controllers/' + packagePath + '/' + className + 'Controller.groovy'
53+
String controllerSpecFile = 'src/test/groovy/' + packagePath + '/' + className + 'ControllerSpec.groovy'
54+
createFile('Controller.groovy.tpl', controllerFile, model, overwrite)
55+
createFile('ControllerSpec.groovy.tpl', controllerSpecFile, model, overwrite)
56+
57+
actionNames.each { String actionName ->
58+
String gspViewFile = 'app/views/' + propertyName + '/' + actionName + '.gsp'
59+
model['action'] = actionName
60+
model['path'] = gspViewFile
61+
createFile('view.gsp.tpl', gspViewFile, model, overwrite)
62+
}
63+
64+
true
65+
}
66+
67+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright 2022-2024 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+
*/
16+
package org.grails.cli.generator
17+
18+
import groovy.transform.CompileStatic
19+
20+
import grails.cli.generator.AbstractGenerator
21+
import org.grails.build.parsing.CommandLine
22+
import org.grails.config.CodeGenConfig
23+
24+
/**
25+
* @author Michael Yan
26+
* @since 2023.2.0
27+
*/
28+
@CompileStatic
29+
class DomainGenerator extends AbstractGenerator {
30+
31+
private static final Map<String, String> TYPES = [
32+
'string': 'String',
33+
'String': 'String',
34+
'int': 'int',
35+
'integer': 'Integer',
36+
'Integer': 'Integer',
37+
'long': 'long',
38+
'Long': 'Long',
39+
'double': 'double',
40+
'Double': 'Double',
41+
'float': 'float',
42+
'Float': 'Float',
43+
'date': 'Date',
44+
'Date': 'Date',
45+
'boolean': 'boolean',
46+
'Boolean': 'Boolean'
47+
]
48+
49+
@Override
50+
boolean generate(CommandLine commandLine) {
51+
String[] args = commandLine.remainingArgs.toArray(new String[0])
52+
if (args.size() < 2) {
53+
return
54+
}
55+
56+
boolean overwrite = commandLine.hasOption('force') || commandLine.hasOption('f')
57+
CodeGenConfig config = loadApplicationConfig()
58+
String className = args[1].capitalize()
59+
String propertyName = className.uncapitalize()
60+
Map<String, String> classAttributes = new LinkedHashMap<>()
61+
String[] attributes = (args.size() >= 3 ? args[2..-1] : []) as String[]
62+
attributes.each { String item ->
63+
String[] attr = (item.contains(':') ? item.split(':') : [item, 'String']) as String[]
64+
classAttributes[attr[0]] = TYPES[attr[1]] ?: attr[1]
65+
}
66+
String defaultPackage = config.getProperty('grails.codegen.defaultPackage')
67+
String packagePath = defaultPackage.replace('.', '/')
68+
69+
Map<String, Object> model = new HashMap<>()
70+
model['packageName'] = defaultPackage
71+
model['className'] = className
72+
model['propertyName'] = propertyName
73+
model['attributes'] = classAttributes
74+
75+
String domainClassFile = 'app/domain/' + packagePath + '/' + className + '.groovy'
76+
String domainClassSpecFile = 'src/test/groovy/' + packagePath + '/' + className + 'Spec.groovy'
77+
createFile('DomainClass.groovy.tpl', domainClassFile, model, overwrite)
78+
createFile('DomainClassSpec.groovy.tpl', domainClassSpecFile, model, overwrite)
79+
80+
true
81+
}
82+
83+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright 2022-2024 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+
*/
16+
package org.grails.cli.generator
17+
18+
import groovy.transform.CompileStatic
19+
20+
import grails.cli.generator.AbstractGenerator
21+
import org.grails.build.parsing.CommandLine
22+
import org.grails.config.CodeGenConfig
23+
24+
/**
25+
* @author Michael Yan
26+
* @since 2023.2.0
27+
*/
28+
@CompileStatic
29+
class InterceptorGenerator extends AbstractGenerator {
30+
31+
@Override
32+
boolean generate(CommandLine commandLine) {
33+
String[] args = commandLine.remainingArgs.toArray(new String[0])
34+
if (args.size() < 2) {
35+
return
36+
}
37+
38+
boolean overwrite = commandLine.hasOption('force') || commandLine.hasOption('f')
39+
CodeGenConfig config = loadApplicationConfig()
40+
String className = args[1].capitalize()
41+
String propertyName = className.uncapitalize()
42+
String defaultPackage = config.getProperty('grails.codegen.defaultPackage')
43+
String packagePath = defaultPackage.replace('.', '/')
44+
45+
Map<String, Object> model = new HashMap<>()
46+
model['packageName'] = defaultPackage
47+
model['className'] = className
48+
model['propertyName'] = propertyName
49+
String interceptorClassFile = 'app/controllers/' + packagePath + '/' + className + 'Interceptor.groovy'
50+
String interceptorClassSpecFile = 'src/test/groovy/' + packagePath + '/' + className + 'InterceptorSpec.groovy'
51+
52+
createFile('Interceptor.groovy.tpl', interceptorClassFile, model, overwrite)
53+
createFile('InterceptorSpec.groovy.tpl', interceptorClassSpecFile, model, overwrite)
54+
55+
true
56+
}
57+
58+
}

0 commit comments

Comments
 (0)