Custom Grails validation - validation

I would like to check to make sure two fields are not equal and one is greater then the other. Say yearBorn and yearMarried. They cannot be equal and yearMarried must be greater then yearBorn.

You can use a 2-parameter custom validator that has access to both the value being validated and the entire instance:
static constraints = {
yearMarried validator: { year, instance ->
if (year == instance.yearBorn) {
return 'i18n.code.for.equal.value'
}
if (year <= instance.yearBorn) {
return 'i18n.code.for.born.after.married'
}
}
}

Related

LINQ Distinct does not invoke IEquatable<T>.Equals

I have a set of domain object, deriving from a base, where I've overridden Equals, IEquatable<T>.Equals and equality operators. I've successfully used Contains, but now I am trying to use Distinct differently. Here's look at a sample code:
var a = new Test { Id = 1 };
var a2 = new Test { Id = 1 };
var list = new List<Test> { a, a2 };
var distinct = list.Distinct().ToList(); // both objects, Equal implementations not called
var containsA = list.Contains(a); // true, Equal implementations called
var containsA2 = list.Contains(a); // true
var containsNewObjectWithSameId = list.Contains(new Test { Id = 1 }); // true
public class Test : IEquatable<Test>
{
public int Id { get; init; }
public bool Equals(Test other)
{
if (ReferenceEquals(null, other))
return false;
if (ReferenceEquals(this, other))
return true;
if (this.GetType() != other.GetType())
return false;
return this.Id == other.Id;
}
public override int GetHashCode() => base.GetHashCode + this.Id;
}
Contains finds matches, but Distinct is feeling very inclusive and keeps them both. From MS docs:
The first search does not specify any equality comparer, which means FindFirst uses
EqualityComparer.Default to determine equality of boxes. That in turn uses the implementation
of the IEquatable.Equals method in the Box class.
What am I missing?
Thanks #JonSkeet for your insight in the comments.
The problem in this case is the way I wrote my GetHashCode method. It has nothing to do with LINQ, as I originally thought.
Explanation
GetHashCode has to be identical for objects that compare equally. In my case - since the base implementation of object.Equals only checks for reference equality and I am comparing two separate objects - a and b, their base.GetHashCode would result in different values, which in turn would render those two objects as not equal.
Solution
In this case, simply returning the Id value is enough as is shown in MS docs:
One of the simplest ways to compute a hash code for a numeric value that has the same or a smaller range than the Int32 type is to simply return that value.
So changing the above code sample like this:
public override int GetHashCode() => this.Id;
would solve the issue. Please keep in mind that if the value of Id is not unique, this will cause ill behavior. In such cases you'll need another property to check and you will have to compose GetHashCode from ALL those properties. For further info refer to MS docs

BootstrapVue table : sort by date and by string?

I am quite new to VueJS, and currently using BootstrapVue (latest version, v2.0.0), mostly its b-table feature. I load table items dynamically (from a JSON file), and one of my field (column) is a string, the other is a formatted date (dd/MM/YYYY). I would like be able to sort those dates like other string or number fields.
The doc mention the possibility to create custom sorting function, so I wrote one (as a global function, using moment.js as suggested) :
function sortDate(a, b, key) {
aDate = moment(a[key], 'DD/MM/YYYY')
bDate = moment(b[key], 'DD/MM/YYYY')
if (aDate.isValid && bDate.isValid) {
if (aDate < bDate) {
return -1
}
else if (aDate > bDate) {
return 1
}
else {
return 0
}
}
return null
}
I then integrate it to HTML b-table using the :sort-compare tag :
<b-table id="bh_table" :items="items" :fields="fields" :sort-compare="sortDate"></b-table>
The problem is that the regulat string-sorting is broken, and I am not sure how to fix it ? Should I create a global method that should detect column type, and sort accordingly ?
It seems to be the thing to do here, but I think it is quite counter-intuitive, getting possible duplicates (I have other table that contains number and dates, only dates, etc.)
You are not checking for which key is being sorted on. Also note a and b are the entire row data.
function sortDate(a, b, key) {
if (key !== 'myDateField') {
// `key` is not the field that is a date.
// Let b-table handle the sorting for other columns
// returning null or false will tell b-table to fall back to it's
// internal sort compare routine for fields keys other than `myDateField`
return null // or false
}
aDate = moment(a[key], 'DD/MM/YYYY')
bDate = moment(b[key], 'DD/MM/YYYY')
if (aDate.isValid && bDate.isValid) {
if (aDate < bDate) {
return -1
}
else if (aDate > bDate) {
return 1
}
else {
return 0
}
}
return null
}

.Max() method giving error when used in an if else

My application is in Asp.Net coded in C# and i'm using LINQ for database transactions. My requirement is to get the Max value of the records saved in a certain table, for this i'm using Max() method.
Below is my controller code :
[HttpPost]
public ActionResult Create(Entity_Name Entity_Object)
{
if (Entity_Object.Condition == true)
{
My required code
}
else
{
var get_Max_Number = db.Entity_Name.ToList();
long Max_Number = 0;
if (get_Max_Number.Count() > 0)
{
Max_Number = Convert.ToInt64(get_Max_Number.Max());
}
My required code
}
}
My issue is when i remove the If-else condition then the same Max() method query works perfect, but when i add the If-else statement then i gets the following error.
Error:
At least one object must implement IComparable.
What i tried :
I attempted to remove the If-Else
I placed the Max() method logic above the If-else
Placing the Max() method above If-Else
[HttpPost]
public ActionResult Create(Entity_Name Entity_Object)
{
var get_Max_Number = db.Entity_Name.ToList();
long Max_Number = 0;
if (get_Max_Number.Count() > 0)
{
Max_Number = Convert.ToInt64(get_Max_Number.Max());
}
if (Entity_Object.Condition == true)
{
My required code
}
else
{
My required code
}
}
Max() needs to know what you're getting the maximum of. If you're Entity_Name class contains a number of properties (strings, ints etc...) then you need to tell it what to get the Maximum on the basis of.
Another thing, you're connecting to a DB via Linq from the looks of things, but executing your Count() & Max() functions in memory after you've retrieved the entire contents of the database table. This will be very inefficient as the table grows in size. LinqToSql & LinqToEF support pushing those functions down to the database level. I'd recommend changing your code to the following.
[HttpPost]
public ActionResult Create(Entity_Name Entity_Object)
{
if (Entity_Object.Condition == true)
{
//My required code
}
else
{
long Max_Number = 0;
if(db.Entity_Name.Count() > 0)
{
Max_Number = Convert.ToInt64(
db.Entity_Name.Max(x => x.PropertyToGetMaxOf)
);
}
//My required code
}
}

In a Grails domain object, is it possible to validate a field based on another field?

I have a Grails domain object that looks like this:
class Product {
Boolean isDiscounted = false
Integer discountPercent = 0
static constraints = {
isDiscounted(nullable: false)
discountPercent(range:0..99)
}
I'd like to add a validator to discountPercent that will only validate if isDiscounted is true, something like this:
validator: { val, thisProduct ->
if (thisProduct.isDiscounted) {
// need to run the default validator here
thisProduct.discountPercent.validate() // not actual working code
} else {
thisProduct.discountPercent = null // reset discount percent
}
Does anyone know how I can do this?
This is more or less what you need (on the discountPercent field):
validator: { val, thisProduct ->
if (thisProduct.isDiscounted)
if (val < 0) {
return 'range.toosmall' //default code for this range constraint error
}
if (99 < val) {
return 'range.toobig' //default code for this range constraint error
} else {
return 'invalid.dependency'
}
You can't both have a special validator that relies on something else and have a special validator, as you can't run a single validator on a field (that I know of), only on single properties. But if you run a validation on this property, you'll depend on yourself and go into endless recursion. Therefore I added the range check manually. In your i18n files you can set up something like full.packet.path.FullClassName.invalid.dependency=Product not discounted.
Good luck!

Grails: specify number of digits for integer in domain class

Is there some better way than validators to enforce an integer be exactly, for example, 2 digits?
In my fantasy world I would do something like this:
class FantasyDomainClass{
Integer[2] twoDigitInteger //fantasy world knows I mean base 10
}
Maybe BigInteger?
Based on the answers posed, I am considering I may not want an integer as '01' is an acceptable value.
You can set a contraint on the field that it be between 10 and 99:
class FantasyDomainClass {
Integer twoDigitInteger
static constraints = {
twoDigitInteger min:10, max:99
}
}
I would go with a custom validator and set it as
class FantasyDomainClass {
Integer twoDigitInteger
static constraints = {
twoDigitInteger validator: {
return (it.toString().size() <= 2)
}
}

Resources