Skip to content

Commit

Permalink
Grace generate command
Browse files Browse the repository at this point in the history
Provide some generators below,

- controller
- domain
- interceptor
- service
- taglib

Closes gh-771
  • Loading branch information
rainboyan committed Dec 3, 2024
1 parent 10004bb commit f0d87f8
Show file tree
Hide file tree
Showing 19 changed files with 637 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
/*
* Copyright 2022-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.grails.cli.command.generate

import groovy.transform.CompileStatic

import grails.build.logging.GrailsConsole
import grails.cli.generator.Generator
import org.grails.build.parsing.CommandLine
import org.grails.cli.profile.CommandDescription
import org.grails.cli.profile.ExecutionContext
import org.grails.cli.profile.ProjectCommand

/**
* Generate command uses Groovy templates to create everything you need.
*
* @author Michael Yan
* @since 2023.2.0
*/
@CompileStatic
class GenerateCommand implements ProjectCommand {

static final String USAGE = 'grace generate GENERATOR [args] [options]'
static final String EXAMPLES = '''
# Generate Grace artefacts
$ grace generate domain Post title:string
$ grace generate controller Post index create
'''

final String name = 'generate'
final CommandDescription description = new CommandDescription(name, 'Generate all for you need', USAGE, ['g'])

GenerateCommand() {
this.description.flag([name: 'help', aliases: '-h', type: 'boolean', description: "Print generator's options and usage", required: false])
.flag([name: 'force', aliases: '-f', type: 'boolean', description: 'Overwrite files that already exist', required: false])
.flag([name: 'skip', aliases: '-s', type: 'boolean', description: 'Skip files that already exist', required: false])
.flag([name: 'quiet', aliases: '-q', type: 'boolean', description: 'Suppress status output', required: false])
}

@Override
boolean handle(ExecutionContext executionContext) {
CommandLine commandLine = executionContext.commandLine

if (commandLine.hasOption(CommandLine.HELP_ARGUMENT) || commandLine.hasOption('h')) {
if (commandLine.remainingArgs) {
String generatorName = commandLine.remainingArgs[0]
Generator generator = loadedGenerators().find { it.name == generatorName }
if (generator) {
return generator.help(commandLine)
}
else {
noGenerator(generatorName, executionContext.console)
printCommandHelp(executionContext.console)
}
}
else {
printCommandHelp(executionContext.console)
}
return true
}

if (!commandLine.remainingArgs) {
printCommandHelp(executionContext.console)
return false
}

String generatorName = commandLine.remainingArgs[0]
Generator generator = loadedGenerators().find { it.name == generatorName }
if (generator) {
return generator.generate(commandLine)
}
else {
noGenerator(generatorName, executionContext.console)
printCommandHelp(executionContext.console)
}

true
}

void printCommandHelp(GrailsConsole console) {
console.out.println('Usage:')
console.out.println(' ' + USAGE)
console.out.println()

if (!this.description.getFlags().isEmpty()) {
console.out.println('General options:')
}
this.description.getFlags().each {f ->
def flag = new StringBuilder()
flag << ' ' << (f.aliases ? "$f.aliases, " : ' ')
if (f.type == 'boolean') {
flag << "[--${f.name}]".padRight(30)
}
else {
flag << "[--${f.name}=${f.banner}]".padRight(30)
}
flag << '# ' + f.description
console.out.println(flag)
}
if (!this.description.getFlags().isEmpty()) {
console.out.println()
}

console.out.println('Please choose a generator below:')
console.out.println()
loadedGenerators().each { Generator generator ->
println ' ' + generator.name
}
console.out.println()
console.out.println('Example:')
console.out.println(EXAMPLES)
}

void noGenerator(String generatorName, GrailsConsole console) {
console.error("Generator [$generatorName] not found, please choose the right generator!")
console.out.println()
}

private static Collection<Generator> loadedGenerators() {
List<Generator> allGenerators = new ArrayList<>()
ServiceLoader.load(Generator).forEach { Generator generator ->
allGenerators.add(generator)
}
allGenerators.sort(true) {
it.name
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
* Copyright 2022-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.grails.cli.generator

import groovy.transform.CompileStatic

import grails.cli.generator.AbstractGenerator
import org.grails.build.parsing.CommandLine
import org.grails.config.CodeGenConfig

/**
* @author Michael Yan
* @since 2023.2.0
*/
@CompileStatic
class ControllerGenerator extends AbstractGenerator {

@Override
boolean generate(CommandLine commandLine) {
String[] args = commandLine.remainingArgs.toArray(new String[0])
if (args.size() < 2) {
return
}

boolean overwrite = commandLine.hasOption('force') || commandLine.hasOption('f')
CodeGenConfig config = loadApplicationConfig()
String className = args[1].capitalize()
String propertyName = className.uncapitalize()
String[] actionNames = args.size() >= 3 ? args[2..-1] : ['index']
String defaultPackage = config.getProperty('grails.codegen.defaultPackage')
String packagePath = defaultPackage.replace('.', '/')

Map<String, Object> model = new HashMap<>()
model['packageName'] = defaultPackage
model['className'] = className
model['propertyName'] = propertyName
model['actions'] = actionNames

String controllerFile = 'app/controllers/' + packagePath + '/' + className + 'Controller.groovy'
String controllerSpecFile = 'src/test/groovy/' + packagePath + '/' + className + 'ControllerSpec.groovy'
createFile('Controller.groovy.tpl', controllerFile, model, overwrite)
createFile('ControllerSpec.groovy.tpl', controllerSpecFile, model, overwrite)

actionNames.each { String actionName ->
String gspViewFile = 'app/views/' + propertyName + '/' + actionName + '.gsp'
model['action'] = actionName
model['path'] = gspViewFile
createFile('view.gsp.tpl', gspViewFile, model, overwrite)
}

true
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2022-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.grails.cli.generator

import groovy.transform.CompileStatic

import grails.cli.generator.AbstractGenerator
import org.grails.build.parsing.CommandLine
import org.grails.config.CodeGenConfig

/**
* @author Michael Yan
* @since 2023.2.0
*/
@CompileStatic
class DomainGenerator extends AbstractGenerator {

private static final Map<String, String> TYPES = [
'string': 'String',
'String': 'String',
'int': 'int',
'integer': 'Integer',
'Integer': 'Integer',
'long': 'long',
'Long': 'Long',
'double': 'double',
'Double': 'Double',
'float': 'float',
'Float': 'Float',
'date': 'Date',
'Date': 'Date',
'boolean': 'boolean',
'Boolean': 'Boolean'
]

@Override
boolean generate(CommandLine commandLine) {
String[] args = commandLine.remainingArgs.toArray(new String[0])
if (args.size() < 2) {
return
}

boolean overwrite = commandLine.hasOption('force') || commandLine.hasOption('f')
CodeGenConfig config = loadApplicationConfig()
String className = args[1].capitalize()
String propertyName = className.uncapitalize()
Map<String, String> classAttributes = new LinkedHashMap<>()
String[] attributes = (args.size() >= 3 ? args[2..-1] : []) as String[]
attributes.each { String item ->
String[] attr = (item.contains(':') ? item.split(':') : [item, 'String']) as String[]
classAttributes[attr[0]] = TYPES[attr[1]] ?: attr[1]
}
String defaultPackage = config.getProperty('grails.codegen.defaultPackage')
String packagePath = defaultPackage.replace('.', '/')

Map<String, Object> model = new HashMap<>()
model['packageName'] = defaultPackage
model['className'] = className
model['propertyName'] = propertyName
model['attributes'] = classAttributes

String domainClassFile = 'app/domain/' + packagePath + '/' + className + '.groovy'
String domainClassSpecFile = 'src/test/groovy/' + packagePath + '/' + className + 'Spec.groovy'
createFile('DomainClass.groovy.tpl', domainClassFile, model, overwrite)
createFile('DomainClassSpec.groovy.tpl', domainClassSpecFile, model, overwrite)

true
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2022-2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.grails.cli.generator

import groovy.transform.CompileStatic

import grails.cli.generator.AbstractGenerator
import org.grails.build.parsing.CommandLine
import org.grails.config.CodeGenConfig

/**
* @author Michael Yan
* @since 2023.2.0
*/
@CompileStatic
class InterceptorGenerator extends AbstractGenerator {

@Override
boolean generate(CommandLine commandLine) {
String[] args = commandLine.remainingArgs.toArray(new String[0])
if (args.size() < 2) {
return
}

boolean overwrite = commandLine.hasOption('force') || commandLine.hasOption('f')
CodeGenConfig config = loadApplicationConfig()
String className = args[1].capitalize()
String propertyName = className.uncapitalize()
String defaultPackage = config.getProperty('grails.codegen.defaultPackage')
String packagePath = defaultPackage.replace('.', '/')

Map<String, Object> model = new HashMap<>()
model['packageName'] = defaultPackage
model['className'] = className
model['propertyName'] = propertyName
String interceptorClassFile = 'app/controllers/' + packagePath + '/' + className + 'Interceptor.groovy'
String interceptorClassSpecFile = 'src/test/groovy/' + packagePath + '/' + className + 'InterceptorSpec.groovy'

createFile('Interceptor.groovy.tpl', interceptorClassFile, model, overwrite)
createFile('InterceptorSpec.groovy.tpl', interceptorClassSpecFile, model, overwrite)

true
}

}
Loading

0 comments on commit f0d87f8

Please sign in to comment.