传统的Module配置一般采用XML - 这种繁杂的东西就不去说它了。
随着脚本语言在Java中的引入,我们有了更简洁更易维护的Module配置方式。
subclass一个Groovy的BuilderSupport先:
package com.g /** * Created by IntelliJ IDEA. * User: S.C. * Date: 5/3/11 * Time: 9:54 AM * To change this template use File | Settings | File Templates. */ import org.codehaus.groovy.grails.web.context.ServletContextHolder as SCH import com.g.utils.EvalUtils class Module extends BuilderSupport { private static Map modules public static final String HOME = 'Home' public static Map getModuleByClazz(clazzOrClazzName) { for(KV in modules) { if((clazzOrClazzName instanceof String ? KV.value.clazz?.name : KV.value.clazz) == clazzOrClazzName) { return KV.value } } } public static String getBelongsToFieldName(m) { def belongsTo = m?.belongsTo if(!belongsTo) return null def fieldName = belongsTo.name.split('\\.')[-1] fieldName[0].toLowerCase() + fieldName[1..-1] } protected void setParent(Object parent, Object child){ if(parent.children == null) parent.children = [] parent.children << child child.parent = parent } protected Object createNode(Object name){ createNode(name, null) } protected Object createNode(Object name, Object value){ createNode(name, null, value) } protected Object createNode(Object name, Map attributes){ createNode(name, attributes, null) } protected Object createNode(Object name, Map attributes, Object value){ def module = attributes ?: [:] module.name = name module.id = name.encodeAsMD5() module.label = module.label ?: (module.clazz ? null : module.name) module.description = module.description ?: module.qtip ?: module.label modules[name] = modules[module.id] = module return module } protected void nodeCompleted(Object parent, Object node) { if(node.clazz && !node.belongsTo) { def p = parent while(p && !p.clazz) { p = p.parent } node.belongsTo = p?.clazz } } def invokeMethod(String name, args) { try{ super.invokeMethod name, args }catch(x){ println x } } private static void build() { println "building modules ..." modules = [:] def file = new File(SCH.getServletContext().getRealPath("/WEB-INF/db/modules.groovy")) if(file.exists()) { def b = new Module() def c = EvalUtils.eval('{->'+file.text+'}') b."$HOME"(c) println "built ${modules.keySet().size()/2} modules." } } }
module.groovy配置文件:
Sponsors { Sponsor( label: {o->o.name}, description:{o->"${o.name}<br>${o.address?.country?:''}"}, searchFields:'name', qtip: {o->"<table><tr><td align='right'><b>Name: <b></td><td>${o.name}</td></tr></table>"}, sort: 'name', clazz: Sponsor, idRex:{usr-> User.isInternalAccount(usr) ? '.*' : '' }, defaultInternalPermission:'ru' ) } Studies { Study( label: {o->"${o.jobCode}${o.nickname?(' : '+o.nickname):''} (Sponsored by ${o.sponsor?.name})"}, plural:'Studies', description:{o-> def desc = ["<b>${o.jobCode}</b><br><br><table>"] desc << "<tr><td><b>Sites in total:</b> </td><td align='right'>${Site.findAll(study:o).size()}</td></tr>" GenericSelectionItem.findAll(sort:'name', group:'siteStatus').each{status-> desc << "<tr><td><b>${status.html}:</b> </td><td align='right' width='500'>${Site.findAll(study:o, status:status).size()}</td></tr>" } def subjects = Subject.findAll('site.study':o) desc <<"<tr><td> </td><td> </td></tr>" desc << "<tr><td><b>Subjects in total:</b> </td><td align='right'>${subjects.size()} (<font color='red'><b>${Subject.findAll('site.study':o, screenFailure:true).size()} Screen Failures</b></font>)</td></tr>" desc << "</table>" desc.join() }, searchFields:'jobCode,nickname', qtip: {o-> """<table> <tr><td><b>JobCode:</b> </td><td>${o.jobCode}</td></tr> <tr><td><b>Nickname: </td><td>${o.nickname?:''}</td></tr> <tr><td><b>Sponsor: </td><td>${o.sponsor?.name?:''}</td></tr> </table>""" }, sort: 'jobCode', clazz: Study, idRex: {usr-> def accessibleStudies = ACL.findAll(module:'Study', users:usr)?.oid.join('|') def ownedStudies = Study.findAll(all:true,version:0,createdBy:usr?.username)?.id.collect{it.substring(0,36)}.join('|') return accessibleStudies+'|'+ownedStudies } ) { // ... "Euro Country Specific Documents"(hide:{mid-> !Study.isEuro(mid.split(':')[1]) }) { //... } } } System { // ... } Help { // ... }
Module配置文件(groovy)中大量使用了Closures,其中一个最简单的例子是
"Euro Country Specific Documents"(hide:{mid->
!Study.isEuro(mid.split(':')[1])
})
!Study.isEuro(mid.split(':')[1])
})
这样在显示modules的时候,这个hide closure将被调用,专门为涉及Euro Countries的Study做的Module "Euro Country Specific Documents"就能智能地根据某一指定的study(临床试验项目)是否在欧洲国家做来显示/隐藏,酱紫管module显示的代码就可以做的很简洁,无他,唯调用closure而已:
// ... if(m.hide?.call(params.id)) { continue } // ...
---------- P.S. Study#isEuro ----------
public static boolean isEuro(sid) { // The countries currently using the euro are: // 1) Andorra // 2) Austria // 3) Belgium // 4) Cyprus // 5) Estonia // 6) Finland // 7) France // 8) Germany // 9) Greece // 10) Ireland // 11) Italy // 12) Kosovo // 13) Luxembourg // 14) Malta // 15) Monaco // 16) Montenegro // 17) Netherlands // 18) Portugal // 19) San Marino // 20) Slovakia // 21) Slovenia // 22) Spain // 23) Vatican City def euroCountries = Country.findAll(name:~/Andorra|Austria|Belgium|Cyprus|Estonia|Finland|France|Germany|Greece|Ireland|Italy|Kosovo|Luxembourg|Malta|Monaco|Montenegro|Netherlands|Netherlands Antilles|Portugal|San Marino|Slovakia|Slovenia|Spain|Holy See \(Vatican City\)/) !!Study.find(id:sid){ def qq = it.descend('countries'), c euroCountries.each{ def c1 = qq.constrain(it) c = c ? c.or(c1) : c1 } } }