scala function with repeated parameters - function

I see that it possible to uses the following syntax for method that take parameters of a repeated type:
def capitalizeAll( args: String*) = {
args.map { args => args.capitalize }
}
However I was wondering how an function can be used instead of "args => args.capitalize"
for example (does not work):
def func(s: String): String = { s.capitalize }
def capitalizeAll2( args: String*) = {
args.map { func( args ) }
}
how can I make this work?
Cheers

There is no magic:
def func(s: String): String = { s.capitalize }
def capitalizeAll2( args: String*) = {
args.map { arg => func( arg ) }
}
Here I gave arg name to currently processed string (out of all args strings). Your first example works only because of shadowing (all strings are args and current string given the same name, which just shadows original).
Almost no magic...
def capitalizeAll3( args: String*) = {
args.map(func)
}
The latest example uses syntax sugar to apply function with only one parameter to args.

Related

Groovy LinkedHashMap returned from a Jenkins shared library is converted to String

I have a function in a Jenkins shared library which builds and returns a map object (class java.util.LinkedHashMap):
#!/usr/bin/env groovy
def call(Map config) {
script {
echo "Building nested map"
def MAP_VAR = [:]
MAP_VAR.put("one", [:])
MAP_VAR.put("two", [:])
MAP_VAR.get("one").put("a", "b")
MAP_VAR.get("two").put("c", "d")
echo "Returning: ${MAP_VAR}"
echo "Type: ${MAP_VAR.getClass()}"
return MAP_VAR
}
}
When this function runs the log output shows:
Returning: [one:[a:b], two:[c:d]]
Type: class java.util.LinkedHashMap
But when I call the function and assign the return value to a variable, it ends up being a string (class java.lang.String):
#!/usr/bin/env groovy
library identifier: 'library#main',
changelog: false,
retriever: modernSCM([ $class: 'GitSCMSource',
remote: 'git#github.com:org/repo.git',
credentialsId: 'credentials' ])
pipeline {
agent none
stages {
stage('Get map') {
agent any
steps {
script {
env.MAP_VAR = getMapVar()
}
}
}
stage('Check map') {
agent any
steps {
script {
echo "Value: ${env.MAP_VAR}"
echo "Type: ${env.MAP_VAR.getClass()}"
}
}
}
}
}
The output shows:
Value: {one={a=b}, two={c=d}}
Type: class java.lang.String
Ultimately I'm trying to access the map's properties in multiple stages within the Jenkinsfile. If I try this:
echo "Value: ${env.MAP_VAR['one']}"
I get:
groovy.lang.MissingPropertyException: No such property: one for class: java.lang.String
I've tried:
def env.MAP_VAR = getMapVar()
But that results in:
WorkflowScript: 59: unexpected token: def # line 59, column 21.
def env.MAP_VAR = getMapVar()
^
I've also tried:
def Map env.MAP_VAR = getMapVar()
But that results in:
WorkflowScript: 60: unexpected token: Map # line 60, column 25.
def Map env.MAP_VAR = getMapVar()
^
How can I get the Map/LinkedHashMap from the function as a Map/LinkedHashMap (which would allow me to access the properties/values of the map contents) and assign it to a global variable which can be used in all stages?
def env.MAP_VAR = getMapVar()
This will not work as you are trying to define a key MAP_VAR in env map.
def Map env.MAP_VAR = getMapVar()
This also will not work as you are trying to define MAP_VAR key in env map as def and also MAP. This is similar to doing something like String Integer my_var_name = something()
env.MAP_VAR = getMapVar()
You are storing the return value as a string in env environment variable map. Jenkins output is accurate given the code.
To achieve what you are trying to do, you can store it as a variable and it will work fine. In the following example, the variable goes into Groovy's script binding and will be available in the next stage. You can also define def MAP_VAR outside the pipeline block to get the same result.
#!/usr/bin/env groovy
library identifier: 'library#main',
changelog: false,
retriever: modernSCM([ $class: 'GitSCMSource',
remote: 'git#github.com:org/repo.git',
credentialsId: 'credentials' ])
pipeline {
agent none
stages {
stage('Get map') {
agent any
steps {
script {
MAP_VAR = getMapVar()
}
}
}
stage('Check map') {
agent any
steps {
script {
echo "Value: ${MAP_VAR}"
echo "Type: ${MAP_VAR.getClass()}"
}
}
}
}
}
This will print
Value: [one:[a:b], two:[c:d]]
Type: class java.util.LinkedHashMap
In stage two,
echo "${MAP_VAR['one']}"
will output
[a:b]

How to return json from Play Scala controller?

I would like to know that how can I return json response data from Play(2.2.x) Scala controller class to display on my view page ? I have json objects in Postgresql database(table name: "test" and having: id and name). Please provide me any solutions for it.
I have tried the following cases(a and b), but I am not sure why I am not getting the response(like: names) on my controller, so I can show them on my view page ? since I am very new to Play/Scala and Postgresql.
case a. If I give like:
model:
def getTestValuesFromTable() = {
DB.withConnection { implicit connection =>
val selectJson =SQL("select name from test").on().apply().collect {
case Row(id:Long, Some(name:String)) =>
new TestContent(name)
}
//.head
//JsObject(selectJson().map { row => row[Long]("id").toString -> JsString(row[String]("name")) }.toSeq)
}
}
controller:
def getTest = Action {
val response = TestContent.getTestValuesFromTable()
Ok("Done")
//Ok(response)
}
Output is: Done(application is executing fine without any exceptions, of course json data is not coming since I am returning: Done only, so getting output: "Done")
case b. If I do like this: getting error: not enough arguments for method apply: (n: Int)models.Testname in trait LinearSeqOptimized. Unspecified value parameter n. I really not sure how can I get my response for it ?
controller:
def getTest = Action {
val response = TestContent.getTestValuesFromTable()
// Ok("Done")
Ok(response)
}
model:
def getTestValuesFromTable(): JsValue = {
DB.withConnection { implicit connection =>
val selectJson = SQL("select * from test")
JsObject(selectJson().map { row => row[Long]("id").toString -> JsString(row[String]("name")) }.toSeq)
// val selectJson =SQL("select name from test").on().apply().collect {
// case Row(id:Long, Some(name:String)) =>
// new TestContent(name)
// }
//.head
JsObject(selectJson().map { row => row[Long]("id").toString -> JsString(row[String]("name")) }.toSeq)//not enough arguments for method apply: (n: Int)models.Testname in trait LinearSeqOptimized. Unspecified value parameter n.
}
}
Please let me know how to get my response ?
getJsonValuesFromTable method return nothing (Unit). To fix it change definition of this method to
def getJsonValuesFromTable(testContent: TestContent) = {
or explicitly setting type:
def getJsonValuesFromTable(testContent: TestContent): Unit = {
Also as a next step to let client know that you are returning json, you should set content type:
Ok(Json.obj(response)).as("application/json")

Scala - Can I define a function that receives any function as a parameter?

Is it possible, in Scala, to define a function that would receive any other function as a parameter?
It should be something like the following:
object Module extends SecureModule{
val bc = new MyBC()
def method(parameter: Type) = {
exec(bc.method(parameter))
}
def method2(parameter1: Type1, parameter2: Type2) = {
exec(bc.method2(parameter1,parameter2))
}
}
trait SecureModule {
def exec(f: ANY_PARAMETER => ANY_RESULT) = {
//some extra processing
f
}
}
is it possible? If so, how could I achieve this?
Thank you in advance.
The nice thing about scala is that you can create what seems to be your own syntax.
If what you want to do is wrap an operation so that you can do pre and post processing, as well as control the execution context, then you do this by using call-by-name parameters. For example, if we just wanted to time how long a block of code takes, then we could do something like this:
def timer[T](block: => T): (T,Long) = {
val startDate = new Date()
val result = block
val endDate = new Date()
(result, endDate.getTime()-startDate.getTime())
}
We can use it like this:
val (result,duration) = timer {
1+3
}
Or like this
val (result,duration) = timer {
"hello" + " world!"
}
And the result will have the correct type from the block that you pass in while also giving you the duration that you expect.
I am under the impression that your description is somewhat misleading.
The way I understand it, what you (might) want to do is delaying the execution of the bc.method calls until some other code has been performed.
If so, try this:
object Module extends SecureModule{
val bc = new MyBC()
def method(parameter: Type) = {
exec(() => bc.method(parameter))
}
def method2(parameter1: Type1, parameter2: Type2) = {
exec(() => bc.method2(parameter1,parameter2))
}
}
trait SecureModule {
def exec[Result](f: () => Result): Result = {
//some extra processing
f()
}
}
You can't take any function as a parameter. What would you even do it?
At best, you can take any function that has a specific number of parameters.
For example, here, f takes one argument and returns a value.
def exec[A,B](f: A => B)
And here, f takes two arguments:
def exec[A,B,C](f: (A, B) => C)
If you don't care about the return type of the function, you could always use Any instead of a type parameter, since functions are covariant in their return type:
def exec[A](f: A => Any)

Groovy JsonBuilder strange behavior when toString()

I need to create a json to use as body in an http.request. I'm able to build dynamically up the json, but I noticed a strange behavior when calling builder.toString() twice. The resulting json was totally different. I'm likely to think this is something related to a kind of buffer or so. I've been reading the documentation but I can't find a good answer. Here is a code to test.
import groovy.json.JsonBuilder
def builder = new JsonBuilder()
def map = [
catA: ["catA-element1", "catA-element2"],
catB:[],
catC:["catC-element1"]
]
def a = map.inject([:]) { res, k, v ->
def b = v.inject([:]) {resp, i ->
resp[i] = k
resp
}
res += b
}
println a
def root = builder.query {
bool {
must a.collect{ k, v ->
builder.match {
"$v" k
}
}
}
should([
builder.simple_query_string {
query "text"
}
])
}
println builder.toString()
println builder.toString()
This will print the following lines. Pay attention to the last two lines
[catA-element1:catA, catA-element2:catA, catC-element1:catC]
{"query":{"bool":{"must":[{"match":{"catA":"catA-element1"}},{"match":{"catA":"catA-element2"}},{"match":{"catC":"catC-element1"}}]},"should":[{"simple_query_string":{"query":"text"}}]}}
{"match":{"catC":"catC-element1"}}
In my code I can easily send the first toString() result to a variable and use it when needed. But, why does it change when invoking more than one time?
I think this is happening because you are using builder inside the closure bool. If we make print builder.content before printing the result (buider.toString() is calling JsonOutput.toJson(builder.content)) we get:
[query:[bool:ConsoleScript54$_run_closure3$_closure6#294b5a70, should:[[simple_query_string:[query:text]]]]]
Adding println builder.content to the bool closure we can see that the builder.content is modified when the closure is evaluated:
def root = builder.query {
bool {
must a.collect{ k, v ->
builder.match {
"$v" k
println builder.content
}
}
}
should([
builder.simple_query_string {
query "text"
}
])
}
println JsonOutput.toJson(builder.content)
println builder.content
The above yields:
[query:[bool:ConsoleScript55$_run_closure3$_closure6#39b6156d, should:[[simple_query_string:[query:text]]]]]
[match:[catA:catA-element1]]
[match:[catA:catA-element2]]
{"query":{"bool":{"must":[{"match":{"catA":"catA-element1"}},{"match":{"catA":"catA-element2"}},{"match":{"catC":"catC-element1"}}]},"should":[{"simple_query_string":{"query":"text"}}]}}
[match:[catC:catC-element1]]
You can easily avoid that with a different builder for the closure inside:
def builder2 = new JsonBuilder()
def root = builder.query {
bool {
must a.collect{ k, v ->
builder2.match {
"$v" k
}
}
}
should([
builder.simple_query_string {
query "text"
}
])
}
Or even better:
def root = builder.query {
bool {
must a.collect({ k, v -> ["$v": k] }).collect({[match: it]})
}
should([
simple_query_string {
query "text"
}
])
}

Grails JSON converter and JSONObject code breaks when moved to src/groovy

I am trying to move some code from a grails service file into src/groovy for better reuse.
import grails.converters.JSON
import org.codehaus.groovy.grails.web.json.JSONObject
class JsonUtils {
// seems like a clunky way to accomplish converting a domainObject
// to its json api like object, but couldn't find anything better.
def jsonify(obj, ArrayList removeableKeys = []) {
def theJson = obj as JSON
def reParsedJson = JSON.parse(theJson.toString())
removeableKeys.each { reParsedJson.remove(it) }
return reParsedJson
}
// essentially just turns nested JSONObject.Null things into java null things
// which don't get choked on down the road so much.
def cleanJson(json) {
if (json instanceof List) {
json = json.collect(cleanJsonList)
} else if (json instanceof Map) {
json = json.collectEntries(cleanJsonMap)
}
return json
}
private def cleanJsonList = {
if (it instanceof List) {
it.collect(cleanJsonList)
} else if (it instanceof Map) {
it.collectEntries(cleanJsonMap)
} else {
(it.class == JSONObject.Null) ? null : it
}
}
private def cleanJsonMap = { key, value ->
if (value instanceof List) {
[key, value.collect(cleanJsonList)]
} else if (value instanceof Map) {
[key, value.collectEntries(cleanJsonMap)]
} else {
[key, (value.class == JSONObject.Null) ? null : value]
}
}
}
but when I try to call jsonify or cleanJson from services I get MissingMethodExceptions
example call from grails service file:
def animal = Animal.read(params.animal_id)
if (animal) json.animal = JsonUtils.jsonify(animal, ['tests','vaccinations','label'])
error:
No signature of method: static org.JsonUtils.jsonify() is applicable for argument types: (org.Animal, java.util.ArrayList) values: [ ...]]\ Possible solutions: jsonify(java.lang.Object, java.util.ArrayList), jsonify(java.lang.Object), notify()
Also tried making the jsonify take an animal jsonify(Animal obj, ...) then it just said Possible solutions: jsonify(org.Animal, ...
The cleanJson method was meant to deal with JSONObject.Null things which have caused problems for us before.
example call:
def safeJson = JsonUtils.cleanJson(json) // json is request.JSON from the controller
error:
groovy.lang.MissingMethodException: No signature of method: static org.JsonUtils.cleanJson() is applicable for argument types: (org.codehaus.groovy.grails.web.json.JSONObject) values: [[...]]
Possible solutions: cleanJson(org.codehaus.groovy.grails.web.json.JSONObject)
All this code worked as it is when it was in service file. I am running grails 2.3.11 BTW
You've declared jsonify() and cleanJson() as instance methods and try to use them as static. Declare them as static and it should work:
class JsonUtils {
def static jsonify(obj, ArrayList removeableKeys = []) {
(...)
}
def static cleanJson(json) {
(...)
}
}
You need to define jsonify() and cleanJson() as static in order to call them statically.