Често срещани методи beforeInsert и beforeUpdate от Mixin за общи колони на домейн

Повечето от домейн обектите, които нашата компания използва, ще имат някои общи свойства. Те представляват потребителя, който е създал обекта, потребителя, който последно е актуализирал обекта, и програмата, която е използвал, за да го направи.

В интерес на DRYизвеждане на моите класове по домейн, искам да намеря някакъв начин да добавете една и съща логика на beforeInsert и beforeUpdate към всички класове на домейн, които имат тези колони, без да се намесвате с тези, които нямат.

Как искам да го направя е да използвам Mixin със собствени методи beforeInsert и beforeUpdate. Знам, че можете да използвате Mixins в класове на домейн.

package my.com

import my.com.DomainMixin

@Mixin(DomainMixin)
class MyClass {
    String foo
    String creator
    String updater

    static constraints = {
        creator nullable:false
        updater nullable:false
    }
}


package my.com
class DomainMixin {
    def beforeInsert() {
        this.creator = 'foo'
        this.updater = 'foo'
    }

    def beforeUpdate() {
        this.updater = 'bar'
    }
}

Единичните тестове биха показали, че методът beforeInsert всъщност не се задейства, когато се прилага по този начин.

Странична бележка: Знам също, че е възможно да добавите методите във файл BootStrap.groovy с помощта на metaClass, но любопитството ми надделя и наистина искам да видя дали mixin работи. Чувствайте се свободни да ми кажете, че това е по-добрият начин да го направя и че не трябва да се бъркам там, където човек не трябва.


person jonnybot    schedule 14.01.2015    source източник


Отговори (2)


FYI, използването на groovy.lang.Mixin е силно обезкуражено (от ръководителя на проекта Groovy, например). Ако трябва да използвате mixins, трябва да използвате grails.util.Mixin вместо това. Едно нещо, което не ми харесва във вашия подход към миксина, е имплицитното и неналожено предположение, че целта на миксина има свойства creator и updater

Лично аз вероятно бих използвал обикновено старо наследяване за това, напр.

abstract class Audit {

    String creator
    String updater

    def beforeInsert() {
        this.creator = 'foo'
        this.updater = 'foo'
    }

    def beforeUpdate() {
        this.updater = 'bar'
    }

    static constraints = {
        creator nullable: false
        updater nullable: false
    }
}

всички класове на домейн, които трябва да бъдат одитирани, просто биха разширили Audit. Алтернативен (и за предпочитане) подход би бил използването на характеристика вместо абстрактен базов клас, но ще трябва да използвате сравнително нова версия на Grails, за да направите това.

person Dónal    schedule 14.01.2015
comment
Наследяването ми се струва разумно. Просто ще променя моя mixin в src/groovy на абстрактен клас и ще използвам extends. Бих искал да използвам характеристики, но все още не сме напълно готови за най-новите Grails. Освен това отбелязах това в редакция, но за да работи това, ви е необходим метод beforeValidate(), който попълва задължителните полета създател и актуализатор, или beforeInsert и beforeUpdate никога не се извикват. - person jonnybot; 15.01.2015
comment
Точно като актуализация, наследяването създава някои проблеми с модулното тестване на домейна, които все още не разрешавам... най-вече защото примерът тук скрива сложността на действителната ми реализация. Вижте stackoverflow.com/questions/28048179/ - person jonnybot; 05.02.2015
comment
Резюме от връзката по-горе: @Delegate методът на Groovy беше отговорът. Предполагам, че Traits биха били още по-красиви, но засега съм доволен от използването на анотацията @Delegate за въвеждане на клас с метод beforeValidate. - person jonnybot; 16.03.2015

Вместо mixins можете да използвате Event Bus на Plugin на Grails Platform, за да направите приложението си по-сухо. Приставката поддържа добавяне на слушатели към GORM събития и те могат да бъдат приложени към всеки даден клас или интерфейс. Можете също да прикачите повече от един слушател и да напишете по-сбити модулни тестове.

Ако искате да споделите някаква логика между домейни, те трябва да имплементират интерфейс и след това да създадат услуга със следния метод:

// domainService.groovy

@grails.events.Listener(namespace='gorm')
def beforeInsert(DomainInterface domain){
    domain.creator = 'foo'
    domain.updater = 'bar'

    // If the method returns false, then domain.save() won't be called. 
}
person César    schedule 15.01.2015