Groovy closure without preceding equal sign prop = {} vs prop {} - groovy

I'm trying to create a config file for my java app that can have lazy evaluation, for instance, contain an e-mail template with current date time. I use ConfigSlurper to parse the config file.
For some reason I can't find at https://groovy-lang.org/closures.html the description of the commonly used form (see build.gradle): prop1 { ... } prop2 { ... }. Am I looking at the wrong place?
My tests show that such closures are evaluated immediately and the example above can be rewritten as: prop1 = { ... }() prop2 = { ... }() (notice the parentheses, without them the evaluation is lazy).
I also don't understand why using dots in property without the equal sign cause MissingMethodException: No signature of method: groovy.util.ConfigObject.URL() if I use: server.URL { System.out.println('ccc'); 'asd' } in the example below.
It's disappointing that the choice is completely transparent for Groovy, but you have to check that a value is instance of Closure or GString when using java.
I used this article as the starting point https://blog.mrhaki.com/2009/08/grassroots-groovy-configuration-with.html
// We can group settings.
// The following settings are created:
// app.version, app.setting1, app.setting2, app.setting3, app.date, app.active
app {
System.out.println('aaa')
version = "1.0"
// We can write code to set up settings.
[1, 2, 3].each {
this."setting${it}" = it * 10
}
// We can use Java objects
date = new Date()
active = true
}
server.URL = "http://default"
// Environment specific settings override settings with the
// same name.
environments {
development {
server = { System.out.println('ccc'); 'asd' }
// server.URL = "${-> System.out.println('bbb');new Date()}http://localhost"
}
test {
server.URL = 'http://test:9080'
}
integrationtest {
server.URL = 'http://integrationtest/url'
}
production {
server.URL = 'http://prod/url'
}
}

My tests show that such closures are evaluated immediately and the
example above can be rewritten as: prop1 = { ... }()
Closures are not evaluated immediately. The parens above are causing the closure to be evaluated.
For some reason I can't find at https://groovy-lang.org/closures.html
the description of the commonly used form (see build.gradle): prop1 {
... } prop2 { ... }.
prop1 {} is invoking a method named prop1 and passing a closure as a parameter That code is equivalent to this:
def myClosure = {}
prop1(myClosure)
I also don't understand why using dots in property without the equal
sign cause MissingMethodException: No signature of method:
groovy.util.ConfigObject.URL()
Something like server.URL = 'http://test:9080' is assigning a value (http://test:9080) to a property (server.URL). If you remove the equal sign you are changing the expression fundamentally and then would be invoking a method named URL on an object named server and passing http://test:9080 as a parameter.

Related

How to update a module.exports properly?

I have this file that stores some of my environment variables.
Let's call it generalEnv.js
module.exports = {
CONSTANT_1: process.env.CONSTANT_1,
CONSTANT_2: process.env.CONSTANT_2
};
When the app initializes, I don't put the value of process.env.CONSTANT_1 in the env variables yet because I have to look into some places first if it exists(mongodb for instance). If it does not exists on mongodb, I will add a value into process.env.CONSTANT_1 and I was expecting that the value will reflect on generalEnv now.
When I tried accessing the CONSTANT_1 in another file.
Let's call it getConstantOne.js
const { CONSTANT_1 } = require('./generalEnv');
module.exports = () => {
// I was expecting that CONSTANT_1 will have a value here now
if(!CONSTANT_1) {
// do something
}
return CONSTANT_1
}
it does not reflect.. how do I update the closure of generalEnv.js for process.env.CONSTANT_1 to reflect on CONSTANT_1?
When assigning to a variable (or a value in an object/element in an array), the assignment will replace the value, not modify it. Therefore, any "copies" of that value will not be affected, and remain the same. Consider this example:
let a = 0;
let b = a;
a = 1;
What happens to b? Answer: Its value is 0.
To work around this we need some way of modifying the value instead of replacing it. Unfortunately, "primitive types" (strings/numbers/booleans etc.) cannot be modified in javascript. There are types that can be modified however, such as objects. You could solve this by wrapping your variables in an object called "env".
let env: {
CONSTANT_1: process.env.CONSTANT_1,
CONSTANT_2: process.env.CONSTANT_2
}
modules.exports = { env }
and then to modify:
env.CONSTANT_1 = "new value"
and to access:
if (!env.CONSTANT_1) { ... }

Using find{ } on a map where the whole map is evaluated not each element

I created some mixin methods. Code and example below:
URL.metaClass.withCreds = { u, p ->
delegate.openConnection().tap {
setRequestProperty('Authorization', "Basic ${(u + ':' + p).bytes.encodeBase64()}")
}
}
URLConnection.metaClass.fetchJson = {
delegate.setRequestProperty('Accept', 'application/json')
delegate.connect()
def code = delegate.responseCode
def result = new JsonSlurper().parse(code >= 400 ? delegate.errorStream : delegate.inputStream as InputStream)
[
ok : code in (200..299),
body: result,
code: code
]
}
example usage:
new URL("$baseUrl/projects/$name").withCreds(u, p).fetchJson().find {
it.ok
}?.tap{
it.repos = getRepos(it.key).collectEntries { [(it.slug): it] }
}
}
When I dont use find(), my object is, as expected, a map with those 3 elements. When I use find it is a Map.Entry with key ok and value true
which produces this error:
groovy.lang.MissingPropertyException: No such property: ok for class: java.util.LinkedHashMap$Entry
Possible solutions: key
It occured to me when I wrote this post that it was treated the map as an iterable and thus looking at every entry which I have subsequently verified. How do I find on the whole map? I want it.ok because if it's true, I need to carry it forward
There is no such method in Groovy SDK. Map.find() runs over an entry set of the map you call method on. Based on expectation you have defined I'm guessing you are looking for a function that tests map with a given predicate and returns the map if it matches the predicate. You may add a function that does to through Map.metaClass (since you already add methods to URL and URLConnection classes). Consider following example:
Map.metaClass.continueIf = { Closure<Boolean> predicate ->
predicate(delegate) ? delegate : null
}
def map = [
ok : true,
body: '{"message": "ok"}',
code: 200
]
map.continueIf { it.ok }?.tap {
it.repos = "something"
}
println map
In this example we introduced a new method Map.continueIf(predicate) that tests if map matches given predicate and returns a null otherwise. Running above example produces following output:
[ok:true, body:{"message": "ok"}, code:200, repos:something]
If predicate is not met, map does not get modified.
Alternatively, for more strict design, you could make fetchJson() method returning an object with corresponding onSuccess() and onError() methods so you can express more clearly that you add repos when you get a successful response and optionally you create an error response otherwise.
I hope it helps.

Initialize a Blockly Mutator within JavaScript

Hi,
as far as I know, custom blocks in Blockly can be defined wether in JSON or in JavaScript, but how can a mutator be initialized in JavaScript?
with JSON:
Blockly.defineBlocksWithJSONArray([
{....
"mutator": "myMutatorName"
});
Then the Mutator_MIXIN must be defined and with Blockly.Extension.registerMutator('myMutatorName', Blockly.myMutator_MIXIN, null, null) the mutator is added to the Block.
with JavaScript:
Blockly.Blocks['blockName'] = {
init: function() = {
....
??? this.setMutator(???)???
};
}
So how can this be done in JavaScript?
Kind regards
a new one
I might be just a little bit late here, but I'll leave the answer anyway for those who need a bit more concrete example.
In JavaScript, you don't actually need to bind a mutator to your block, you just need to define mutationToDom() and domToMutation(xmlElement) functions, like so:
Blockly.Blocks['my_custom_block'] = {
init() {
// Define your basic block stuff here
},
// Mutator functions
mutationToDom() {
let container = document.createElement('mutation');
// Bind some values to container e.g. container.setAttribute('foo', 3.14);
return container;
},
domToMutation(xmlElement) {
// Retrieve all attributes from 'xmlElement' and reshape your block
// e.g. let foo = xmlElement.getAttribute('foo');
// this.reshape(foo);
},
// Aux functions
reshape(param){
// Reshape your block...
}
}
Blockly will automagically take care of the rest and allow you to treat your block as dynamic one.
And if you need to used Mutator Editor UI, you must define decompose(workspace) and compose(containerBlock) functions and call this.setMutator(...) to set which blocks are used in the Mutator Editor UI, like so:
Blockly.Blocks['my_custom_block'] = {
init() {
// Define your basic block stuff here
// Set all block that will be used in Mutator Editor UI, in this
// case only 'my_block_A' and
this.setMutator(new Blockly.Mutator(['my_block_A', 'my_block_B']));
},
// Mutator functions
mutationToDom() {
// Same as previous example
},
domToMutation(xmlElement) {
// Same as previous example
},
decompose(workspace) {
// Decomposeyour block here
},
compose(containerBlock) {
// Compose your block here
},
// Aux functions
reshape(param){
// Same as previous example
}
}
Hope that these short examples help someone :)
You have to declare how the xml is loaded to dom, and how it is saved to xml and redrawn. Also notice how it attaches a mutator to a block element in case that is the only part you need to reference a mutator already present.
init: initFunction (Like you have declared.)
mutationToDom: MutationToDom,
domToMutation: DomToMutation,
updateShape_: UpdateShape`
If all you require is to create a reference to a mutator then what you need is an element of this kind, which we will programatically create in a bit:
<mutation mutator_name="true"></mutation>
The following snippet is an example of the extra functions mutationToDom, DomtoMutation UpdateShape which attaches extra input conditionally. I have a block with a checkbox that when enabled, adds an extra input.
function MutationToDom() {
var container = document.createElement('mutation');
var continueOnError = (this.getFieldValue('HasCONTINUE') == 'TRUE');
container.setAttribute('continueOnError', continueOnError);
return container;
}
function DomToMutation(xmlElement) {
var continueOnError = (xmlElement.getAttribute('continueOnError') == 'true');
this.updateShape_(continueOnError);
}
function UpdateShape(continueOnError) {
// Add or remove a Value Input.
if (continueOnError) {
this.appendValueInput("CONTINUE_ON_ERROR")
.setCheck('CONTINUE_ON_ERROR');
} else {
if (this.childBlocks_.length > 0) {
for (var i = 0; i < this.childBlocks_.length; i++) {
if (this.childBlocks_[i].type == 'continue_on_error') {
this.childBlocks_[i].unplug();
break;
}
}
}
this.removeInput('CONTINUE_ON_ERROR');
}
}

Object.defineProperty on any property [duplicate]

I am aware of how to create getters and setters for properties whose names one already knows, by doing something like this:
// A trivial example:
function MyObject(val){
this.count = 0;
this.value = val;
}
MyObject.prototype = {
get value(){
return this.count < 2 ? "Go away" : this._value;
},
set value(val){
this._value = val + (++this.count);
}
};
var a = new MyObject('foo');
alert(a.value); // --> "Go away"
a.value = 'bar';
alert(a.value); // --> "bar2"
Now, my question is, is it possible to define sort of catch-all getters and setters like these? I.e., create getters and setters for any property name which isn't already defined.
The concept is possible in PHP using the __get() and __set() magic methods (see the PHP documentation for information on these), so I'm really asking is there a JavaScript equivalent to these?
Needless to say, I'd ideally like a solution that is cross-browser compatible.
This changed as of the ES2015 (aka "ES6") specification: JavaScript now has proxies. Proxies let you create objects that are true proxies for (facades on) other objects. Here's a simple example that turns any property values that are strings to all caps on retrieval, and returns "missing" instead of undefined for a property that doesn't exist:
"use strict";
if (typeof Proxy == "undefined") {
throw new Error("This browser doesn't support Proxy");
}
let original = {
example: "value",
};
let proxy = new Proxy(original, {
get(target, name, receiver) {
if (Reflect.has(target, name)) {
let rv = Reflect.get(target, name, receiver);
if (typeof rv === "string") {
rv = rv.toUpperCase();
}
return rv;
}
return "missing";
}
});
console.log(`original.example = ${original.example}`); // "original.example = value"
console.log(`proxy.example = ${proxy.example}`); // "proxy.example = VALUE"
console.log(`proxy.unknown = ${proxy.unknown}`); // "proxy.unknown = missing"
original.example = "updated";
console.log(`original.example = ${original.example}`); // "original.example = updated"
console.log(`proxy.example = ${proxy.example}`); // "proxy.example = UPDATED"
Operations you don't override have their default behavior. In the above, all we override is get, but there's a whole list of operations you can hook into.
In the get handler function's arguments list:
target is the object being proxied (original, in our case).
name is (of course) the name of the property being retrieved, which is usually a string but could also be a Symbol.
receiver is the object that should be used as this in the getter function if the property is an accessor rather than a data property. In the normal case this is the proxy or something that inherits from it, but it can be anything since the trap may be triggered by Reflect.get.
This lets you create an object with the catch-all getter and setter feature you want:
"use strict";
if (typeof Proxy == "undefined") {
throw new Error("This browser doesn't support Proxy");
}
let obj = new Proxy({}, {
get(target, name, receiver) {
if (!Reflect.has(target, name)) {
console.log("Getting non-existent property '" + name + "'");
return undefined;
}
return Reflect.get(target, name, receiver);
},
set(target, name, value, receiver) {
if (!Reflect.has(target, name)) {
console.log(`Setting non-existent property '${name}', initial value: ${value}`);
}
return Reflect.set(target, name, value, receiver);
}
});
console.log(`[before] obj.example = ${obj.example}`);
obj.example = "value";
console.log(`[after] obj.example = ${obj.example}`);
The output of the above is:
Getting non-existent property 'example'
[before] obj.example = undefined
Setting non-existent property 'example', initial value: value
[after] obj.example = value
Note how we get the "non-existent" message when we try to retrieve example when it doesn't yet exist, and again when we create it, but not after that.
Answer from 2011 (obsoleted by the above, still relevant to environments limited to ES5 features like Internet Explorer):
No, JavaScript doesn't have a catch-all property feature. The accessor syntax you're using is covered in Section 11.1.5 of the spec, and doesn't offer any wildcard or something like that.
You could, of course, implement a function to do it, but I'm guessing you probably don't want to use f = obj.prop("example"); rather than f = obj.example; and obj.prop("example", value); rather than obj.example = value; (which would be necessary for the function to handle unknown properties).
FWIW, the getter function (I didn't bother with setter logic) would look something like this:
MyObject.prototype.prop = function(propName) {
if (propName in this) {
// This object or its prototype already has this property,
// return the existing value.
return this[propName];
}
// ...Catch-all, deal with undefined property here...
};
But again, I can't imagine you'd really want to do that, because of how it changes how you use the object.
Preface:
T.J. Crowder's answer mentions a Proxy, which will be needed for a catch-all getter/setter for properties which don't exist, as the OP was asking for. Depending on what behavior is actually wanted with dynamic getters/setters, a Proxy may not actually be necessary though; or, potentially, you may want to use a combination of a Proxy with what I'll show you below.
(P.S. I have experimented with Proxy thoroughly in Firefox on Linux recently and have found it to be very capable, but also somewhat confusing/difficult to work with and get right. More importantly, I have also found it to be quite slow (at least in relation to how optimized JavaScript tends to be nowadays) - I'm talking in the realm of deca-multiples slower.)
To implement dynamically created getters and setters specifically, you can use Object.defineProperty() or Object.defineProperties(). This is also quite fast.
The gist is that you can define a getter and/or setter on an object like so:
let obj = {};
let val = 0;
Object.defineProperty(obj, 'prop', { //<- This object is called a "property descriptor".
//Alternatively, use: `get() {}`
get: function() {
return val;
},
//Alternatively, use: `set(newValue) {}`
set: function(newValue) {
val = newValue;
}
});
//Calls the getter function.
console.log(obj.prop);
let copy = obj.prop;
//Etc.
//Calls the setter function.
obj.prop = 10;
++obj.prop;
//Etc.
Several things to note here:
You cannot use the value property in the property descriptor (not shown above) simultaneously with get and/or set; from the docs:
Property descriptors present in objects come in two main flavors: data descriptors and accessor descriptors. A data descriptor is a property that has a value, which may or may not be writable. An accessor descriptor is a property described by a getter-setter pair of functions. A descriptor must be one of these two flavors; it cannot be both.
Thus, you'll note that I created a val property outside of the Object.defineProperty() call/property descriptor. This is standard behavior.
As per the error here, don't set writable to true in the property descriptor if you use get or set.
You might want to consider setting configurable and enumerable, however, depending on what you're after; from the docs:
configurable
true if and only if the type of this property descriptor may be changed and if the property may be deleted from the corresponding object.
Defaults to false.
enumerable
true if and only if this property shows up during enumeration of the properties on the corresponding object.
Defaults to false.
On this note, these may also be of interest:
Object.getOwnPropertyNames(obj): gets all properties of an object, even non-enumerable ones (AFAIK this is the only way to do so!).
Object.getOwnPropertyDescriptor(obj, prop): gets the property descriptor of an object, the object that was passed to Object.defineProperty() above.
obj.propertyIsEnumerable(prop);: for an individual property on a specific object instance, call this function on the object instance to determine whether the specific property is enumerable or not.
The following could be an original approach to this problem:
var obj = {
emptyValue: null,
get: function(prop){
if(typeof this[prop] == "undefined")
return this.emptyValue;
else
return this[prop];
},
set: function(prop,value){
this[prop] = value;
}
}
In order to use it the properties should be passed as strings.
So here is an example of how it works:
//To set a property
obj.set('myProperty','myValue');
//To get a property
var myVar = obj.get('myProperty');
Edit:
An improved, more object-oriented approach based on what I proposed is the following:
function MyObject() {
var emptyValue = null;
var obj = {};
this.get = function(prop){
return (typeof obj[prop] == "undefined") ? emptyValue : obj[prop];
};
this.set = function(prop,value){
obj[prop] = value;
};
}
var newObj = new MyObject();
newObj.set('myProperty','MyValue');
alert(newObj.get('myProperty'));
You can see it working here.
I was looking for something and I figured out on my own.
/*
This function takes an object and converts to a proxy object.
It also takes care of proxying nested objectsa and array.
*/
let getProxy = (original) => {
return new Proxy(original, {
get(target, name, receiver) {
let rv = Reflect.get(target, name, receiver);
return rv;
},
set(target, name, value, receiver) {
// Proxies new objects
if(typeof value === "object"){
value = getProxy(value);
}
return Reflect.set(target, name, value, receiver);
}
})
}
let first = {};
let proxy = getProxy(first);
/*
Here are the tests
*/
proxy.name={} // object
proxy.name.first={} // nested object
proxy.name.first.names=[] // nested array
proxy.name.first.names[0]={first:"vetri"} // nested array with an object
/*
Here are the serialised values
*/
console.log(JSON.stringify(first)) // {"name":{"first":{"names":[{"first":"vetri"}]}}}
console.log(JSON.stringify(proxy)) // {"name":{"first":{"names":[{"first":"vetri"}]}}}
var x={}
var propName = 'value'
var get = Function("return this['" + propName + "']")
var set = Function("newValue", "this['" + propName + "'] = newValue")
var handler = { 'get': get, 'set': set, enumerable: true, configurable: true }
Object.defineProperty(x, propName, handler)
this works for me

Groovy: evaluate a property with a variable in it

A bit new to groovy, I am trying to match a variable string to a property pulled from a file using ConfigSlurper. I have the slurper part working fine, but can't seem to figure out the right way to evaluate a property with a variable in it. I think I was getting warm when I found evaluating-code-dynamically-in-groovy but I am not entirely sure.
//properties.groovy
jobs {
foo {
email="foo#email.com"
}
}
//myscript.groovy
def config = new ConfigSlurper().parse(new File('properties.groovy').toURI().toURL())
List jobs = (ArrayList) BazAPI.getArtifacts(bucket) // list of objects, foo is one
ListIterator jobIterator = jobs.listIterator();
while (jobIterator.hasNext()) {
Object j = jobIterator.next();
job_name = "${j.name}" //
email = config.jobs."${job_name}".email /* NEED TO FIGURE OUT HOW TO EVAL */
foo_email = config.jobs.foo.email //evaluates to the correct property in properties.groovy
//these values get fed to a DSL but to illustrate
println "${job_name}" // prints foo
println "${email}" // prints [:]
println "${foo_email}" // prints foo#email.com
}
Have you tried
config.jobs[ j.name ].email

Resources