Gopal S Akshintala
03/01/2020, 7:43 AMFormField
used in this example)?
I could come-up with something like below, using inheritance, but I feel that’s not the ideal way, as I hv to touch the Framework code (Rules<F>
) every time I extend this for a new type. Cc: @raulraja
interface FormFieldValidation<F> : ApplicativeError<F, Nel<ValidationError>> {
...// This has contain FormField.contains, FormField.maxLength, FormField.validateWithEmailRules()
}
interface FormFieldValidation2<F> : ApplicativeError<F, Nel<ValidationError>> {
...// This has contain FormField2.contains, FormField2.maxLength, FormField2.validateWithEmailRules()
}
sealed class Rules<F>(A: ApplicativeError<F, Nel<ValidationError>>) :
ApplicativeError<F, Nel<ValidationError>> by A,
FormFieldValidation2<F>,
FormFieldValidation<F> {
.....
}
raulraja
03/01/2020, 7:48 AMraulraja
03/01/2020, 7:49 AMGopal S Akshintala
03/01/2020, 7:49 AMraulraja
03/01/2020, 7:49 AMraulraja
03/01/2020, 7:50 AMraulraja
03/01/2020, 7:51 AMGopal S Akshintala
03/01/2020, 7:52 AMraulraja
03/01/2020, 7:52 AMGopal S Akshintala
03/01/2020, 7:52 AMvalidateWithEmailRules
is coupled with the F
in Rulesraulraja
03/01/2020, 7:52 AMraulraja
03/01/2020, 7:53 AMraulraja
03/01/2020, 7:54 AMraulraja
03/01/2020, 7:54 AMGopal S Akshintala
03/01/2020, 7:55 AMGopal S Akshintala
03/01/2020, 8:20 AMsealed class FFType {
data class FormField1(val label: String, val value: String) : FFType()
data class FormField2(val label: String, val value: String) : FFType()
}
Rules Engine:
sealed class Rules2<F, M: FFType>(A: ApplicativeError<F, Nel<ValidationError>>) :
FormField1Validation<F, M>,
ApplicativeError<F, Nel<ValidationError>> by A {
object ErrorAccumulationStrategy :
Rules2<ValidatedPartialOf<Nel<ValidationError>>, FormField1>(Validated.applicativeError(NonEmptyList.semigroup()))
object FailFastStrategy :
Rules2<EitherPartialOf<Nel<ValidationError>>, FormField1>(Either.applicativeError())
companion object {
infix fun <A> failFast(f: FailFastStrategy.() -> A): A = f(FailFastStrategy)
infix fun <A> accumulateErrors(f: ErrorAccumulationStrategy.() -> A): A = f(ErrorAccumulationStrategy)
}
}
Gopal S Akshintala
03/01/2020, 8:20 AMinterface FormFieldValidation<F, M: FFType> : ApplicativeError<F, Nel<ValidationError>> {
private fun FormField1.contains(needle: String): Kind<F, FormField1> =
if (value.contains(needle, false)) just(this)
else raiseError(ValidationError.DoesNotContain(needle).nel())
private fun FormField1.maxLength(maxLength: Int): Kind<F, FormField1> =
if (value.length <= maxLength) just(this)
else raiseError(ValidationError.MaxLength(maxLength).nel())
fun FormField1.validateWithEmailRules(): Kind<F, Email> =
mapN(
contains("@"),
maxLength(250)
) {
Email(value)
}.handleErrorWith { raiseError(ValidationError.NotAnEmail(it).nel()) }
private fun FormField2.contains(needle: String): Kind<F, FormField2> =
if (value.contains(needle, false)) just(this)
else raiseError(ValidationError.DoesNotContain(needle).nel())
private fun FormField2.maxLength(maxLength: Int): Kind<F, FormField2> =
if (value.length <= maxLength) just(this)
else raiseError(ValidationError.MaxLength(maxLength).nel())
fun FormField2.validateWithEmailRules(): Kind<F, Email> =
mapN(
contains("@"),
maxLength(250)
) {
Email(value)
}.handleErrorWith { raiseError(ValidationError.NotAnEmail(it).nel()) }
}
Gopal S Akshintala
03/01/2020, 8:21 AMval failFastErrors = Rules2 failFast {
listOf(
FormField1("Invalid Email Domain Label", "<http://nowhere.com|nowhere.com>"),
FormField1("Too Long Email Label", "nowheretoolong${(0..251).map { "g" }}"), //this fails fast
FormField1("Valid Email Label", "<mailto:getlost@nowhere.com|getlost@nowhere.com>")
).map { it.validateWithEmailRules() }
}
println("FailFast Errors: $failFastErrors")
val accumulatedErrors = Rules2 accumulateErrors {
listOf(
FormField2("Invalid Email Domain Label", "<http://nowhere.com|nowhere.com>"),
FormField2("Too Long Email Label", "nowheretoolong${(0..251).map { "g" }}"), //this accumulates N errors
FormField2("Valid Email Label", "<mailto:getlost@nowhere.com|getlost@nowhere.com>")
).map { it.validateWithEmailRules() }
}
Gopal S Akshintala
03/01/2020, 8:29 AMRules<F>
) is a library which operates on two modes, exposed by its DSL - failFast and Error Accumulation
• As a client I have various DataTypes (FormField1
and FormField2
). I want to make use of this library to run my rules. Client should be able to supply in a different module:
1. Validation constraints for the Type (like ``Type.maxLength`, Type.contains
)
2. Order to run these validations (like Type.validateWithEmailRules()
)
• Then Client should consume the library by calling the respective DSL (like Rules FailFast{...}
)Gopal S Akshintala
03/01/2020, 8:33 AMFormFieldValidation
above)?
• How can I extend my engine without touching it. (In this case I have to explicitly add this inheritance Rules2_<_F, M: FFType_>:_ FormFieldValidation_<_F, M_>_
to call validateWithEmailRules
in the context) ?Gopal S Akshintala
03/01/2020, 8:34 AMraulraja
03/01/2020, 8:44 AMGopal S Akshintala
03/01/2020, 8:46 AMvalue
Gopal S Akshintala
03/01/2020, 8:47 AMGopal S Akshintala
03/01/2020, 8:48 AMinterface FormFieldValidation<F, M: FFType> : ApplicativeError<F, Nel<ValidationError>> {
private fun M.contains(needle: String): Kind<F, M> =
// vvv Compiler error at value
if (value.contains(needle, false)) just(this)
else raiseError(ValidationError.DoesNotContain(needle).nel())
raulraja
03/01/2020, 12:04 PMraulraja
03/01/2020, 12:05 PMGopal S Akshintala
03/01/2020, 12:06 PMGopal S Akshintala
03/01/2020, 12:07 PMraulraja
03/01/2020, 3:15 PMraulraja
03/01/2020, 3:18 PMGopal S Akshintala
03/01/2020, 3:22 PMNewFormField1
, and I will supply this interface
nterface NewFormFieldValidationConstraints<F, M: FFType> : ApplicativeError<F, Nel<ValidationError>> {
private fun NewFormField1.someDifferntConstraint1(needle: String): Kind<F, NewFormField1> =
if (value.contains(needle, false)) just(this)
else raiseError(ValidationError.DoesNotContain(needle).nel())
private fun NewFormField1.someDifferntConstraint2(maxLength: Int): Kind<F, NewFormField1> =
if (value.length <= maxLength) just(this)
else raiseError(ValidationError.MaxLength(maxLength).nel())
fun NewFormField1.validateNewFormField(): Kind<F, Email> =
mapN(
someDifferntConstraint1("@"),
someDifferntConstraint2(250)
) {
// something, nothing related to email
}.handleErrorWith { raiseError(ValidationError.NotValid(it).nel()) }
}
Gopal S Akshintala
03/01/2020, 3:23 PMGopal S Akshintala
03/01/2020, 3:25 PMNewFormFieldValidationConstraints
constraints to the Rule<F>
class, inorder to extend my library capability to validate this NewFormField1
data type`Gopal S Akshintala
03/01/2020, 3:32 PMval failFastErrors = Rules2 failFast {
listOf(
NewFormField1("Invalid Email Domain Label", "<http://nowhere.com|nowhere.com>"),
NewFormField1("Too Long Email Label", "nowheretoolong${(0..251).map { "g" }}"), //this fails fast
NewFormField1("Valid Email Label", "<mailto:getlost@nowhere.com|getlost@nowhere.com>")
).map { it.validateNewFormField1() }
}
Gopal S Akshintala
03/01/2020, 3:34 PMNewFormField1
which has a value
field to validateGopal S Akshintala
03/01/2020, 3:37 PMGopal S Akshintala
03/01/2020, 3:52 PMGopal S Akshintala
03/01/2020, 3:57 PMRandomeTypeValidation
using inheritance (Line: 41). But it doesn’t seem to be idea, as a consumer I won’t have access to library code, I should be able to supply this in some other way
• How to extend my design so I can fit in RandomeType2
which has different props and different validation constraints/rules?raulraja
03/01/2020, 4:43 PMraulraja
03/01/2020, 4:43 PMraulraja
03/01/2020, 4:44 PMGopal S Akshintala
03/02/2020, 12:37 AMGopal S Akshintala
03/02/2020, 4:28 AMraulraja
03/02/2020, 9:55 PMGopal S Akshintala
03/03/2020, 2:17 AMGopal S Akshintala
03/03/2020, 2:19 AMvalidation-fx
has the code which is used in the other two modules, I used Mono and IO as examplesGopal S Akshintala
03/03/2020, 2:27 AMRules FailFast {…}
here (only for data validations on one of the field types Email
).
• But short-term goal is to extend this for other types, without touching sealed class Rules_<_F_>_
Gopal S Akshintala
03/03/2020, 2:28 AMEmail.contains
instead of having them in the library
• Then next goal is to make Rules Strategy{…}
as the entry point for all validations, so client call looks like Rules FailFast/Accumulate{…all validation constraints…}
Gopal S Akshintala
03/04/2020, 9:41 AMGopal S Akshintala
03/04/2020, 9:42 AMraulraja
03/04/2020, 12:28 PMGopal S Akshintala
03/04/2020, 12:29 PMraulraja
03/05/2020, 5:46 PMraulraja
03/05/2020, 5:47 PMraulraja
03/05/2020, 5:47 PMraulraja
03/05/2020, 5:52 PMraulraja
03/05/2020, 5:52 PMraulraja
03/05/2020, 5:52 PMGopal S Akshintala
03/10/2020, 11:50 AMraulraja
03/10/2020, 4:47 PMparMapN
instead of mapN
raulraja
03/10/2020, 4:48 PMupdate
and insert
I’d use Kind<F, Unit>
if there is no valuable returnGopal S Akshintala
03/11/2020, 6:04 AMAny?
instead of Unit
as I am struck with Mono<Void>
as below
Screen Shot 2020-03-11 at 11.28.49 AM.pngGopal S Akshintala
03/11/2020, 6:07 AMparMapN
I think can only be used in accumulate error strategy (should not be used for fail fast strategy), right?raulraja
03/11/2020, 8:58 AMGopal S Akshintala
03/11/2020, 8:59 AMGopal S Akshintala
03/11/2020, 8:59 AMraulraja
03/11/2020, 8:59 AMraulraja
03/11/2020, 9:00 AMGopal S Akshintala
03/11/2020, 9:00 AMraulraja
03/11/2020, 9:01 AMraulraja
03/11/2020, 9:06 AMunit
which maps any A
in this case Void?
to Kind<F, Unit>
Gopal S Akshintala
03/11/2020, 9:10 AMGopal S Akshintala
03/14/2020, 6:55 AMforIO { ref<UserRepository>().update(this) }.map {}
- empty map
did the job, but kind of feels non-idiomaticraulraja
03/14/2020, 10:35 AMGopal S Akshintala
03/14/2020, 10:37 AMvoid()
raulraja
03/14/2020, 11:47 AMGopal S Akshintala
03/14/2020, 11:48 AM