2013-10-20 22 views
1

Grails中,你可以在項目的Config.groovy中定義全局約束文件中像這樣在一個Grails插件定義全局約束

grails.gorm.default.constraints = { 
    myShared(nullable: false, blank: false) 
} 

,並使用它們像這樣的域名

static constraints = { 
    name(shared: "myShared") 
} 

內由於我們的域類在幾個Grails項目中被重用,它們被分成插件。插件的Config.groovy文件被排除在外,因此定義全局約束將不起作用。因此,我創建了一個Constraints.groovy文件,該文件被合併到包含域類的插件的插件描述符中的應用程序配置中。這工作,但我仍然得到以下異常運行主項目(Grails的運行的應用程序):

Caused by GrailsConfigurationException: Property [test.plugin.TestDomain.name] references shared constraint [myShared:null], which doesn't exist! 

在Grails的核心部分的調試,我發現,域類已經與之前的共享限制初始化後插件描述符運行。

public DefaultGrailsDomainClass(Class<?> clazz, Map<String, Object> defaultConstraints) 

構造函數中的映射包含共享約束。如果我將全局約束放在主項目的Config.groovy文件中,它包含已定義的約束,並且一切正常。但是,如果我將它們合併到插件描述符中,則此映射爲空,並引發異常。

我的問題是,如果有可能以某種方式在Grails插件中定義全局約束?我可能錯過了什麼? 將全局約束複製到每個Grails項目中都不應該是解決方案。另外一種不使用另一個插件來定義約束的解決方案是優選的。

順便說一句,我們正在使用Grails 2.2.4。

+0

Config.groovy不包含在內,但您可以創建另一個以Config結尾的文件,例如: MyPluginConfig.groovy。你能檢查一下共享約束條件嗎?沒有必要手動將它們合併到您的應用程序中。 –

+0

感謝您的回覆。我嘗試了你的建議,但那也行不通。你確定配置通過命名他們像你所建議的?我認爲這隻適用於UrlMappings? – cawka

+0

是的,你是對的。 [Grails平臺核心插件添加一個doWithConfig結束](http://grailsrocks.github.io/grails-platform-core/ref/Plugin%20Conventions/doWithConfig.html),你可以使用它。 –

回答

1

由於Grails在doWithSpring閉包中初始化約束,我認爲你不能使用配置文件來完成它。

但是,如果你看DomainClassGrailsPlugin你可以訪問配置對象。

def doWithSpring = { 
    def config = application.config 
    def defaultConstraintsMap = getDefaultConstraints(config) 
    ... 
} 

所以我認爲你可以這樣做(未測試)

def loadBefore = ['domainClass'] 

def doWithSpring = { 
    def config = application.config 
    config.grails.gorm.default.constraints = { 
    myShared(nullable: false, blank: false) 
    } 
} 
+0

+1爲您的幫助到目前爲止。非常感謝你。 – cawka

1

我調試遠一點塞爾吉奧在心中的答案,並用一個解決方案,可能會爲一些項目的工作過來了,對於一些它不會。不幸的是,我們在工作中的Grails項目屬於後一組項目......但首先要做的事情。這是我做的。

我設置了一個完全空的Grails項目和Grails插件項目。該插件包含內聯以反映我們在工作中的Grails項目中的情況。

我在插件的conf目錄中創建了一個文件Constraints.groovy,就像問題中所描述的一樣。

grails.gorm.default.constraints = { 
    myShared(nullable: false, blank: false) 
} 

具有可用於添加此文件Config.groovy中的配置位置的插件內測試這些共享的限制。

grails.config.locations = [Constraints] 

現在來製作主要項目的這些約束條件。這是通過向插件描述符添加一些行來實現的。

def loadBefore = ['domainClass'] 

正如Sérgio建議我更改了加載順序。雖然我不確定這是否真的有必要。更多關於爲什麼我不確定後續的細節。

def doWithSpring = { 
    ConstraintEvalUtils.clearDefaultConstraints() 

    mergeConfig(application) 
} 

protected mergeConfig(application) { 
    application.config.merge(loadConfig(application)) 
} 

protected loadConfig(application) { 
    new ConfigSlurper(Environment.current.name).parse(application.classLoader.loadClass("Constraints")) 
} 

這兩種方法負責將Constraints.groovy的內容加載和合併到應用程序配置中。但是這個技巧實際上在合併之前調用了ConstraintEvalUtils.clearDefaultConstraints()

/** 
* Looks up the default configured constraints from the given configuration 
*/ 
public static Map<String, Object> getDefaultConstraints(ConfigObject config) { 
    def cid = System.identityHashCode(config) 
    if (defaultConstraintsMap == null || configId != cid) { 
     configId = cid 
     def constraints = config?.grails?.gorm?.default?.constraints 
     if (constraints instanceof Closure) { 
      defaultConstraintsMap = new ClosureToMapPopulator().populate((Closure<?>) constraints); 
     } 
     else { 
      defaultConstraintsMap = Collections.emptyMap() 
     } 
    } 
    return defaultConstraintsMap 
} 

此方法(也在ConstraintEvalUtils)被調用來加載共享限制。正如你所看到的結果緩存在defaultConstraintsMap。因此,如果第一次調用此方法時共享約束爲空,它總是返回空映射。所以我調試了這個方法來找出這個方法實際被調用的時間。它總是返回一張空白的地圖。即使當我在插件描述符中設置loadBeforecore時,該方法在插件描述符之前已經被調用(不止一次)。正如我已經提到ConstraintEvalUtils.clearDefaultConstraints()工作,因爲它清理緩存的約束,並可以從我的約束合併到配置中新加載默認約束。

這就是它。其實並不需要添加很多行。它適用於我的測試項目,也可能適用於其他項目,但不適用於我們的Grails項目。如果我在那裏工作,我會更新我的答案。