Groovy: More elegant way to achieve this? - groovy

I have following groovy function:
def getDependantLibs(updatedLib) {
def dependants = []
getAllLibs().each { lib ->
try {
def ref = getFromStash("/repos/${lib}/dependencies.json").find { it.name == updatedLib }
if (ref != null) {
dependants.add([ name: lib, version: ref.version ])
}
} catch (err) {}
}
return dependants
}
My question is, can I achieve this in a more elegant way (maybe with groovy Collecion functions like collect, flatten, ...)

Yes, you can use collectMany. E.g.:
def deps = [1,2,3,4]
println deps.collectMany{
try {
if (it&1) {
throw new RuntimeException(it.toString())
}
(0..it).collect{
[x: it]
}
}
catch (Exception e) {
println "failed: $e.message"
return []
}
}
// output:
// failed: 1
// failed: 3
// [[x:0], [x:1], [x:2], [x:0], [x:1], [x:2], [x:3], [x:4]]

Instead of using Collection.each(Closure closure) you can utilize Collection.collect(Closure transform) for transforming each element of the list from one format to another and Collection.findAll(Closure predicate) to filter null elements from final list. Something like this:
def getDependantLibs(updatedLib) {
return getAllLibs().collect { lib ->
try {
[lib: lib, ref: getFromStash("/repos/${lib}/dependencies.json").find { it.name == updatedLib }]
} catch (err) {
[lib: lib, ref: null]
}
}.findAll { it.ref != null }.collect { [name: it.lib, version: it.ref.version] }
}
Using Java 8 Stream API
You can always use Java 8 Stream API with Groovy. The main advantage of using Stream API in this case is that all operations on stream are lazy - they are executed when reduction function is called. It means that you can apply n-number of transformations and only one iteration will be triggered. In Groovy if you apply let's say 3 bulk collection methods, Groovy will iterate 3 times.
Below you can find an example of using Java 8 Stream API in Groovy:
def getDependantLibsWithStream(updatedLib) {
return getAllLibs().stream()
.map({ lib ->
try {
[lib: lib, ref: getFromStash("/repos/${lib}/dependencies.json").find { it.name == updatedLib }]
} catch (err) {
[lib: lib, ref: null]
}
})
.filter({ it.ref != null })
.map({ [name: it.lib, version: it.ref.version] })
.collect(Collectors.toList())
}
I hope it helps.

Related

Jenkins Library / Groovy Script - Wrap Code dynamically

I'm developing a Jenkins shared library right now.
I wasn't able to figure how to easily "wrap" a code inside a function without copy-pasting the whole code. For example: If a developer sets a value to true, then I want to wrap the whole code inside a function. Right now I want to use this to allow e.g. the gitlabIntegration to be turned off from the Jenkinsfile.
Example:
// vars/stageWrapper.groovy
def call(Map parameters = [:], body) {
stage(stageName) {
if (pushtoGitlab) {
gitlabCommitStatus(stageName) {
if (!containerName) body()
else {
container(containerName) {
body()
}
}
}
} else {
if (!containerName) body()
else {
container(containerName) {
body()
}
}
}
}
}
let the user select if the stage should be pushed to gitlab via the gitlabCommitStatus wrapper.
switch to a specified container or use default container (if none is specified)
To realize this I currently repeat the code, which I really don't like...
Is there any way of achieving the same, but without repeating the same code over and over?
Thank You!
In Groovy you can reuse a Closure in different DSL-Builders by setting it's delegate to builder's delegate.
Something like this should work:
def containerNameBody = { body ->
if (!containerName)
body()
else
container(containerName) {
body()
}
}
def call(Map parameters = [:], body) {
stage(stageName) {
containerNameBody.delegate = delegate
if (pushtoGitlab)
gitlabCommitStatus(stageName) {
containerNameBody body
}
else
containerNameBody body
}
}
How about following approach to pass down the param into function, then decide how to do inside the function by the param value.
def gitHub(gitHubOn) {
}
def gitLab(gitLabOn) {
}
def call(Map parameters = [:], body){
//some code....
foo=bar
gitLab(parameters.gitLabOn)
gitHub(parameters.gitHubOn)
body()
}

RxJs getValue for nested BehaviorSubject object

I'm working with angular 4 and RxJS 5, I have an object with the following structure (with nested subjects, in this example only 2 levels):
objs = BehaviorSubject<MyObj[]>;
MyObj = {
prop1: BehaviorSubject<string> = "prop1";
prop2: BehaviorSubject<string> = "prop2";
prop1.subscribe(newValue => prop2 = prop1);
}
So if I try to output the value of objs I got something like the following:
console.log(objs.value) =>
[
{
prop1: BehaviorSubject<string>;
prop2: BehaviorSubject<string>;
},
{
...
}]
so the result doesn't include the values of prop1 and prop2 but I still have BehaviorSubject at the second level.
Is there a way to get the nested objs value like the following?:
objs = [{
prop1: "prop1";
prop2: "prop2"
}, {...}]
I'm wondering if there's a sort of the knockout ko.toJSON (http://knockoutjs.com/documentation/plugins-mapping.html)
Do you think it is generally a bad practice to have nested BehaviorSubjects or people do it everyday :)?
No, there is nothing builtin that does that. You could certainly roll your own method. This might be something you can start with:
function isPrimitive(obj: any) {
return obj !== Object(obj);
}
function desubjectify(obj: any) {
if(isPrimitive(obj)) {
return obj;
}
if(obj instanceof BehaviorSubject) {
return desubjectify(obj.value);
}
let transformed: any = {};
for(let key of Object.keys(obj).filter(key => !key.startsWith('_'))) {
let value = obj[key];
if(value instanceof BehaviorSubject) {
transformed[key] = desubjectify(value.value);
} else {
transformed[key] = desubjectify(value);
}
}
return transformed;
}
Nested behavior subjects seems fishy to me but I wouldn't rule it out immediately with out knowing more details. However, I would recommend you find a good tutorial on ngrx/store and read it. Anytime people start to work a lot with behavior subjects I would recommend this.

(Swift.LazyMapCollection<Swift.Dictionary<Swift.String, Swift.String>(_base:[ ]

The code says that it is perfectly fine and that there are no errors, but when I go to run the simulator, the words will include:
(Swift.LazyMapCollection < Swift.Dictionary < Swift.String, Swift.String > (_base:[ ]
I am trying to create a quote app that displays a quote.
Here is the code for the Import of the Plist:
import Foundation
struct ImportList {
let path: String
init(FileName: String) {
self.path = NSBundle.mainBundle().pathForResource("\(FileName)", ofType:"plist")!
}
var dict: Dictionary<String, String> {
return NSDictionary(contentsOfFile: path)! as! Dictionary
}
var array: Array<AnyObject> {
return [String](arrayLiteral: String(dict.keys) { $0 as String})
}
func count() -> Int {
return array.count
}
}
Thank you.
Don't use arrayLiteral in this case, just use Array():
var array: Array<AnyObject> {
return Array(dict.keys)
}
It safely converts the lazy collection to an actual array.

Return Nested Key in Groovy

I am trying to determine the best way to return nested key values using groovy. If I have a map:
def map = [
OrganizationName: 'SampleTest',
Address: [
Street: '123 Sample St',
PostalCode: '00000',
]
]
Is there a way to return all of the keys? OrganizationName, OrganizationURL, Address.Street, Address.PostalCode? If I didn't have an map within a map I could use map.keySet() as String[]. Should I just loop through each key and see if it is an instanceof another map?
The Groovy libraries don't provide a method for this, but you can write your own. Here's an example that you can copy-paste into the Groovy console
List<String> getNestedMapKeys(Map map, String keyPrefix = '') {
def result = []
map.each { key, value ->
if (value instanceof Map) {
result += getNestedMapKeys(value, keyPrefix += "$key.")
} else {
result << "$keyPrefix$key"
}
}
result
}
// test it out
def map = [
OrganizationName: 'SampleTest',
Address: [
Street: '123 Sample St',
PostalCode: '00000',
]
]
assert ['OrganizationName', 'Address.Street', 'Address.PostalCode'] == getNestedMapKeys(map)
Use Following generic recursion Method To generate the list of all nested map keys
def getListOfKeys(def map, String prefix,def listOfKeys){
if(map instanceof Map){
map.each { key, value->
if(prefix.isEmpty()){
listOfKeys<<key
}else{
listOfKeys<< prefix+"."+key
}
if(value instanceof List){
List list = value
list.eachWithIndex { item, index ->
if(prefix.isEmpty()){
getListOfKeys(item, key+"["+index+"]",listOfKeys)
}else{
getListOfKeys(item, prefix+"."+key+"["+index+"]",listOfKeys)
}
}
}else if(value instanceof Map){
if(prefix.isEmpty()){
getListOfKeys(value, key,listOfKeys)
}else{
getListOfKeys(value, prefix+"."+key,listOfKeys)
}
}
}
}
}
call above method as follows
def void findAllKeysInMap(){
Map map = [ "fields":[
"project":
[
"key": "TP"
],
"summary": "Api Testing Issue.",
"description": "This issue is only for api testing purpose",
"issuetype": [
"name": ["Bug":["hello":[["saurabh":"gaur","om":"prakash"], ["gaurav":"pandey"], ["mukesh":"mishra"]]]]
]
]
]
def listOfKeys=[]
getListOfKeys(map, '', listOfKeys)
println "listOfKeys>>>"+listOfKeys
}
output:
listOfKeys>>>[fields, fields.project, fields.project.key, fields.summary, fields.description, fields.issuetype, fields.issuetype.name, fields.issuetype.name.Bug, fields.issuetype.name.Bug.hello, fields.issuetype.name.Bug.hello[0].saurabh, fields.issuetype.name.Bug.hello[0].om, fields.issuetype.name.Bug.hello[1].gaurav, fields.issuetype.name.Bug.hello[2].mukesh]
There's no such method You're looking for in groovy. You need to do it using instanceof and probably a recursive method.
Slightly shorter:
String key = 'Address.Street'
key.split('\\.').inject(yourMap) {map, path -> map[path]}
If you can't guarantee that the path exists and is complete (say, if you tried to access OrganizationName.id) you'll need to add some safeguards (check that map is not null, and that it's really a Map)

Reduce code repetition in Groovy closures

In a piece of Gradle build script, the amount of code i'm repeating is increasing. All tasks have a big part in common, except for a few lines:
task copyZipFile() {
doLast {
def remoteBuildProperties = getRemoteBuildProperties(project)
ant {
taskdef(name: 'ftp',
classname: 'org.apache.tools.ant.taskdefs.optional.net.FTP',
classpath: configurations.ftpAntTask.asPath)
ftp(server: remoteBuildProperties['host.name'],
userid: remoteBuildProperties['username'],
password: remoteBuildProperties['password'],
remotedir: 'some-folder', // This value differs from call to call
passive: 'true') {
// The next two lines also are different per case, and might be less or more lines
fileset(dir: rootProject.buildDir) { include(name: 'build.zip') }
fileset(dir: rootProject.projectDir) { include(name: 'build.properties') }
}
}
}
}
I don't like to repeat myself, so I'd like to reduce this code to a new helper method that does this trick, and a simple caller, something like:
task copyZipFile() {
doLast {
def remoteBuildProperties = getRemoteBuildProperties(project)
upload(remoteBuildProperties, 'some-folder') {
fileset(dir: rootProject.buildDir) { include(name: 'build.zip') }
fileset(dir: rootProject.projectDir) { include(name: 'build.properties') }
}
}
}
How would I achieve this?
You can pass the inner closure to your upload method as the final parameter. Set the delegate to the original builder delegate so the inner closure calls get handled properly. For example:
def upload(remoteBuildProperties, folder, body) {
ant {
taskdef(name: 'ftp',
classname: 'org.apache.tools.ant.taskdefs.optional.net.FTP',
classpath: configurations.ftpAntTask.asPath)
ftp(server: remoteBuildProperties['host.name'],
userid: remoteBuildProperties['username'],
password: remoteBuildProperties['password'],
remotedir: folder,
passive: 'true') {
body.delegate = delegate
body()
}
}
}

Resources