I'm trying to accept multiple values from a groovy method into a Jenkins pipeline and keep hitting Pipeline Workflow errors, any pointers as to what I'm doing wrong here is greatly appreciated.
(env.var1, env.var2, env.var3) = my_func()
def my_func(){
def a =10
def b =10
def c =10
return [a, b, c]
}
I get following error:
expecting ')', found ',' #(env.var1, env.var2, env.var3) = my_func()
You are using Groovy's multiple assignment feature incorrectly. It works when you assign a collection of values to a list of new variables. You can't use this type of assignment to assign values to an existing object. Your code also fails when executed in plain Groovy:
def env = [foo: 'bar']
(env.var1, env.var2, env.var3) = my_func()
println env
def my_func(){
def a =10
def b =10
def c =10
return [a,b,c]
}
Output:
1 compilation error:
expecting ')', found ',' at line: 3, column: 14
In Jenkins environment, env variable is represented not by a map, but by EnvActionImpl object which means it does not even support plus() or putAll() methods. It only overrides getProperty() and setProperty() methods, so you can access properties with env.name dot notation.
Solution
The simplest solution to your problem is to use multiple assignment correctly and then set env variables from variables. Consider following example:
node {
stage("A") {
def (var1, var2, var3) = my_func()
env.var1 = var1
env.var2 = var2
env.var3 = var3
}
stage("B") {
println env.var1
}
}
def my_func() {
def a = 10
def b = 10
def c = 10
return [a, b, c]
}
Keep in mind that var1, var2 and var3 variables cannot already exist in current scope, otherwise compiler will throw an exception.
Related
Im trying to get the value of a map by running Code.eval_string/1 and it fails with error:
warning: variable "data" does not exist and is being expanded to "data()", please use parentheses to remove the ambiguity or change the variable name
nofile:1
** (CompileError) nofile:1: undefined function data/0
(elixir 1.10.2) lib/code.ex:332: Code.eval_string_with_error_handling/3
main.exs:65: RulesEngine.evaluate_object_by_condition/2
(elixir 1.10.2) lib/enum.ex:1396: Enum."-map/2-lists^map/1-0-"/2
main.exs:17: RulesEngine.evaluate_object_by_condition/2
Code is:
#doc "Evaluate condition"
#default_op "="
def evaluate_object_by_condition(data, condition) do
IO.puts("Evaluando una regla en particular:")
IO.inspect(condition)
IO.inspect(data)
attr = condition.attr
value = condition.value
IO.inspect(attr)
eval_data = Code.eval_string(attr)
op = Map.get(condition, "op", #default_op)
IO.puts("DATA to EVAL")
IO.inspect(eval_data)
# value_type = Map.get(condition, "type")
# Hacer type checking y agregar a value_type
## Falta obtener el valor del objeto
# res = evaluate("=", value, obj.value)
true
end
then I run:
obj = %{
value: 1,
tiene_beca: 1,
tiene_credito: 1
}
condition= %{
attr: "data.tiene_beca",
value: 1
}
RulesEngine.evaluate_object_by_condition(obj, rules_or)
So I'm trying to get the value of data.tiene_beca, and getting that variable name from a string, which would be the correct way of doing that in elixir?
First of all, consider whether you actually need all this flexibility. Code.eval_string runs the code without any checks or restrictions whatsoever, so this potentially opens a security hole in the code. I would do something like this instead:
["data", field_name] = String.split(attr, ".")
field_name = String.to_existing_atom(field_name)
eval_data = data[field_name]
That said, the reason your code isn't working is that Code.eval_string doesn't have access to local variables in the calling function, so you'd need to pass the variable as a binding explicitly:
eval_data = Code.eval_string(attr, [data: data])
In the below code, can anyone explain why does t1:print() works but (t1):print fails. I am attempting to make something like (t1 * 3):print() work without using an intermediate variable.
function classTestTable(members)
members = members or {}
local mt = {
__metatable = members;
__index = members;
}
function mt.print(self)
print("something")
end
return mt
end
TestTable = {}
TestTable_mt = ClassTestTable(TestTable)
function TestTable:new()
return setmetatable({targ1 = 1}, TestTable_mt )
end
TestTable t1 = TestTable:new()
t1:print() -- works fine.
(t1):print() -- fails with error "attempt to call a boolean value"
Lua expressions can extend over multiple lines.
print
(3)
Will print 3
So
t1:print()
(t1):print()
actually is equivalent to
t1:print()(t1):print()
or
local a = t1:print()
local b = a(t1)
b:print()
So you're calling the return value of t1:print()
To avoid that follow Egors advice and separate both statements with a semicolon.
t1:print();(t1):print()
I want to assign a value to an existing variable, but the name of the variable is dynamic. How do I do that
def a1 = 0;
def b = 1;
eval("a${b} =1;");
print a1
No need for javascript here:
def name = 'someName';
def value = 'someValue';
new GroovyShell(this.binding).evaluate("${name} = '${value}'")
assert someName == value;
Though this doesn't answer your question exactly, an easy way around this is to drop your dynamic variables as map keys instead... avoid needing to eval them
def b = 1
def map = [:]
map."a${b}" = 1
assert map."a${b}" == 1
println(map) // result is [a1:1]
I would like to perform double substitution.
When printing:
def y = "\${x}"
def x = "world"
def z = "Hello ${y}"
println z
It prints:
Hello ${x}
When I would like it to print Hello World, I tried performing a double evaluation ${${}}, casting it to org.codehaus.groovy.runtime.GStringImpl, and a desperate ${y.toStrin()}
Edit:
To be more clear I mean this, but in Groovy:
https://unix.stackexchange.com/questions/68042/double-and-triple-substitution-in-bash-and-zsh
https://unix.stackexchange.com/questions/68035/foo-and-zsh
(Why I am doing this?: Because we have some text files that we need evaluate with groovy variables; the variables are many and in different part of the code are different, therefore I would like to have a solution working across all cases, not to have to bind each time each variable, not adding many lines of code)
So with what you have you're escaping the $ so it is just interpreted as a string.
For what you are looking to do I would look into Groovys's templating engines:
http://docs.groovy-lang.org/docs/next/html/documentation/template-engines.html
After reading your comment I played around with a few ideas and came up with this contrived answer, which is also probably not quite what you are looking for:
import groovy.lang.GroovyShell
class test{
String x = "world"
String y = "\${x}"
void function(){
GroovyShell shell = new GroovyShell();
Closure c = shell.evaluate("""{->"Hello $y"}""")
c.delegate = this
c.resolveStrategry = Closure.DELEGATE_FIRST
String z = c.call()
println z
}
}
new test().function()
But it was the closest thing I could come up with, and may lead you to something...
If I understand right, you are reading y from somewhere else. So you want to evaluate y as a GString after y and then x have been loaded. groovy.util.Eval will do this for simple cases. In this case, you have just one binding variable: x.
def y = '${x}'
def x = 'world'
def script = "Hello ${y}"
def z = Eval.me('x', x, '"' + script + '".toString()') // create a new GString expression from the string value of "script" and evaluate it to interpolate the value of "x"
println z
With globals you can use _G[name] to access the global variable name if you have a string "name":
function setGlobal(name, val)
_G[name] = val
end
If you have
-- module.lua
local var1
local var2
there is no _L that would allow you to do the equivalent for locals:
function setLocal(name, val)
_L[name] = val -- _L doesn't exist
end
Is there another way that you could access a local variable by string representing its name?
You can use debug.getlocal() and debug.setlocal() in the debug library:
function setLocal(name, val)
local index = 1
while true do
local var_name, var_value = debug.getlocal(2, index)
if not var_name then break end
if var_name == name then
debug.setlocal(2, index, val)
end
index = index + 1
end
end
Test:
local var1
local var2
setLocal("var1", 42)
print(var1)
Output: 42
I strongly recommend not using getLocal, it's a function in the debug library which should never be used in official commercial uses because it affects performance and opens huge vulnerabilities for hackers to exploit! Never depend on debug functions for your logic.
If you really need this, then why not define a dictionary _L, then:
local _L = {}
_L.var1 = ...
_L.var2 = ...
The pattern above is not against the rules of Lua's design.