我也不是 groovy 的 DSL 方面的专家,但一直在玩它,我认为你的情况是可行的。但它很大。
写作
verify generalTab { ... }
Groovy 似乎解决了
verify( generalTab({ ... }) )
所以接近你想要的方法是拦截方法丢失的调用('generalTab'对我来说似乎是一个html组件ID,如果我错了,请纠正我)。
你将需要:一个verify()
方法和一个methodMissing()
方法。
你的if
和else
...呃,我们可以把它换成when
和otherwise
吗?只是为了避免 groovy 自己的保留字 ;-)
后面的那些双字if
使整个事情变得很丑陋。如果你可以使用一个点或一个单词会更好。
when dataValidFor clientName then addInfoToReport otherwise addErrorToReport
决议为
when(dataValidFor).clientName(then).addInfoToReport(otherwise).addErrorToReport
解析起来会很奇怪。如果您可以执行以下操作会更好:
when dataValidFor('clientName') then addInfoToReport otherwise addErrorToReport
我做了以下事情:
report = [:]
// the closure passed as a parameter to the html component
Closure runningVerification
// the closure that handles adding info to report
Closure addInfoToReport = { Data data ->
report[data] = "[INFO] Field '$data.field' from component '$data.component' valid: $data.valid"
}
// the closure that handles adding errors to report
Closure addErrorToReport = { Data data ->
report[data] = "[ERROR] Field '$data.field' from component '$data.component' valid: $data.valid"
}
/*
* The when() method will receive a data object and returns
* a map to be handled by the 'then' and the 'otherwise' cases
*
* The 'then' and 'otherwise' must passes closures to this method
*/
def when(Data data) {
data.component = runningVerification.binding.htmlComponent
[ then:
{ Closure thenAction ->
if (data.valid) thenAction(data)
[ otherwise:
{ Closure otherwiseAction ->
if (!data.valid) otherwiseAction(data)
}
]
}
]
}
/*
* Handles missing method calls. We need this to keep track of the
* 'generalTab', the HTML component whose tests are being ran against
*/
def methodMissing(String method, args)
{
runningVerification = args[0]
runningVerification.delegate = this
runningVerification.binding.htmlComponent = method // awful
runningVerification()
}
/*
* Verify the status of the validation for html component. The
* argument is useless, it needs to access the report variable in
* the binding
*/
def verify(dataValidation) {
def errors = report.findAll { !it.key.valid }.size()
report.each { println it.value }
print "Result: "
if (errors == report.size()) {
println "Every test failed"
} else if (errors == 0) {
println "Success"
} else {
println "At least one test failed"
}
}
class Data { String component; String field; Boolean valid }
Data dataValidFor(String property) {
new Data(valid: new Random().nextInt() % 2, field: property)
}
Data confidentialData(String property) {
new Data(valid: new Random().nextInt() % 2, field: property)
}
verify generalTab {
when dataValidFor('clientName') then addInfoToReport otherwise addErrorToReport
when dataValidFor('clientCountry') then addInfoToReport otherwise addErrorToReport
when confidentialData('clientId') then addInfoToReport otherwise addErrorToReport
}
它有效。它打印(随机):
[INFO] Field 'clientName' from component 'generalTab' valid: true
[ERROR] Field 'clientCountry' from component 'generalTab' valid: false
[INFO] Field 'clientId' from component 'generalTab' valid: true
Result: At least one test failed
它变得非常丑陋。这更像是一个概念证明。您需要使用 BaseScripts、GroovyShell 分离这些类,将其委托给其他类等等。您还需要巧妙地对其建模,考虑报告类等。但到目前为止,我认为这是可行的。不过比较大。
我的阅读建议:
Guillaume Laforge 展示了火星机器人的脚本 DSL:
http ://www.slideshare.net/glaforge/going-to-mars-with-groovy-domainspecific-languages
Groovy 命令表达式的艺术:http:
//www.canoo.com/blog/2011/12/08/the-art-of-groovy-command-expressions-in-dsls/
这是我今天发送到 groovy 列表的一封电子邮件,一旦我设法通过 JFugue 完成了一个 DSL 供我个人使用:
http: //groovy.329449.n5.nabble.com/Method-chaining-in-DSL-Instructions- td5711254.html
它在 github 上:
https ://github.com/wpiasecki/glissando