I'm trying to add new functionality, without refactoring the entire code. Actually the code calls msg.typeOfMsg('msg'), but I want to add validation when it comes true change all the typeOfMsg to a specific type:
def specialRule = true
def message = changeMessageType()
def changeMessageType() {
def toInform = { def message -> thePath.toChange.inform(message) }
return specialRule ? [reprove : toInform, approve : toInform] : thePath.toChange
}
println(message.reprove('Standart reprove message')
This is the solution I found, it works, but I have a lot of message types and custom message types, there's some way to dynamically change all calls to inform, without refactoring all the code to calls something like message(typeOfMessage, message)?
-----Edit-----
Here's a runnable version
def message = changeMessageType()
def changeMessageType() {
def originalPath = new OriginalPath()
def specialRule = true
def toInform = { def message -> originalPath.inform(message) }
return specialRule ? [reprove : toInform, approve : toInform] : originalPath
}
message.approve('Standart reprove message')
class OriginalPath {
def reprove(message) {
println "Reprove: ${message}"
}
def approve(message) {
println "Approve: ${message}"
}
def inform(message) {
println "Inform: ${message}"
}
}
Related
I'm changing my jenkins job's SCM url from, lets say gitlab to github by using Groovy script. Is there any way to minimize the complexity of this code ?
I have used Jenkins.instance.items.each {} which traverses through all the jobs in my jenkins
import hudson.plugins.git.*
import jenkins.*
import jenkins.model.*
def modifyGitUrl(url) {
def newurl = url.replace("source", "destination")
return newurl
}
def modifyGitID(credentialsId) {
def newID = ("Jenkins User ID")
return newID
}
Jenkins.instance.items.each {
if(it.name == "expected name"){
if (it.scm instanceof GitSCM) {
def oldScm = it.scm
def newUserRemoteConfigs = oldScm.userRemoteConfigs.collect {
new UserRemoteConfig(modifyGitUrl(it.url), it.name, it.refspec, modifyGitID(it.credentialsId))
}
def newScm = new GitSCM(newUserRemoteConfigs, oldScm.branches, oldScm.doGenerateSubmoduleConfigurations,
oldScm.submoduleCfg, oldScm.browser, oldScm.gitTool, oldScm.extensions)
it.scm = newScm
it.save()
}
}
}
I am trying to augment the 'load' pipeline step function and I keep getting an error. I have found the code it executes based on the stack trace but I can't for the life of me figure out why it wouldn't just call the code as written.
I have written lots-and-lots of Java code so I know what it's trying to do. I just don't understand why it's trying to do it or how to convince it to stop! The groovy sample works perfectly! BTW: if there is an idiomatic way to do this in groovy/jenkins, I am all in.
Jenkins version: 2.176.1
Groovy plugin: 2.2
test.groovy
def someFunction(def params){
println("someFunction ${params}")
}
def someFunction2(def params){
println("someFunction2 ${params}")
}
def mainFunc(def stuff){
}
def somemainThingrunFunmain(){
}
def ___cps___21685(){
}
def ___cps___21688(){
}
this
main.groovy
def loaded = evaluate('test.groovy' as File)
def toAugment = loaded.class.declaredMethods*.name.findAll { !(it =~ '^(main|run)$|^[$]|^(___cps___)') }
def proxy = new Script(this.binding) {
#Override
Object run() {
monad.run()
}
}
toAugment.each {
proxy.metaClass."${it}" = { "logging ${it}".tap { println(it)} } >> loaded.&"${it}"
}
proxy.someFunction('hello world1')
proxy.someFunction2('hello world2')
outputs:
called
someFunction hello world1
called
someFunction2 hello world2
Now in Jenkins:
Jenkinsfile:
library 'common-libraries#chb0'
node('java') {
stage('SCM') {
checkout scm
}
def loaded = load('test.groovy')
stage('experiment') {
loaded.someFunction('hello world1')
loaded.someFunction2('hello world2')
}
}
adapted library (in common-library:vars/load.groovy):
def call(String path) {
def loaded = steps.load(path)
def proxy = new Script(this.getBinding()) { // error here
#Override
Object run() {
loaded.run()
}
}
// remove groovy and jenkins generated functions. Don't touch those
def toAugment = loaded.class.declaredMethods*.name.findAll { !(it =~ '^(main|run)$|^[$]|^(___cps___)') }
toAugment.each {
proxy.metaClass."${it}" = { "logging ${it}".tap { println(it) } } >> loaded.&"${it}"
}
}
exception:
groovy.lang.GroovyRuntimeException: Could not find matching constructor for: load$1(load, Script1, groovy.lang.Binding)
at groovy.lang.MetaClassImpl.invokeConstructor(MetaClassImpl.java:1732)
at groovy.lang.MetaClassImpl.invokeConstructor(MetaClassImpl.java:1532)
at org.codehaus.groovy.runtime.callsite.MetaClassConstructorSite.callConstructor(MetaClassConstructorSite.java:49)
at org.codehaus.groovy.runtime.callsite.CallSiteArray.defaultCallConstructor(CallSiteArray.java:60)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callConstructor(AbstractCallSite.java:235)
at com.cloudbees.groovy.cps.sandbox.DefaultInvoker.constructorCall(DefaultInvoker.java:25)
I have one action in my controller that upload csv file with list of numbers.
Now the process is first I need to delete existing data from the table on certain condition then insert fresh data.
My snippet code is as follows..
Controller:
#Transactional
def uploadFile() {
if(!params?.updateExisting){
println "Going to call service to delete records"
myService.deleteNumbers()
def newList = Number.findAllByDeleted(false)
println "NEW LS:"+newList
//HERE I'm GETTING BLANK
}
def file = request.getFile("fileCsv")
file.inputStream
.splitEachLine(',') { fields ->
Number.withNewTransaction {
def number = fields[0]?.toString().replaceAll(" ","").replaceAll("-","")
Number numberInstance = new Number()
def numberExist = Number.findAllByNumberAndDeleted(number, false)
//HERE NUMBER IS STILL EXIST
if(!numberExist){
numberInstance.number = number
numberInstance.save(flush:true)
count++
}else{
println "Number exist: "+number
}
}
}
redirect(uri:'/number/list')
}
myService:
#Transactional
def deleteNumbers(){
Number.findAll().each {
it.deleted = true
it.save(flush: true)
}
}
After calling service method deleteNumbers I'm getting blank list NEW LS:[], But then def numberExist = Number.findAllByNumberAndDeleted(number, false) returns me a number means already exist.
Thanks..
Try removing Number.withNewTransaction closure. Your code should work..
I have some widgets in my application. I want to load them in a single page. It works always when loaded sequentially,but fails(throws exception-java.lang.IllegalStateException-Cannot ask for request attribute - request is not active anymore!) when loaded in parallel. Am I missing something ? Is there any other way to load these in parallel? Please suggest. Here is my code snippet
class TestController {
def widget1() {
render "Widget 1"
}
def widget2() {
render "Widget 2"
}
}
class ManagerController {
def loadSequential(){
def data = [:]
data['view1'] = g.include(action: 'widget1', controller: 'test')
data['view2'] = g.include(action: 'widget2', controller: 'test')
render data['view1']
render data['view2']
}
def loadParallel(){
def data = [:]
PromiseMap pm = new PromiseMap()
pm['view1'] = { g.include(action: 'widget1', controller: 'test') }
pm['view2'] = { g.include(action: 'widget2', controller: 'test') }
def result = pm.get()
render result['view1']
render result['view2']
}
}
Here's my 2p on this matter.
We've tried really hard to make Grails render pages in parallel but we couldn't make it work. After digging really deep we simply found that Grails is not ready for this kind of stuff. It's build in a way where rendering is always thread-exclusive.
I'd say it's all due to the org.codehaus.groovy.grails.web.pages.GroovyPageOutputStack that is not ready for a concurrent access.
I'll watch this space just in case someone managed to make it work.
Try this:
import static grails.async.Promises.*
...
def loadParallel() {
task {
def data = [:]
PromiseMap pm = new PromiseMap()
pm['view1'] = { g.include(action: 'widget1', controller: 'test') }
pm['view2'] = { g.include(action: 'widget2', controller: 'test') }
def result = pm.get()
render result['view1'] + result['view2']
}
}
In the loadSequential() example you are not loading response sequentially, but concatenating two views into a single response. Parallel rendering should be a client side job. See for example how Lift does it with jQuery.
how can i redirect to the same state more than one time using web flow
ex:
on('submit'){
def destinationInstance = Destination.get(params.destination)
def destinationGroupsInstance = DestinationGroup.get(params.destinationGroups)
def h = destinationInstance.addToDestinationGroups(destinationGroupsInstance)
}.to('flowList')
what i need is how to enter to this state more than one time until destinations ends
thx
on('submit'){
def destinationInstance = Destination.get(params.destination)
def destinationGroupsInstance = DestinationGroup.get(params.destinationGroups)
def h = destinationInstance.addToDestinationGroups(destinationGroupsInstance)
}.to{
(condition or while loop or for loop)
if success then
return "<state name>"
else
return "flowList"
}
Reference:
http://livesnippets.cloudfoundry.com/docs/guide/2.%20Grails%20webflow%20plugin.html
Well, you'd probably have something like the following code, which is untested but may give you a general idea.
def destinationFlow = {
initialize {
action {
flow.destination = Destination.get(params.id)
}
on('success').to 'destinationList'
}
destinationList {
render(view: 'destinationList')
on('addDestination') {
def destinationGroup = DestinationGroup.get(params.destinationGroupId)
flow.destination.addToDestinationGroups(destinationGroup)
}.to 'destinationList'
on('finish').to 'done'
}
done {
flow.destination.save()
redirect(...) // out of the flow
}
}
You'll need buttons on your destinationList view that invoke the 'addDestination' or 'finish' actions. See the WebFlow documentation and Reference Guide.