Search code examples
groovydslgroovyshell

Use groovy category in groovy shell


I am working under some DSL using Groovy categories and I would like to find a way to use my DSL with groovy shell without explicitly writing use(MyCategory){ myObject.doSomething() } for each command.

For example, suppose I have the following toy category:

class MyCategory {
    static Integer plus(Integer integer, String string){
        return integer + Integer.valueOf(string)
    }
}

Then, I can use this category in groovysh in the following way:

groovy> use(MyCategory){ 2 + '3' } //gives 5

So, is there any way to set up MyCategory globally for all groovysh commands, so it will not be necessary to each time wrap my commands in use(MyCategory) { ... }? For example:

groovy> useGlobally(MyCategory); //call something like this only once
groovy> 2 + '3' //automatically uses MyCategory and gives 5

Solution

  • The idea of the category is to close the scope of metaprogramming. Why not use metaClass in this case?

    groovy:000> class MyCategory {
    groovy:001>     static Integer plus(Integer integer, String string){
    groovy:002>         return integer + Integer.valueOf(string)
    groovy:003>     }
    groovy:004> }
    ===> true
    groovy:000> Integer.metaClass.mixin MyCategory
    ===> null
    groovy:MyCategory@131fa4b> 2 + '4'
    ===> 6
    groovy:MyCategory@131fa4b> 
    

    Update: With a lot of methods, you can iterate through the first parameters of the static methods and mixin them into the respective parameter type class.

    class MyCategory {
        static global() {
            MyCategory.metaClass.methods
                .findAll { it.isStatic() && !it.name.startsWith("__") && it.name != "global" }
                .each { it.nativeParameterTypes[0].mixin MyCategory }
        }
    
        static Integer plus(Integer integer, String string){
            return integer + Integer.valueOf(string)
        }
    
        static String yell(String a, times) {
          a.toUpperCase() * times + "!!!"
        }
    }
    
    
    MyCategory.global()
    
    
    assert "a".yell(3) == "AAA!!!"
    assert 2+'3' == 5