1

Grails では、プロジェクトの Config.groovy ファイルでグローバル制約を次のように定義できます。

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

ドメイン内でこのように使用します

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

ドメイン クラスはいくつかの Grails プロジェクトで再利用されるため、プラグインに分割されます。プラグインの Config.groovy ファイルは除外されるため、そこでグローバル制約を定義しても機能しません。したがって、ドメイン クラスを含むプラグインのプラグイン記述子内のアプリケーションの構成にマージされる Constraints.groovy ファイルを作成しました。これは機能しますが、メイン プロジェクト (grails run-app) を実行すると、次の例外が発生します。

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 を使用しています。

4

2 に答える 2

1

Grails はdoWithSpringクロージャで制約を初期化するため、config ファイルを使用して行うことはできないと思います。

しかし、見るDomainClassGrailsPluginと、config オブジェクトにアクセスできます。

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)
  }
}
于 2013-10-22T11:03:59.420 に答える
1

Sérgioの答えを念頭に置いてもう少しデバッグし、おそらく一部のプロジェクトでは機能するソリューションと機能しないソリューションを思いつきました。残念ながら、作業中の 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"))
}

この 2 つのメソッドは 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ます。したがって、このメソッドの最初の呼び出しで共有制約が空だった場合、常に空のマップが返されます。そこで、このメソッドをデバッグして、このメソッドが実際にいつ呼び出されるかを調べました。常に空のマップを返しました。プラグイン記述子でに設定loadBeforeした場合でもcore、プラグイン記述子の前にメソッドが(複数回)呼び出されました。既に述べConstraintEvalUtils.clearDefaultConstraints()たように、キャッシュされた制約を消去し、制約をマージした構成からデフォルトの制約を新たにロードできるため、機能しました。

そして、それはほとんどそれです。実際には、追加する必要がある行は多くありません。それは私のテストプロジェクトで動作し、おそらく他のプロジェクトでも動作しますが、作業中の Grails プロジェクトでは動作しません。そこでも機能するようになったら、回答を更新します。

于 2013-10-25T23:18:30.833 に答える