Why is my Groovy object initializer failing to set the property? - grails

I have a Groovy class like so:
class Person {
String firstName
String lastName
Status status = StatusEnum.ACTIVE
}
And I'm creating an instance of it with an object initializer:
def person = new Person(
firstName: "Bob", lastName: "Yelo", status: StatusEnum.INACTIVE)
However, this doesn't modify the person's status and it remains as ACTIVE. I have to explicitly declare it:
person.status = StatusEnum.INACTIVE
Which properly sets the status. Does anyone know why I have to explicitly set it?

I'm guessing it's having something to do with the type of the field being Status rather than StatusEnum?
Declaring it like this worked as you're suggesting it should groovy console:
enum StatusEnum {
ACTIVE, INACTIVE
}
class Person {
String firstName
String lastName
StatusEnum status = StatusEnum.ACTIVE
}
def person = new Person(firstName: "Bob", lastName: "Yelo", status: StatusEnum.INACTIVE)
assert StatusEnum.INACTIVE == person.status

Related

`toString` method adding null to object in Dart

I am working on a Flutter web app, and am using a custom class to keep track of form data. I am instantiating a new instance of the following class:
class Contact {
String name;
String relationship;
String phoneNo;
#override
String toString() {
print("""{
Name: $name,
Relation: $relationship,
Phone: $phoneNo
}""");
}
}
In my controller, once I instantiate, I am printing out the value immediately:
// Method in controller, triggered by onTap
Contact contact = Contact();
print(contact);
The output is:
{
Name: null,
Relation: null,
Phone: null
}
null
Which is causing issues later down the line, as instances of this class are being used as values of a HashMap. I have narrowed down the issue to being caused by the toString method, and when I remove it, Instance of 'Contact' is then printed out, as desired. What is the best way to handle this?
// Method in controller, triggered by onTap
Contact contact = Contact();
print(contact);
You created a new instance of Contact with no arguments that's why all the value of Contact will be null.
To assign a value there are two solutions:
1. Inside from class itself:
class Contact {
String name = 'John doe';
String relationship = 'Brother';
String phoneNo = '1234567890';
#override
String toString() {
print("""{
Name: $name,
Relation: $relationship,
Phone: $phoneNo
}""");
}
}
2.From outside of Class
for that, you have to initiate Constructor in your class
class Contact {
String name;
String relationship;
String phoneNo;
Contact(this.name, this.relationship, this.phoneNo);
//You can also choose between named parameters and positional parameters
// For named parameters Contact({this.name, and so on....})
#override
String toString() => """{
Name: $name,
Relation: $relationship,
Phone: $phoneNo
}""";
}
in this scenario you have to pass values where you create instance of class as shown here:
Contact contact = Contact('John Doe', 'brother', '123456789');
print(contact);

Constructor Optional Params

Is there a way to set a constructor optional param?
I mean something like:
User.fromData(this._name,
this._email,
this._token,
this._refreshToken,
this._createdAt,
this._expiresAt,
this._isValid,
{this.id});
It indicates that
Named option parameters can't start with an underscore.
But I need this field as private, so, I'm lost now.
This is a more general answer for future viewers.
Positional optional parameters
Wrap the optional parameter with [ ] square brackets.
class User {
String name;
int age;
String home;
User(this.name, this.age, [this.home = 'Earth']);
}
User user1 = User('Bob', 34);
User user2 = User('Bob', 34, 'Mars');
Optional parameters need to be nullable if you don't provide a default value:
class User {
String name;
int age;
String? home; // <-- Nullable
User(this.name, this.age, [this.home]);
}
Named optional parameters
Wrap the optional parameter with { } curly braces.
class User {
String name;
int age;
String home;
User(this.name, this.age, {this.home = 'Earth'});
}
User user1 = User('Bob', 34);
User user2 = User('Bob', 34, home: 'Mars');
The default for home is "Earth", but like before, if you don't provide a default then you need to change String home to String? home.
Private fields
If you need private fields then you can use [] square brackets:
class User {
int? _id;
User([this._id]);
}
User user = User(3);
or do as the accepted answer says and use an initializer list:
class User {
int? _id;
User({int? id})
: _id = id;
}
User user = User(id: 3);
Named required parameters
Named parameters are optional by default, but if you want to make them required, then you can use the required keyword:
class User {
final String name;
final int age;
final String home;
User({
required this.name,
required this.age,
this.home = 'Earth',
});
}
User user1 = User(name: 'Bob', age: 34);
User user2 = User(name: 'Bob', age: 34, home: 'Mars');
You need to use a simple parameter and initialize your private field in initializer list.
class User {
final String _id;
final String _name;
User.fromData(this._name, {required String id})
: _id = id;
}
In addition to great Suragch's answer I wanted to mention required word. You can use it for multiple constructor or function named parameters to specify required ones.
class User {
int _id;
String _firstName;
String _lastName;
User({required int id, String firstName = "", String lastName})
: _id = id, // required parameter
_firstName = firstName, // optional parameter with default value ""
_lastName = lastName; // optional parameter without default value
}
User user1 = User(id: 1);
User user2 = User(id: 2, firstName: "John");
User user3 = User(id: 3, lastName: "Snow");
Related Dart docs here.
For Dart version <= 2.10 #required is an annotation and used with the # prefix.

Setting groovy getter/setter methods via constructor in grails domain class

I have a grails unit test that has code similar to below and is appears that setting fields via getter/setter methods doesn't work with constructors (even though it actually works with non-domain classes).
I understand that the following works with properties:
class Person {
def firstName
def lastName
def getFullName() {
return "$firstName $lastName"
}
def setFullName(name) {
firstName = name.split(" ")[0]
lastName = name.split(" ")[1]
}
}
def = new Person(fisrtName: "Joe", lastName: "Bloggs")
But when I do the following the first and last name fields don't get set:
def = new Person(fullName: "Joe Bloggs")
Is there a way to set fields via methods in a groovy contstructor?
What version of groovy are you using? This works fine for me with groovy 1.8.6 and I think it's worked for that way for a very long time:
class Person {
def firstName
def lastName
def getFullName() {
return "$firstName $lastName"
}
def setFullName(name) {
firstName = name.split(" ")[0]
lastName = name.split(" ")[1]
}
}
def p1 = new Person(firstName: "Joe", lastName: "Bloggs")
def p2 = new Person(fullName: "Joe Bloggs")
assert p1.firstName == p2.firstName
assert p1.lastName == p2.lastName
Updated:
Just tried this on grails 2.0.3. You need to be more explicit in your method signatures for grails to work. I changed the method signature for the getter to be String and the setter to be void and it worked. It did not work with just def. Grails 2 is much more strict about matching signatures than previous versions of grails were and I'm betting that this is part of it.
Also, you should specify that the fullName "property" is transient as it isn't a real property that should get persisted in the database. Here's the domain and test class that work for me in grails 2.0.3:
Person.groovy:
package com.example
class Person {
String firstName
String lastName
static transients = ["fullName"]
String getFullName() {
return "$firstName $lastName"
}
void setFullName(String name) {
firstName = name.split(" ")[0]
lastName = name.split(" ")[1]
}
}
PersonTests.groovy:
package com.example
import grails.test.mixin.*
import org.junit.*
/**
* See the API for {#link grails.test.mixin.domain.DomainClassUnitTestMixin} for usage instructions
*/
#TestFor(Person)
#Mock([Person])
class PersonTests {
void testFullName() {
Person p1 = new Person(firstName: "Joe", lastName: "Bloggs").save(failOnError: true)
Person p2 = new Person(fullName: "Joe Bloggs").save(failOnError: true)
assert p1.firstName == p2.firstName
assert p1.lastName == p2.lastName
assert p1.fullName == p2.fullName
}
}
This is caused by because
See 'bindable' here

Grails Integration test: Failure...Cannot invoke method addToPosts() on null object

:D
I was following a tutorial in a book, and I did follow it thoroughly.
However, come the part where I am supposed to write an integration test, it suddenly failed saying: Cannot invoke method addToPosts() on null object right after I ran the test. I wonder, what could be wrong... :| Please help! :) Below is the code for the test:
void testFirstPost() {
def user = new User(userID: 'joemillan', password:'youaretheonly',
homepage: 'www.geeee.com').save()
def post = new Post (content: 'hellloo oasdo sjdosa daodkao ')
user.addToPosts(post)
assertEquals 1, User.get(user.id).posts.size()
}
Here is the user class:
class User {
String userID
String password
String homepage
Profile profile
static hasMany=[posts:Post, tags:Tag]
static constraints = {
userID (unique: true, size: 6..20)
password (size: 6..20, validator:{password,userID-> return password !=userID.userID}) //validator = The password must not match the username.
homepage (url:true, nullable: true)
profile (nullable: true)
}
}
Here is the Post class:
class Post {
String content
Date dateCreated
static constraints = {
content (blank:false)
}
static belongsTo = [user:User]
static hasMany = [tags:Tag]
static mapping = {
sort dateCreated: "desc"
}
}
save() returns null if validation fails, and "www.geeee.com" isn't a valid URL. It would work with "http://www.geeee.com".
But you should split the creation and saving into 2 steps so you can check it:
def user = new User(userID: 'joemillan', password:'youaretheonly',
homepage: 'www.geeee.com')
user.save()
assertFalse user.hasErrors()
or use failOnError if you are confident that that part should succeed and only want to test the other parts, e.g.
def user = new User(userID: 'joemillan', password:'youaretheonly',
homepage: 'www.geeee.com').save(failOnError: true)

Why One-to-one relationship dosen't work?

I'm trying to create a very simple relationship between two objects. Can anybody explain me why I can't find the Company object via findBy method?
class Company {
String name
String desc
City city
static constraints = {
city(unique: true)
}
}
class City {
String name
static constraints = {
}
}
class BootStrap {
def init = { servletContext ->
new City(name: 'Tokyo').save()
new City(name: 'New York').save()
new Company(name: 'company', city: City.findByName('New York')).save()
def c = Company.findByName('company') // Why c=null????!
}
def destroy = {
}
}
A field called desc conflicts with the database keyword for descending sort. Per default a field is nullable:false in Grails. So first rename that field to for example description and then provide one or mark that field as nullable:true in your constraints.
class BootStrap {
def init = { servletContext ->
new City(name: 'Tokyo').save()
new City(name: 'New York').save()
new Company(name: 'company', city: City.findByName("New York")).save()
assert Company.findByName('company') != null
}
}
Remember that you can always check for the errors that prevent Grails from saving your objects to the database easily:
def invalidCompany = new Company() // missing required name property
if (!invalidCompany.validate())
invalidCompany.errors.each { println it }

Resources