El desarrollo de software está evolucionando cada vez más rápido. En los últimos años, hemos escuchado, con frecuencia, sobre una diversidad de tecnologías y conceptos que son considerados, tarde o temprano, en los proyectos. Éstos, siendo nuevos o existentes, tienen la necesidad de evolucionar y adaptarse a los nuevos cambios que el desarrollo de software trae consigo. Acaso necesitamos escribir código que modifica código?

Código que modifica código?

En un inicio puede sonar algo loco o ridículo: quién escribe código para modificar código si puedo aplicar el cambio directamente? Ahora, imaginemos que queremos aplicar un mismo cambio a diferentes proyectos, digamos que queremos migrar de JUnit4 a JUnit5 (el cual está en camino). Lo primero que se nos viene a la mente es usar el poderosísimo copiar y pegar en nuestros…​ digamos 10 proyectos. Otra opción puede ser crear una guía de migración y que los responsables de esos proyectos se hagan cargo de los cambios manualmente. Sin duda alguna, aplicar este cambio será una tarea dura y tediosa. Una alternativa más confiable no sería escribir una receta con todos los pasos que deseamos hacer y que sea testeable asegurando que mis cambios serán aplicados correctamente?

De ahora en adelante Código que modifica código tiene mucho más sentido. Lo único que hace falta ahora es un lenguaje que nos permita automatizar nuestro desarrollo y es el rol que Rug jugará de ahora en adelante.

Rug es un nuevo lenguaje el cual provee un DSL (Domain Specific Language) y ha sido creado por Atomist, cuyo CEO es nada más y nada menos que Rod Johnson. No te suena? El creador de spring-framework!!!

Rug actualmente provee editors, generators, tests, predicates. En versiones futuras proveerá reviewers y executors. Actualmente, existen diferentes Rug Types y en esta ocasión revisaremos el POM Rug Type. Primero, para poder empezar, necesitamos instalar Rug CLI.

En el siguiente ejemplo, haremos uso de un generator, escribiremos un editor y luego lo aplicaremos sobre un proyecto existente. Para propósitos de este artículo usaremos un proyecto generado por Spring Initializr.

Al ejecutar el siguiente comando dentro del proyecto, se creará un directorio llamado .atomist.

rug edit atomist-rugs:rug-editors:ConvertExistingProjectToRugArchiveWithGenerator generator_name=JUnit5Generator rug_archive_name=junit5-rugs rug_archive_group_id=eddumelendez

La estructura del proyecto será la siguiente:

.
├── .atomist
│   ├── editors
│   │   └── JUnit5Generator.rug
│   ├── manifest.yml
│   └── tests
│       └── JUnit5Generator.rt
├── .atomist.yml
├── .gitignore
├── .mvn
│   └── wrapper
│       ├── maven-wrapper.jar
│       └── maven-wrapper.properties
├── mvnw
├── mvnw.cmd
├── pom.xml
└── src
    ├── main
    │   ├── java
    │   │   └── com
    │   │       └── example
    │   │           └── DemoRugApplication.java
    │   └── resources
    │       ├── application.properties
    │       ├── static
    │       └── templates
    └── test
        └── java
            └── com
                └── example
                    └── DemoRugApplicationTests.java

Para lograr nuestro propósito, listaremos lo que nos hace falta para poder tener nuestro pom.xml con los cambios necesarios para ejecutar JUnit5 en nuestro proyecto:

  1. Agregar properties con las versiones correctas

  2. Excluir las dependencia de junit4

  3. Agregar la dependencia de junit5

  4. Agregar las dependencias correctas para maven-surefire-plugin

Agregar el archivo AddJUnit5MavenConfig.rug en la ruta .atomist/editor/, el cual será nuestro editor principal. Luego, listaremos y crearemos cada uno de los editores que se encargarán de realizar los cambios mencionados anteriormente.

@description "adds junit5 support in maven"
@tag "java"
@tag "junit5"
@tag "maven"
editor AddJUnit5MavenConfig

AddJUnit5Properties
ExcludeJUnit4Dependency
AddJUnit5Dependency
AddSurefirePlugin

@description "adds junit5 maven properties"
editor AddJUnit5Properties

with Pom begin
  do addOrReplaceProperty "junit.jupiter.version" "5.0.0-M3"
  do addOrReplaceProperty "junit.platform.version" "1.0.0-M3"
end

@description "exclude junit4 dependency from spring-boot-starter-test"
editor ExcludeJUnit4Dependency

let junit4ExclusionContent = """
<exclusions>
    <exclusion>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
    </exclusion>
</exclusions>
"""

with Pom
    do addOrReplaceNode "/project/dependencies/dependency/artifactId[text() = 'spring-boot-starter-test']/.." "/project/dependencies/dependency/artifactId[text() = 'spring-boot-starter-test']/exclusions" "exclusions" junit4ExclusionContent

@description "adds junit5 maven dependency"
editor AddJUnit5Dependency

with Pom begin
  do addOrReplaceDependencyOfVersion "org.junit.jupiter" "junit-jupiter-api" "${junit.jupiter.version}"
  #do addOrReplaceDependencyScope "org.junit.jupiter" "junit-jupiter-api" "test"
  do addOrReplaceNode "/project/dependencies/dependency/artifactId[text() = 'junit-jupiter-api']/.." "/project/dependencies/dependency/artifactId[text() = 'junit-jupiter-api']/scope" "scope" "<scope>test</scope>"
end

@description "adds maven-surefire-plugin with junit5 dependencies"
editor AddSurefirePlugin

let surefirePluginBuildContent = """<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.19.1</version>
    <dependencies>
        <dependency>
            <groupId>org.junit.platform</groupId>
            <artifactId>junit-platform-surefire-provider</artifactId>
            <version>${junit.platform.version}</version>
        </dependency>
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter-engine</artifactId>
            <version>${junit.jupiter.version}</version>
        </dependency>
    </dependencies>
</plugin>"""

with Pom
  do addOrReplaceBuildPlugin "org.apache.maven.plugins" "maven-surefire-plugin" surefirePluginBuildContent

Antes de finalizar y aplicar los cambios de nuestro editor, le pediremos a rug que nos diga un poco más de AddJUnit5MavenConfig.

rug describe editor eddumelendez:junit5-rugs:AddJUnit5MavenConfig -l

En el terminal se muestra la información de nuestro editor y cómo ejecutarlo.

AddJUnit5MavenConfig
eddumelendez:junit5-rugs:0.1.0
adds junit5 support in maven

→ Tags
  java (java)
  junit5 (junit5)
  maven (maven)
→ Parameters
  no parameters needed

To invoke the AddJUnit5MavenConfig editor, run:
  rug edit "eddumelendez:junit5-rugs:AddJUnit5MavenConfig" -a 0.1.0 -l

Para finalizar, ejecutaremos el editor.

rug edit eddumelendez:junit5-rugs:AddJUnit5MavenConfig -a 0.1.0 -l

El resultado de la operación se muestra a continuación.

→ Project
  ~/Documents/Git/demo-rug/ (35 kb in 9 files)

→ Changes
  ├── pom.xml updated 1 kb
  ├── pom.xml updated 1 kb
  ├── pom.xml updated 1 kb
  ├── pom.xml updated 2 kb
  └── .atomist.yml created 2 kb

Como podemos visualizar, el archivo pom.xml ha sido modificado 4 veces dado los 4 editores que se crearon internamente en AddJUnit5MavenConfig. Del mismo modo, el archivo .atomist.yml fue modificado, guardando un histórico de la operación ejecutada.


Eddú Meléndez

Java Software Engineer, Open Source Contributor