Skip to content

Instantly share code, notes, and snippets.

@danveloper
Last active December 10, 2015 01:48
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save danveloper/4362217 to your computer and use it in GitHub Desktop.
Save danveloper/4362217 to your computer and use it in GitHub Desktop.
Full example for rules engine post
/**
*
* Structure:
* + module: model
* - class: com.danveloper.model.Student
*
**/
class Student {
enum DormHall {
Honors, Preferred, Normal
}
def firstName
def lastName
def gpa
// We only want dormHall to be assigned when it is deliberate, so throw an exception when
// it is accidentally assigned outside of our rules engine
private def dormHall
public void setDormHall() {
throw new RuntimeException("Student dorm hall is not directly assignable.")
}
}
/**
*
* Structure:
* + module: services
* - class: com.danveloper.services.StudentService
*
**/
class StudentService {
/**
* This may be a Gormish app, which means that we can garner
* some of the more flexible notation that GORM offers our model
* entities.
*
* see: https://github.com/danveloper/gormish
**/
void save(Student student) {
student.save(flush: true)
}
}
/**
*
* Structure:
* + module: application
* - class: com.danveloper.application.rules.RulesEngineSupport
*
**/
class RulesEngineSupport<E> {
RulesEngine engine
E obj
// Deduce the class-level closure list for a given class
def getRules() {
def rules = []
engine.class.declaredFields.each {
def field = engine."${it.name}"
if (!it.isSynthetic() && field instanceof Closure && it.name.endsWith("Rule")) {
rules << it.name
}
}
rules
}
def apply() {
rules.each { rule ->
engine."$rule"(obj)
}
}
}
/**
*
* Structure:
* + module: application
* - class: com.danveloper.application.rules.RulesEngine
*
**/
public interface RulesEngine {
}
/**
*
* Structure:
* + module: application
* - class: com.danveloper.application.rules.RulesEngineFactory
*
**/
class RulesEngineFactory<E extends RulesEngine> {
private final E engine
public RulesEngineFactory(final Class<E> c) {
this.engine = (E)c.newInstance()
this.engine.engine = this.engine
}
def getObject(final def obj) {
this.engine.obj = obj
this.engine
}
}
/**
*
* Structure:
* + module: application
* - class: com.danveloper.application.rules.student.EnrollmentRulesEngine
*
**/
class EnrollmentRulesEngine extends RulesEngineSupport<Student> implements RulesEngine {
def dormHallRule = { obj ->
if (obj.gpa >= 3.5) {
obj.@dormHall = Student.DormHall.Honors
} else if (obj.gpa >= 3.0) {
obj.@dormHall = Student.DormHall.Preferred
} else {
obj.@dormHall = Student.DormHall.Normal
}
}
}
/**
*
* Structure:
* + module: application
* - class: com.danveloper.application.processors.EnrollmentProcessor
*
**/
class EnrollmentProcessor {
static def enrollmentRulesFactory = new RulesEngineFactory<EnrollmentRulesEngine>(EnrollmentRulesEngine)
def studentService
def annualDormHallAssignment() {
def students = Student.list(/* do some filtering to make sure they are enrolling for this year */)
def errors = []
students.each { student ->
try {
def studentEnrollmentRulesEngine = enrollmentRulesFactory.getObject(student)
studentEnrollmentRulesEngine.apply()
studentService.save(student)
} catch (Exception e) {
errors << student
}
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment