Last active
December 10, 2015 01:48
-
-
Save danveloper/4362217 to your computer and use it in GitHub Desktop.
Full example for rules engine post
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* | |
* 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