-
Notifications
You must be signed in to change notification settings - Fork 4
/
FsmGrailsPlugin.groovy
115 lines (102 loc) · 4.77 KB
/
FsmGrailsPlugin.groovy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import grails.plugin.fsm.FsmUtils
import grails.plugin.fsm.FsmSupportException
import org.springframework.beans.BeanUtils;
import org.codehaus.groovy.grails.commons.GrailsClassUtils
class FsmGrailsPlugin {
// the plugin version
def version = "0.6.8.MP"
// the version or versions of Grails the plugin is designed for
def grailsVersion = "1.1.1 > *"
// the other plugins this plugin depends on
def dependsOn = [:]
def loadAfter = ['controllers', 'hibernate', 'domainclass']
// resources that are excluded from plugin packaging
def pluginExcludes = [
"grails-app/views/error.gsp",
"grails-app/domain/FsmSupportDummy.groovy",
"grails-app/domain/FsmMultipleActions.groovy"
]
def watchedResources = "file:./grails-app/domain/**/*.groovy"
def author = "Jorge Uriarte"
def authorEmail = "[email protected]"
def title = "Finite State Machine behaviour for domain classes"
def description = '''\\
This plugin allow definition of simple workflows attached to domain classes, including
states, events, transitions and conditions.
Current workflow's state will be held in domain class' property that must be defined.
Multiple workflows can be defined on every domain class.
'''
// URL to the plugin's documentation
def documentation = "http://grails.org/plugin/fsm"
def doWithSpring = {
// TODO Implement runtime spring config (optional)Classes
}
def doWithApplicationContext = { applicationContext ->
// TODO Implement post initialization spring config (optional)
}
def doWithWebDescriptor = { xml ->
// TODO Implement additions to web.xml (optional)
}
def doWithDynamicMethods = { ctx ->
// Will add the fire closure where needed
application.domainClasses.each {domainClass ->
MetaClassRegistry registry = GroovySystem.metaClassRegistry
def fsm = GrailsClassUtils.getStaticPropertyValue(domainClass.clazz, FsmUtils.FSMDEF )
if (fsm) {
// Will create the proper FsmSupport instance!
fsm.each {String p, definition ->
definition.each { start, defclosure ->
def mp = domainClass.metaClass.getMetaProperty(p)
if (!mp)
throw new FsmSupportException("Error in FSM definition: '${domainClass.clazz}' does not have '${p}' property to hold defined workflow status!")
def tmp = domainClass.clazz.newInstance()
if (tmp[p] != null)
log.warn("Default value of '${domainClass.clazz}.${p}' will be overriden by FSM definition for that property. ")
// Modify the metaclass so new instances will have new behaviour!!
domainClass.metaClass.setProperty("_fsm${p}", null) // internal, will hold FsmSupport instance
domainClass.metaClass.fire = FsmUtils.fireClosure
domainClass.metaClass."fire_${p}" = FsmUtils.fireClosure.curry(p)
domainClass.metaClass.fireable = FsmUtils.fireableClosure
domainClass.metaClass."fireable_${p}" = FsmUtils.fireableClosure.curry(p)
}
}
// This code is a COPY of DomainClassGrailsPlugin.enhanceDomainClasses
// because I cannot seem to be able to decorate it.
// We just added the "${p}" initializing!
domainClass.metaClass.constructor = {->
def bean
if(ctx.containsBean(domainClass.fullName)) {
bean = ctx.getBean(domainClass.fullName)
}
else {
bean = BeanUtils.instantiateClass(domainClass.clazz)
}
fsm.each { pp, defdef ->
defdef.each { startstart, clos ->
// def setter = GrailsClassUtils.getSetterName(pp)
// bean."${setter}"(startstart)
bean."${pp}" = startstart
}
}
bean
}
domainClass.metaClass.static.create = {->
def bean = ctx.getBean(domainClass.getFullName())
fsm.each { pp, defdef ->
defdef.each { startstart, clos ->
bean."${pp}" = startstart
}
}
bean
}
}
}
}
def onChange = { event ->
event.manager?.getGrailsPlugin("fsm")?.doWithDynamicMethods(event.ctx)
}
def onConfigChange = { event ->
// TODO Implement code that is executed when the project configuration changes.
// The event is the same as for 'onChange'.
}
}