Grails CSV Plugin, CSVWriter Example not working - plugins

I'm working on a Grails project and I want to write some data from different domain objects to a csv. I thought I would just be able to use the CSV Plugin available. I downloaded and installed the Grails CSV plugin v.3 and to get it working I just thought I would try the example. Unfortunately, the example isn't working and I'm not sure why. I've posted the example below.
def sw = new StringWriter()
def b = new CSVWriter(sw) {
col1 { it.val1 }
col2 { it.val2 }
}
b << [val1: 'a', val2: 'b']
b << [val1: 'c', val2: 'd']
assert b.writer.toString() == '''"col1","col2"
"a","b"
"c","d"'''
It is throwing a compiler error saying unexpected token at col1. Am I doing something wrong here?
I was looking at the export plugin as well, but it doesn't seem as though I'm able to use data from multiple domain classes with that.

Try changing your code to:
def sw = new StringWriter()
def b = new CSVWriter(sw, {
col1 { it.val1 }
col2 { it.val2 }
})
Generally, when the last argument is a closure, you can specify it outside parentheses, but it works only in top-level expressions -- in this example the top-level expression is an assignment.

Related

Exclude null columns in an update statement - JOOQ

I have a POJO that has the fields that can be updated. But sometimes only a few fields will need to be updated and the rest are null. How do I write an update statement that ignores the fields that are null? Would it be better to loop through the non missing ones and dynamically add to a set statement, or using coalesce?
I have the following query:
jooqService.using(txn)
.update(USER_DETAILS)
.set(USER_DETAILS.NAME, input.name)
.set(USER_DETAILS.LAST_NAME, input.lastName)
.set(USER_DETAILS.COURSES, input.courses)
.set(USER_DETAILS.SCHOOL, input.school)
.where(USER_DETAILS.ID.eq(input.id))
.execute()
If there is a better practice?
I don't know Jooq but it looks like you could simply do this:
val jooq = jooqService.using(txn).update(USER_DETAILS)
input.name.let {jooq.set(USER_DETAILS.NAME, it)}
input.lastName.let {jooq.set(USER_DETAILS.LAST_NAME, it)}
etc...
EDIT: Mapping these fields explicitly as above is clearest in my opinion, but you could do something like this:
val fields = new Object[] {USER_DETAILS.NAME, USER_DETAILS.LAST_NAME}
val values = new Object[] {input.name, input.lastName}
val jooq = jooqService.using(txn).update(USER_DETAILS)
values.forEachIndexed { i, value ->
value.let {jooq.set(fields[i], value)}
}
You'd still need to enumerate all the fields and values explicitly and consistently in the arrays for this to work. It seems less readable and more error prone to me.
In Java, it would be somthing like this
var jooqQuery = jooqService.using(txn)
.update(USER_DETAILS);
if (input.name != null) {
jooqQuery.set(USER_DETAILS.NAME, input.name);
}
if (input.lastName != null) {
jooqQuery.set(USER_DETAILS.LAST_NAME, input.lastName);
}
// ...
jooqQuery.where(USER_DETAILS.ID.eq(input.id))
.execute();
Another option rather than writing this UPDATE statement is to use UpdatableRecord:
// Load a POJO into a record using a RecordUnmapper
UserDetailsRecord r =
jooqService.using(txn)
.newRecord(USER_DETAILS, input)
(0 .. r.size() - 1).forEach { if (r[it] == null) r.changed(it, false) }
r.update();
You can probably write an extension function to make this available for all jOOQ records, globally, e.g. as r.updateNonNulls().

Filtering a collection of IO's: List[IO[Page]] scala

I am refactoring a scala http4s application to remove some pesky side effects causing my app to block. I'm replacing .unsafeRunSync with cats.effect.IO. The problem is as follows:
I have 2 lists: alreadyAccessible: IO[List[Page]] and pages: List[Page]
I need to filter out the pages that are not contained in alreadyAccessible.
Then map over the resulting list to "grant Access" in the database to these pages. (e.g. call another method that hits the database and returns an IO[Page].
val addable: List[Page] = pages.filter(p => !alreadyAccessible.contains(p))
val added: List[Page] = addable.map((p: Page) => {
pageModel.grantAccess(roleLst.head.id, p.id) match {
case Right(p) => p
}
})
This is close to what I want; However, it does not work because filter requires a function that returns a Boolean but alreadyAccessible is of type IO[List[Page]] which precludes you from removing anything from the IO monad. I understand you can't remove data from the IO so maybe transform it:
val added: List[IO[Page]] = for(page <- pages) {
val granted = alreadyAccessible.flatMap((aa: List[Page]) => {
if (!aa.contains(page))
pageModel.grantAccess(roleLst.head.id, page.id) match { case Right(p) => p }
else null
})
} yield granted
this unfortunately does not work with the following error:
Error:(62, 7) ';' expected but 'yield' found.
} yield granted
I think because I am somehow mistreating the for comprehension syntax, I just don't understand why I cannot do what I'm doing.
I know there must be a straight forward solution to such a problem, so any input or advice is greatly appreciates. Thank you for your time in reading this!
granted is going to be an IO[List[Page]]. There's no particular point in having IO inside anything else unless you truly are going to treat the actions like values and reorder them/filter them etc.
val granted: IO[List[Page]] = for {
How do you compute it? Well, the first step is to execute alreadyAccessible to get the actual list. In fact, alreadyAccessible is misnamed. It is not the list of accessible pages; it is an action that gets the list of accessible pages. I would recommend you rename it getAlreadyAccessible.
alreadyAccessible <- getAlreadyAccessible
Then you filter pages with it
val required = pages.filterNot(alreadyAccessible.contains)
Now, I cannot decipher what you're doing to these pages. I'm just going to assume you have some kind of function grantAccess: Page => IO[Page]. If you map this function over required, you will get a List[IO[Page]], which is not desirable. Instead, we should traverse with grantAccess, which will produce a IO[List[Page]] that executes each IO[Page] and then assembles all the results into a List[Page].
granted <- required.traverse(grantAccess)
And we're done
} yield granted

Scala how to use regex on endsWith?

I'm trying to figure out how to isolate all file extensions from a list of file names using regex and endsWith.
So as an example
input:
file.txt, notepad.exe
output:
txt, exe
What my idea is, is to use filter to get file names that endsWith("."_). But endsWith("."_) doesn't work.
Any suggestions?
You really do not want to filter, you want to map each filename into its extension.
(and maybe then collect only the ones that had an extension and probably you only want each unique extension)
You can use a regex for that.
object ExtExtractor {
val ExtRegex = """.*\.(\w+)?""".r
def apply(data: List[String]): Set[String] =
data.iterator.collect {
case ExtRegex(ext) => ext.toLowerCase
}.toSet
}
You can see it running here.
how about using split('.') which will return a
String[] parts = fileName.split("\\.");
String extension = parts[parts.length-1];

programmatically setting cq:tags save blank value in the node in AEM

I am trying to programmatically extract data from a json string, converts into a string array and adding it as cq:tags property and corresponding values into a node, however when I do so, though cq:tags property is added but with blank values.
My node is something like this: /content/<my project node>/ContentPage/jcr:content
ResourceResolver resolver = CommonUtils.getResourceResolver(resourceResolverFactory);
String[] strValue = tagList.stream().toArray(String[]::new); // tagList has String values in form of array.
Resource resource = resolver.getResource(CONTENT_DATA_NODE);
if (resource != null) {
Node node = resource.adaptTo(Node.class);
if (node != null) {
NodeIterator nodeIterator = node.getNodes();
while (nodeIterator.hasNext()) {
innerNode = nodeIterator.nextNode();
innerNode.setProperty(CQ_TAGS, strValue);
innerNode.getSession().save();
}
}
}
and my sling user mapper service is mybundle.core:datawrite=userdatawriter , also if my resource resolverfactory is null, I get resolver from request directly.
Initially, I thought it could be an access issue, so I programmatically tried with any random property and value:
property: xyz , values: aa,bb,cc,dd
Which is written by my code without any issues,
it is only when programmatically adding cq:tags is when the problem arises. Though I can add cq:tags with any long list of values manually without any issues, either from page properties or in the crxde node itself.
What am I missing here and doing wrong in the code which can not only add cq:tags but also overwrite if cq:tags exists.
P.S: my AEM version is AEM 6.5 SP2
I can see the same thing happen in AEM 6.4.3. Immediately upon saving the property, the value can be read as expected. Here's a few quick examples I ran in the AEM Groovy console.
def node = getNode('/content/screens/we-retail/apps/virtual-showroom/en/jcr:content')
String[] arr = ['a', 'b', 'c'];
String[] tagArr = ['we-retail:equipment', 'we-retail:activity/biking']
node.setProperty('foo', arr)
println node.getProperty('foo').values // prints the a, b,c tags
node.setProperty('cq:tags', tagArr)
session.save()
println node.getProperty('cq:tags').values // prints the a, b,c tags
println node.getProperty('foo').values // prints the a, b,c tags
However, upon inspecting the page in CRXDE, I can see that the property is empty. This does not happen when the values you use match existing tags in AEM. For example:
def node = getNode('/content/screens/we-retail/apps/virtual-showroom/en/jcr:content')
String[] arr = ['a', 'b', 'c'];
String[] tagArr = ['we-retail:equipment', 'we-retail:activity/biking']
node.setProperty('foo', arr)
println node.getProperty('foo').values
node.setProperty('cq:tags', tagArr)
println node.getProperty('cq:tags').values // prints the we-retail tags
session.save()
println node.getProperty('foo').values
println node.getProperty('cq:tags').values // prints the we-retail tags
and the same values are visible in CRXDE.
This behaviour, I believe, is controlled by the Day CQ Tagging Service (com.day.cq.tagging.impl.JcrTagManagerFactoryImpl)
Unchecking the box will disable validation and allow you to persist those values. However, tagging a page with tags that don't exist will cause its own share of problems. Instead, I would suggest making sure to create those tags before using them.

Play! + Scala: Split string by commnas then Foreach loop

I have a long string similar to this:
"tag1, tag2, tag3, tag4"
Now in my play template I would like to create a foreach loop like this:
#posts.foreach { post =>
#for(tag <- #post.tags.split(",")) {
<span>#tag</span>
}
}
With this, I'm getting this error: ')' expected but '}' found.
I switched ) for a } & it just throws back more errors.
How would I do this in Play! using Scala?
Thx in advance
With the help of #Xyzk, here's the answer: stackoverflow.com/questions/13860227/split-string-assignment
Posting this because the answer marked correct isn't necessarily true, as pointed out in my comment. There are only two things wrong with the original code. One, the foreach returns Unit, so it has no output. The code should actually run, but nothing would get printed to the page. Two, you don't need the magic # symbol within #for(...).
This will work:
#for(post <- posts)
#for(tag <- post.tags.split(",")) {
<span>#tag</span>
}
}
There is in fact nothing wrong with using other functions in play templates.
This should be the problem
#for(tag <- post.tags.split(",")) {
<span>#tag</span>
}