Tarea 2 - Build Systems
Imagina que trabajas en una empresa de desarrollo de software que desea construir una plataforma para monitorear el clima en distintas ciudades. El equipo ha decidido comenzar con una prueba de concepto enfocada en Santiago, Chile, utilizando la API pública de Open-Meteo.
Como parte del equipo, se te ha asignado la responsabilidad de:
- Diseñar la estructura inicial del proyecto con una buena separación de responsabilidades.
- Implementar dos aplicaciones simples que consulten y muestren el clima actual, una en grados Celsius y otra en Fahrenheit.
- Encapsular la lógica compartida en una biblioteca reutilizable.
- Automatizar parte del flujo de desarrollo mediante tareas y plugins de Gradle que analicen el código en busca de problemas comunes o malas prácticas.
Este ejercicio simula un entorno de trabajo real donde se espera que combines habilidades técnicas (Kotlin, Gradle, modularización) con buenas prácticas de diseño de proyectos mantenibles y extensibles.
Esta tarea está dividida en tres partes:
- P1 es independiente y puede resolverse por separado. Su objetivo es practicar la estructura de proyectos multi-módulo y la reutilización de código entre aplicaciones.
- P2 introduce una tarea personalizada de Gradle para análisis de código.
- P3 extiende la lógica de P2 encapsulándola en un plugin configurable. Por tanto, P3 depende directamente de lo implementado en P2.
Puedes comenzar con P1 o con P2, según te resulte más cómodo. Sin embargo, para completar P3 necesitas tener una solución funcional de P2.
P1 [2 pts] – Proyecto Multi-Módulo con Lógica Compartida
Construirás un proyecto Gradle multi-módulo utilizando Kotlin DSL. El objetivo es reutilizar código entre dos pequeñas aplicaciones que consultan el clima actual en Santiago, Chile, usando la API pública de Open-Meteo.
Requisitos
- Inicializa un proyecto Gradle multi-módulo con Kotlin DSL.
- Crea tres módulos:
lib
: biblioteca compartida que encapsula la lógica común.app-celsius
: aplicación que muestra el clima de Santiago en grados Celsius.app-fahrenheit
: aplicación que muestra el clima de Santiago en grados Fahrenheit.
- Usa el plugin
application
en cada app para facilitar su ejecución. - En
settings.gradle.kts
, registra todos los módulos y define el nombre del proyecto. - Centraliza las versiones y dependencias utilizando un archivo
libs.versions.toml
o un bloque común. - Incluye las dependencias necesarias para cada módulo en su respectivo
build.gradle.kts
. - Separa correctamente la lógica en sus módulos correspondientes, de acuerdo a la lógica de negocios y aplicación.
API sugerida
Consulta el clima actual en Santiago mediante la siguiente URL:
https://api.open-meteo.com/v1/forecast?latitude=-33.45&longitude=-70.66¤t=temperature_2m&temperature_unit=celsius
Recomendación: OkHttp + org.json
OkHttp es una biblioteca moderna para realizar peticiones HTTP. Puedes combinarla con org.json
, una biblioteca liviana que permite analizar respuestas JSON sin anotaciones ni configuración especial.
Eres libre de usar cualquier otra biblioteca si lo prefieres, mientras hagas la configuración apropiada con Gradle.
Ejemplo: consultar un usuario con JSONPlaceholder
import okhttp3.OkHttpClient
import okhttp3.Request
import org.json.JSONObject
val client = OkHttpClient()
fun getUsername(userId: Int): String {
val url = "https://jsonplaceholder.typicode.com/users/$userId"
val request = Request.Builder().url(url).build()
val response = client.newCall(request).execute()
if (!response.isSuccessful) error("Request failed with code: ${response.code}")
val body = response.body?.string() ?: error("Empty response body")
val json = JSONObject(body)
// Extraemos el nombre de usuario del campo "username"
return json.getString("username")
}
- Puedes construir URLs manualmente como
https://.../recurso?id=valor
. - Las respuestas se acceden como objetos
JSONObject
, donde usas:getString("clave")
,getInt("clave")
,getDouble("clave")
según el tipo.getJSONObject("clave")
para acceder a objetos anidados.
- Puedes usar este patrón para adaptarlo fácilmente a otras APIs, como la de Open-Meteo.
Ejemplo de ejecución esperada (app-celsius):
The current temperature in Santiago is 22.1°C
Ejemplo de ejecución esperada (app-fahrenheit):
The current temperature in Santiago is 71.8°F
Cómo ejecutar tus aplicaciones
Para probar cada módulo de aplicación (app-celsius
y app-fahrenheit
), debes configurar el plugin application
en sus respectivos archivos build.gradle.kts
.
Asegúrate de definir la clase principal con:
application {
mainClass.set("tu.paquete.PrincipalKt")
}
Reemplaza
tu.paquete.PrincipalKt
por el nombre completo de la clase que contiene tu funciónfun main
.
Luego, puedes ejecutar la app desde la raíz del proyecto con:
./gradlew :app-celsius:run
./gradlew :app-fahrenheit:run
Esto compilará y ejecutará la aplicación correspondiente.
P2 [2 pts] – Tarea de Gradle
Crea una tarea personalizada que realice un análisis básico del código fuente. Esta tarea debe inspeccionar recursivamente todos los archivos .kt
dentro de un directorio y generar un informe con:
- Comentarios
TODO
o líneas sospechosas (FIXME
,HACK
, etc.), detectadas mediante expresiones regulares configurables. - Cantidad de funciones declaradas por archivo.
Para encontrar coincidencias puedes usar Regex.findAll
:
val regex = todoRegex.get().toRegex()
val lines = file.readLines()
val matches = mutableListOf<Pair<Int, String>>()
for (i in lines.indices) {
val line = lines[i]
if (regex.findAll(line).any()) {
matches.add(i to line)
}
}
Esto te da una lista con número de línea y contenido donde se encontraron coincidencias.
La secuencia que retorna findAll()
se puede convertir a lista con .toList()
o filtrar con .any()
.
Parámetros esperados
todoRegex: String
: expresión regular para detectarTODO
s.suspiciousRegex: String
: expresión regular paraFIXME
,HACK
, etc.sourceDir: File
: directorio raíz a analizar.reportFile: File
: archivo donde se escribirá el informe.
Ejemplo de uso
tasks.register<AnalyzeSourcesTask>("analyzeSources") {
todoRegex.set("TODO")
suspiciousRegex.set("FIXME|HACK")
sourceDir.set(file("src/main/kotlin"))
reportFile.set(file("build/reports/analysis-report.txt"))
}
Ejemplo de salida esperada
File: UserService.kt
- Function count: 5
- TODO found at line 42: // TODO: refactor this logic
File: AuthValidator.kt
- Function count: 2
- Suspicious line at line 17: // FIXME: this is a hack
P3 [2 pts] – Plugin Avanzado
Encapsula la lógica de análisis en un plugin Gradle reutilizable.
Objetivo
Implementar un plugin que:
- Registre automáticamente una tarea de análisis en los proyectos que lo apliquen.
- Exponga una extensión con propiedades configurables:
maxTodoAllowed
: máximo número deTODO
s permitidos.enableRegexCheck
: activa/desactiva los chequeos con expresiones regulares.
- Adapte su comportamiento según la configuración. Si el número de
TODO
s supera el umbral, la tarea debe fallar.
Requisitos
- Implementa la clase
CodeAnalysisPlugin
. - Define una extensión (
CodeAnalysisExtension
) con propiedades configurables. - La tarea debe:
- Emitir un informe con los datos recolectados.
- Lanzar un
GradleException
si el número deTODO
s excede el umbral.
Ejemplo de configuración
codeAnalysis {
maxTodoAllowed.set(5)
enableRegexCheck.set(true)
}
Ejemplo de salida
File: UserService.kt
- Function count: 5
- TODO found at line 42: // TODO: refactor this logic
- TODO found at line 57: // TODO: add logging
File: AuthValidator.kt
- Function count: 2
- Suspicious line at line 17: // FIXME: this is a hack
Summary:
- Total TODOs found: 3
- Max allowed: 2 ❌