At the moment I have the following code:
class SampleFixture {
static aFixtureWithCodeAA() {
fixtureAA()
}
static aFixtureWithCodeBB() {
fixtureBB()
}
static aFixtureWithCodeCC() {
fixtureCC()
}
}
I'd like to transform this into something like
class SampleFixture {
static aFixture(code) {
fixture[code]()
}
}
I have another class where fixtureAA, fixtureBB and fixtureCC are defined. So the code values are predefined. I want the method fixture[code] to be build at run-time rather than having a method for every single fixture.
How can I do this?
EDIT: I've been reading this http://groovy-lang.org/metaprogramming.html#_dynamic_method_names and it looks like what I want to do but I can't seem to get it to work.
Just to clarify: after reading this article, what I would like to end up with is a method with a baseName+varSufix as in "fixture${code}"(). Ideally I would end up with something like:
class SampleFixture {
static aFixture(code) {
MyClass."fixture{code}"()
}
}
So I'd have a different method name depending on the code I am passing.
Do you just mean:
class MyClass {
static fixtureAA() { "oooh, aa" }
static fixtureBB() { "cool, bb" }
static fixtureCC() { "wow, cc" }
}
class MyFixture {
def call(code) {
MyClass."fixture$code"()
}
}
println new MyFixture().call('BB')
(you were so close)
Or, you could do something like:
class MyClass {
static fixtureAA() { "oooh, aa" }
static fixtureBB() { "cool, bb" }
static fixtureCC() { "wow, cc" }
}
class MyFixture {
def methodMissing(String name, args) {
try {
MyClass."fixture$name"()
}
catch(e) {
"No idea how to $name"
}
}
}
assert "oooh, aa" == new MyFixture().AA()
assert "cool, bb" == new MyFixture().BB()
assert "wow, cc" == new MyFixture().CC()
assert "No idea how to DD" == new MyFixture().DD()
You can implement a method called invokeMethod(String method, args) and parse the code from the method argument:
class SampleFixture {
def fixture = [
AA: { "code aa" },
BB: { "code bb" },
CC: { "code cc" },
]
def invokeMethod(String method, args) {
def code = method - "aFixtureWithCode"
fixture[code]()
}
}
f = new SampleFixture()
assert f.aFixtureWithCodeAA() == "code aa"
assert f.aFixtureWithCodeBB() == "code bb"
assert f.aFixtureWithCodeCC() == "code cc"
UPDATE: here's a solution using a second class to redirect the method call to another class
class Fixture {
def fixtureAA() { "code aa" }
def fixtureBB() { "code bb" }
def fixtureCC() { "code cc" }
}
class SampleFixture {}
SampleFixture.metaClass.static.invokeMethod = { String method, args ->
def code = method - "aFixtureWithCode"
new Fixture()."fixture${code}"()
}
assert SampleFixture.aFixtureWithCodeAA() == "code aa"
assert SampleFixture.aFixtureWithCodeBB() == "code bb"
assert SampleFixture.aFixtureWithCodeCC() == "code cc"
Related
My test code below:
#!/usr/bin/env groovy
class Inner {
def methodMissing(String name, args) {
println "inner:${name}"
}
}
def foo = {
bar()
}
foo.delegate = new Inner()
foo.call()
It gives result:
inner:bar
If I add another methodMissing method, like this:
#!/usr/bin/env groovy
class Inner {
def methodMissing(String name, args) {
println "inner:${name}"
}
}
// This method is added
def methodMissing(String name, args) {
println "outer:${name}"
}
def foo = {
bar()
}
foo.delegate = new Inner()
foo.call()
It gives result:
outer:bar
Why? I set the delegate to the closure 'foo'. Why isn't Inner. methodMissing called?
From this doc, I believe you'll want to change the resolution strategy. I have added a foo2 and some asserts to illustrate the contrast in behaviour:
class Inner {
def methodMissing(String name, args) {
println "inner:${name}"
return "inner:${name}"
}
}
def methodMissing(String name, args) {
println "outer:${name}"
return "outer:${name}"
}
def foo = { bar() }
foo.delegate = new Inner()
assert "outer:bar" == foo.call()
def foo2 = { bar() }
foo2.delegate = new Inner()
foo2.resolveStrategy = Closure.DELEGATE_FIRST
assert "inner:bar" == foo2.call()
Why do I have a compilation error for this code:
class SomeList {
final String field
SomeList(String field) {
this.field = field
}
static SomeList "Regular name of method"() {
return new SomeList("Regular name of method")
}
static SomeList "Name with.dot"() {
return new SomeList("Name with.dot")
}
}
class SomeListTests {
#Test
def "some list test"() {
//given
SomeList list = SomeList()
//when
list."Regular name of method"()
//then
//compilation error
}
}
Error message:
Error:Groovyc: While compiling tests of example-project_test: BUG! exception in phase 'semantic analysis' in source unit '/home/alex/Projects/example-project/src/test/groovy/SomeListTests.groovy' Problem loading class SomeList
I did not find any restrictions in the documentation for method names as String.
When i create main method with GroovyShell and try to start script with this method it compiles.
class Main {
public static void main(String[] args) {
GroovyShell shell = new GroovyShell()
shell.run(new File("path/to/Script.groovy"), Collections.emptyList())
println "Everything is cool"
}
}
And this is Script.groovy:
SomeList."Regular name of method"()
SomeList."Name with.dot"()
I create Test.groovy file as Groovy Script file in IDEA:
import org.junit.Test
class SomeList {
final String field
SomeList(String field) {
this.field = field
}
static SomeList "Regular name of method"() {
return new SomeList("Regular name of method")
}
static SomeList "Name with.dot"() {
return new SomeList("Name with.dot")
}
}
class SomeListTests {
#Test
void "some list test"() {
//given
SomeList list = new SomeList()
//when
println list."Regular name of method"()
//then
println list."Name with.dot"()
}
}
And it works fine for me. I'm using Groovy 2.4.15.
The output is:
SomeList#cb51256
SomeList#59906517
I know there are similar questions but the answers are not satisfying.
I get an Groovy ambiguous method overload error when calling a method with null as parameter.
e.g.:
class A{
sampleMethod (B bObj){
if(bObj == null) {
handleNullArgumentGracefully()
}
... do some cool stuff ...
}
sampleMethod (C cObj){
... do some other cool stuff ...
}
}
now when i call sampleMethod(null) groovy does not know which method it should invoke. Thats clear but is there a possibility to set one method of these two as default method to handle such null calls? I want to handle this on the callee side and not on the caller side (i do not want to cast something on the caller side)
UPDATE:
i found a solution how it could work but i donĀ“t know why: convert the non default method to a closure property
class app {
static void main(String[] args) {
def a = new A()
a.sampleMethod(new B())
a.sampleMethod(new C())
a.sampleMethod(null)
}
}
class A {
def sampleMethod(B bObj = null) {
if (bObj == null) {
println("handle null")
}
println("1")
}
def sampleMethod = { C cObj ->
println("2")
}
}
class B {
}
class C {
}
the following will fail with Ambiguous method overloading for method A#sampleMethod
class A{
def sampleMethod (Number o=null){
println "num $o"
}
def sampleMethod (String o){
println "str $o"
}
}
new A().sampleMethod(null)
this one will work (Object will be called for null):
class A{
def sampleMethod (Number o=null){
println "num $o"
}
def sampleMethod (String o){
println "str $o"
}
def sampleMethod(Object o){
println "obj $o"
}
}
new A().sampleMethod(null)
but i like this one:
class A{
def _sampleMethod (Number o){
println "num $o"
}
def _sampleMethod (String o){
println "str $o"
}
def sampleMethod(Object o){
if(o==null){
println "null"
return null
}else if(o instanceof Number){
return _sampleMethod ((Number) o)
}else if(o instanceof String){
return _sampleMethod ((String) o)
}
throw new IllegalArgumentException("wrong argument type: ${o.getClass()}")
}
}
new A().sampleMethod(null)
I would like to apply a meta-programming transformation to some of my classes, let's say by adding printXxx methods, like this:
class Person {
String name
}
def p = new Person()
p.printName() // does something
I have a rough idea how this can be done once I have a metaclass:
Person.metaClass.methodMissing = { name, args ->
delegate.metaClass."$name" = { println delegate."${getPropName(name)}" }
delegate."$name"(*args)
}
Now how do I turn this code into a reusable "library"? I would like to do something like:
#HasMagicPrinterMethod
class Person {
String name
}
or
class Person {
String name
static {
addMagicPrinters()
}
}
Define the behaviour you want to add as a trait
trait MagicPrinter {
void printProperties() {
this.properties.each { key, val ->
println "$key = $val"
}
}
}
Then add this trait to a class
class Person implements MagicPrinter {
String name
}
Now use it!
new Person(name: 'bob').printProperties()
You can go for a mixin approach:
class AutoPrint {
static def methodMissing(obj, String method, args) {
if (method.startsWith("print")) {
def property = (method - "print").with {
it[0].toLowerCase() + it[1..-1]
}
"${obj.getClass().simpleName} ${obj[property]}"
}
else {
throw new NoSuchMethodException()
}
}
}
You can mix it with a static block:
class Person {
static { Person.metaClass.mixin AutoPrint }
String name
}
def p = new Person(name: "john doe")
try {
p.captain()
assert false, "should've failed"
} catch (e) {
assert true
}
assert p.printName() == "Person john doe"
Or with expandoMetaClass:
class Car {
String model
}
Car.metaClass.mixin AutoPrint
assert new Car(model: "GT").printModel() == "Car GT"
Because 7 months later is the new 'now' :-)
Why method A is not compiled while methods B and C are ok?
class A {
methodA() {
}
void methodB() {
}
static methodC() {
}
}
You need to use the "def" modifier in groovy if you are not declaring a type for the method:
class A {
def methodA() {
}
}
should work.