Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Customizable #2

Open
wants to merge 9 commits into
base: master
Choose a base branch
from
2 changes: 1 addition & 1 deletion src/main/groovy/gorm/logical/delete/LogicalDelete.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ import static gorm.logical.delete.PreQueryListener.IGNORE_DELETED_FILTER

@CompileStatic
trait LogicalDelete<D> extends GormEntity<D> {
Boolean deleted = false
boolean deleted = false

static Object withDeleted(Closure closure) {
final initialThreadLocalValue = IGNORE_DELETED_FILTER.get()
Expand Down
11 changes: 11 additions & 0 deletions src/main/groovy/gorm/logical/delete/PreQueryListener.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package gorm.logical.delete

import gorm.logical.delete.basetrait.LogicalDeleteBase
import groovy.transform.CompileStatic
import groovy.util.logging.Slf4j
import org.grails.datastore.mapping.model.PersistentEntity
Expand All @@ -34,13 +35,23 @@ class PreQueryListener implements ApplicationListener<PreQueryEvent> {
Query query = event.query
PersistentEntity entity = query.entity

/** boolean primitive logical delete (false means not deleted) */
if (LogicalDelete.isAssignableFrom(entity.javaClass)) {
log.debug "This entity [${entity.javaClass}] implements logical delete"

if (!IGNORE_DELETED_FILTER.get()) {
query.eq('deleted', false)
}
}

/** Date, String, Boolean logical delete (null means not deleted) */
if (LogicalDeleteBase.isAssignableFrom(entity.javaClass)) {
log.debug "This entity [${entity.javaClass}] implements logical delete"

if (!IGNORE_DELETED_FILTER.get()) {
query.isNull('deleted')
}
}
} catch (Exception e) {
log.error(e.message)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package gorm.logical.delete.basetrait

import grails.gorm.DetachedCriteria
import groovy.transform.CompileStatic
import org.grails.datastore.gorm.GormEnhancer
import org.grails.datastore.gorm.GormStaticApi

import static gorm.logical.delete.PreQueryListener.IGNORE_DELETED_FILTER

@CompileStatic
trait LogicalDeleteBase<D> {
static deletedValue = null

static void setDeletedValue(final newDeletedValue) {
deletedValue = newDeletedValue
}

static returnDeletedValue() {
deletedValue
}

static Object withDeleted(Closure closure) {
final initialThreadLocalValue = IGNORE_DELETED_FILTER.get()
try {
IGNORE_DELETED_FILTER.set(true)
return closure.call()
} finally {
IGNORE_DELETED_FILTER.set(initialThreadLocalValue)
}
}

static D get(final Serializable id) {
if (IGNORE_DELETED_FILTER.get()) {
this.currentGormStaticApi().get(id)
} else {
new DetachedCriteria(this).build {
eq 'id', id
eq 'deleted', deletedValue
}.get()
}
}

static D read(final Serializable id) {
if (IGNORE_DELETED_FILTER.get()) {
this.currentGormStaticApi().read(id)
} else {
new DetachedCriteria(this).build {
eq 'id', id
eq 'deleted', deletedValue
}.get()
}
}

static D load(final Serializable id) {
if (IGNORE_DELETED_FILTER.get()) {
this.currentGormStaticApi().load(id)
} else {
new DetachedCriteria(this).build {
eq 'id', id
eq 'deleted', deletedValue
}.get()
}
}

static D proxy(final Serializable id) {
if (IGNORE_DELETED_FILTER.get()) {
this.currentGormStaticApi().proxy(id)
} else {
new DetachedCriteria(this).build {
eq 'id', id
eq 'deleted', deletedValue
}.get()
}
}

/** ============================================================================================
* Private Methods:
* ============================================================================================= */
private static GormStaticApi<D> currentGormStaticApi() {
(GormStaticApi<D>) GormEnhancer.findStaticApi(this)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package gorm.logical.delete.typetrait

import gorm.logical.delete.basetrait.LogicalDeleteBase
import groovy.transform.CompileStatic
import org.grails.datastore.gorm.GormEntity

@CompileStatic
trait BooleanLogicalDelete<D> implements GormEntity<D>, LogicalDeleteBase<D> {
Boolean deleted = null

void delete(Boolean newValue = Boolean.TRUE) {
this.markDirty('deleted', newValue, this.deleted)
this.deleted = newValue
save()
}

void delete(Map params) {
if (params?.hard) {
super.delete(params)
} else {
this.markDirty('deleted', params?.newValue, this.deleted)
this.deleted = (Boolean) params?.newValue ?: Boolean.TRUE
save(params)
}
}

void unDelete() {
this.markDirty('deleted', null, this.deleted)
this.deleted = null
save()
}

void unDelete(Map params) {
this.markDirty('deleted', params?.newValue, this.deleted)
this.deleted = (Boolean) params?.newValue ?: null
save(params)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package gorm.logical.delete.typetrait

import gorm.logical.delete.basetrait.LogicalDeleteBase
import groovy.transform.CompileStatic
import org.grails.datastore.gorm.GormEntity

@CompileStatic
trait DateLogicalDelete<D> implements GormEntity<D>, LogicalDeleteBase<D> {
Date deleted = null

void delete(Date date = new Date()) {
this.markDirty('deleted', date, this.deleted)
this.deleted = date
save()
}

void delete(Map params) {
if (params?.hard) {
super.delete(params)
} else {
this.markDirty('deleted', params?.newValue, this.deleted)
this.deleted = (Date) params?.newValue ?: new Date()
save(params)
}
}

void unDelete() {
this.markDirty('deleted', null, this.deleted)
this.deleted = null
save()
}

void unDelete(Map params) {
this.markDirty('deleted', params?.newValue, this.deleted)
this.deleted = (Date) params?.newValue ?: null
save(params)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package gorm.logical.delete.typetrait

import gorm.logical.delete.basetrait.LogicalDeleteBase
import groovy.transform.CompileStatic
import org.grails.datastore.gorm.GormEntity

@CompileStatic
trait StringLogicalDelete<D> implements GormEntity<D>, LogicalDeleteBase<D> {
String deleted = null

void delete(String newValue = 'deleted') {
this.markDirty('deleted', newValue, this.deleted)
this.deleted = newValue
save()
}

void delete(Map params) {
if (params?.hard) {
super.delete(params)
} else {
this.markDirty('deleted', params?.newValue, this.deleted)
this.deleted = (String) params?.newValue ?: 'deleted'
save(params)
}
}

void unDelete() {
this.markDirty('deleted', null, this.deleted)
this.deleted = null
save()
}

void unDelete(Map params) {
this.markDirty('deleted', params?.newValue, this.deleted)
this.deleted = (String) params?.newValue ?: null
save(params)
}
}
62 changes: 62 additions & 0 deletions src/test/groovy/gorm/logical/delete/BooleanCriteriaSpec.groovy
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package gorm.logical.delete

import gorm.logical.delete.test.Person4
import gorm.logical.delete.test.Person4TestData
import grails.gorm.transactions.Rollback
import grails.testing.gorm.DomainUnitTest
import spock.lang.Specification

class BooleanCriteriaSpec extends Specification implements DomainUnitTest<Person4>, Person4TestData {

/******************* test criteria ***********************************/

@Rollback
void 'test criteria - logical deleted items'() {
// where detachedCriteria Call
when:
assert Person4.count() == 3
Person4.findByUserName("Ben").delete()
Person4.findByUserName("Nirav").delete()
// tag::criteria_query[]
def criteria = Person4.createCriteria()
def results = criteria {
or {
eq("userName", "Ben")
eq("userName", "Nirav")
}
}
// end::criteria_query[]

then: "we should not get anything bc they were deleted"
!results

when:
results = criteria {
eq("userName", "Jeff")
}

then:
results
results[0].userName == 'Jeff'
}

/******************* test criteria with projection ***********************************/

@Rollback
void 'test criteria with projection - logical deleted items'() {
// projection Call
when:
assert Person4.count() == 3
Person4.findByUserName("Ben").delete()
Person4.findByUserName("Nirav").delete()
def criteria = Person4.createCriteria()
def results = criteria.get {
projections {
count()
}
}

then: "we should not get the deleted items"
results == 1
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package gorm.logical.delete

import gorm.logical.delete.test.Person4
import gorm.logical.delete.test.Person4TestData
import grails.gorm.DetachedCriteria
import grails.gorm.transactions.Rollback
import grails.testing.gorm.DomainUnitTest
import spock.lang.Specification

class BooleanDetachedCriteriaSpec extends Specification implements DomainUnitTest<Person4>, Person4TestData {

/******************* test where ***********************************/

@Rollback
void 'test detached criteria where - logical deleted items'() {
// where detachedCriteria Call
when:
assert Person4.count() == 3
Person4.findByUserName("Ben").delete()
Person4.findByUserName("Nirav").delete()
// tag::detachedCriteria_query[]
DetachedCriteria<Person4> query = Person4.where {
userName == "Ben" || userName == "Nirav"
}
def results = query.list()
// end::detachedCriteria_query[]
then: "we should not get anything bc they were deleted"
!results

when:
query = Person4.where {
userName == "Jeff"
}
results = query.find()

then:
results
results.userName == 'Jeff'
}

/******************* test findall ***********************************/

@Rollback
void 'test detached criteria findAll - logical deleted items'() {
// findAll detachedCriteria Call
when:
assert Person4.count() == 3
Person4.findByUserName("Ben").delete()
Person4.findByUserName("Nirav").delete()
def results = Person4.findAll {
userName == "Ben" || userName == "Nirav"
}

then: "we should not get anything bc they were deleted"
!results

when:
results = Person4.findAll {
userName == "Jeff"
}

then:
results
results[0].userName == 'Jeff'
}

/********************* setup *****************************/
}
Loading