Paquetes y módulos

Esta es la décima entrega de el tour de Ceylon. Si en la parte anterior sobre los tipos genéricos te sentiste un poco abrumado, no te preocupes; esta parte cubrirá algo de material que deberá ser mas fácil llevar acabo. Vamos a poner atención en tema muy distinto. Aprenderemos acerca de paquetes y módulos.

Paquetes e importaciones

No existe una declaración packages en los archivos fuente de Ceylon. El compilador determina el paquete y el módulo en el que un programa permanecerá mediante la ubicación de archivo fuente en que es declarado. Una clase llamada Hello en el paquete org.jboss.hello deberá ser definida en el archivo org/jboss/hello/Hello.Ceylon.

Cuando un archivo fuente en un paquete refiere a elemento de un programa toplevel en otro paquete, deberá explícitamente importar el elemento del programa. Ceylon, a diferencia de Java, no soporta el uso de nombres calificativos dentro del código fuente. Así es que no podemos escribir org.jboss.hello.Hello en Ceylon.

La sintaxis de la declaración import es un poco diferente a la de Java. Para importar un elemento de un programa, escribimos:

import com.redhat.polar.core { Polar }

Para importar varios elementos de un programa de un paquete, escribimos:

import com.redhat.polar.core { Polar, pi }

Para importar todos los elementos de un programa toplevel en un paquete, escribimos:

import com.redhat.polar.core { ... }

Para resolver un conflicto de nombres, podemos renombrar una declaración importada:

import com.redhat.polar.core { PolarCoord=Polar }

Pensamos que renombrar es mucho mas limpio que usar nombres calificativos, podemos incluso renombrar miembros de un tipo:

import com.redhat.polar.core { Polar { r=radius, theta=angle } }

Módulos

El soporte integrado para modularidad es uno de los mayores logros del proyecto Ceylon, pero ¿Qué significa modularidad? Hay varias definiciones para esto:

  • Soporte para una unidad de visibilidad que es mas grande que un paquete, pero mas pequeño que todos los paquetes.
  • Un formato descriptor de un módulo que expresa dependencias entre versiones especificas de módulos.
  • Un integrado formato de módulo y un repositorio de módulos disponible que es entendido por todas las herramientas escritas para el lenguaje, desde el compilador hasta el IDE.
  • Un runtime que cuenta con un cargador de clases (un cargador por módulo) y la habilidad para manejar múltiples versiones de el mismo módulo.
  • Un ecosistema repositorios de módulos remotos donde desarrolladores pueden compartir sus códigos con otros desarrolladores.

Nivel de visibilidad de un módulo

Un paquete en Ceylon puede ser compartido o no compartido. Un módulo no compartido (Por defecto) es visible únicamente a el módulo que contiene el paquete. Podemos crear un paquete compartido proveiendo un descriptor del paquete.

"The typesafe query API."
shared package org.hibernate.query;

Un paquete shared define parte de la API public de el módulo. Así otros módulos pueden acceder a las declaraciones compartidas en un paquete shared.

En tiempo de ejecucuón el paquete es representado por un atributo Package top level llamado package.

Descriptor de módulo

Un módulo deberá especificar explícitamente los módulos de los que depende. Esto es logrado a través de un descriptor de módulo.

"The best-ever ORM solution"
license "http://www.gnu.org/licenses/lgpl.html"
module org.hibernate '3.0.0.beta' {
    shared import ceylon.language '1.0.1';
    import java.sql '4.0';
}

En tiempo de ejecución un módulo es representado por un atributo Module top level llamado module.

Archivos módulo y repositorio de módulos

Un paquete de archivos compilan a archivos .class, los descriptores de paquetes y módulos en un estilo Java a un archivo jar con la extensión .car. El compilador de Ceylon usualmente no produce archivos .class individuales en un directorio. En vez de ello, produce archivos módulo.

Archivos módulo se encuentran en un repositorio de módulos. Un repositorio de módulos es una estructura de directorio bien definida con un bien definido lugar para cada módulo. Un repositorio de módulos puede ser local(en un sistema de archivos) o remoto (en internet). Dada una lista de repositorios de módulos, el compilador de Ceylon puede localizar las dependencias mencionada en el descriptor del módulo cuando se compila. Y cuando termina la compilación del módulo, el coloca al archivo del módulo resultante en un lugar correcto en el repositorio local. La arquitectura también incluye soporte para directorios fuente, archivos de código, directorios de documentación del módulo.

Desarrollando módulos en la IDE de Ceylon

Para empezar con los módulos en la ide de Ceylon, ve a Help > Cheat Sheets..., abre el objeto Ceylon y corre el cheat sheet Introduction to Ceylon Modules, que te guiara paso a paso a través del proceso de crear un módulo, definiendo sus dependencias y exportando su repositorio de módulos.

Ejemplo: Compilando a un repositorio local o remoto

Supongamos que estas escribiendo net.example.foo. Tu directorio de proyecto puede lucir como este:

README
source/
    net/
        example/
            foo/
                Foo.ceylon
                FooService.ceylon
                module.ceylon
documentation/
    manual.html

Aquí el código fuente esta en un directorio llamado source (que es utilizado por defecto y nos salva de tener que pasarle la opción --src a ceylon compile). Desde el directorio del proyecto(el directorio que contiene el directorio source) tu puedes compilar usando el comando:

$ ceylon compile net.example.foo

Este comando deberá compilar los archivos de código fuente (Foo.ceylon y FooService.ceylon) en archivo módulo y publicarlos en el directorio de repositorios por defecto de salida, modules (puedes usar la opción --our build para publicarlos a build). Ahora tu directorio de proyecto lucirá como esto:

README
source/
    net/
        example/
            foo/
                Foo.ceylon
                FooService.ceylon
                module.ceylon
modules/
    net/
        example/
            foo/
                1.0/
                    net.example.foo-1.0.car
                    net.example.foo-1.0.car.sha1
                    net.example.foo-1.0.src
                    net.example.foo-1.0.src.sha1
documentation/
    manual.html

El archivo .src es el archivo de código que puede ser usado por herramientas tales como el IDE, para búsqueda de código. El archivo sha1 contiene la suma de verificación del archivo sin la extensión sha1 y puede ser usado para detectar archivos corruptos.

Puedes generar la documentación del API usando ceylon doc` como en el siguiente ejemplo:

$ ceylon doc net.example.foo

Esto deberá crear un

modules/net/example/foo/1.0/module-doc

directorio que contiene la documentación.

Ahora vamos a suponer que tu proyecto obtiene una dependencia en com.example.bar versión 3.1.4. Tienes que declarar el módulo y la versión de la que depende en tu descriptor module.ceylon , no necesitaras decirle a ceylon compile en que repositorio buscar para encontrar la dependencia.

Una posibilidad es que tu tengas un repositorio local llamado com.example.bar/3.1.4 si este se encuentra en tu repositorio por defecto (~/.ceylon/repo) entonces no necesitas hacer nada, el mismo comando hará el trabajo:

$ ceylon compile net.example.foo

Alternativamente, si tienes otro repositorio local puede especificarlo usando la opción --rep.

El Ceylon Herd en un repositorio de módulos online que contiene módulos de Ceylon open source. Como suele pasar, el Herd es uno de los repositorios por defecto que conoce ceylon compile. Asi si com.example.bar/3.1.4/ esta en Herd entonces el comando a compilar deberá de permanecer agradablemente corto.

$ ceylon compile net.example.foo

(Esto es correcto, igual que como vimos anteriormente). Por cierto, puedes deshabilitar los repositorios por defecto con la opción --d si así lo deseas.

Si com.example.bar/3.1.4 se encuentra en otro repositorio, por decir algo en http://repo.example.com, entonces el comando deberá ser:

$ ceylon compile --rep          \
        http://repo.example.com \
        net.example.foo

Puedes especificar múltiples opciones --rep como sea necesario si tu tienes dependencias en múltiples repositorios.

Cuando estés listo para publicar el módulo en algún lugar, para que cualquier otra persona pueda usarlo. Vamos a decir que lo quieres publicar en http://ceylon.example.net/repo. Solo tienes que compilarlo otra vez, pero esta vez especificando la opción -out.

$ ceylon compile                               \
        --rep http://repo.example.com          \
        --out http://ceylon.example.net/repo   \
        net.example.foo

Es de valor señalar que tomando ventaja de las acertadas cosas como el directorio de código fuente y el directorio de salida, como el que hemos hecho aquí, te salvan de teclear mucho.

Ejecutando un módulo

La ejecución de un módulo de Ceylon es basado en módulos JBoss, una tecnología que también existe el core de JBoss AS 7. Dada una lista de repositorios de módulos, la ejecución automáticamente localiza un archivo módulo y sus dependencias versionada en los repositorios, incluso descargando archivos desde repositorios remotos si es necesario.

Normalmente , la ejecución de Ceylon es invocada especificando el nombre de un módulo ejecutable en la linea de comandos.

Ejemplo: Corriendo un repositorio local o remoto

Vamos a continuar el ejemplo que teníamos donde net.example.foo versión 1.0 fue publicado to http://ceylon.example.net/repo. Ahora, supongamos que quieres correr el módulo(posiblemente desde otra computadora).

Si la dependencias (com.example.bar/3.1.4) pueden ser encontrada en los repositorios por defecto el comando para ceylon run es:

$ ceylon run                              \
    --rep http://ceylon.example.net/repo  \
    net.example.foo/1.0

Puedes pasar opciones también (que son disponibles al programa a través del objeto top level process):

$ ceylon run                              \
    --rep http://ceylon.example.net/repo  \
    net.example.foo/1.0                   \
    my options

Si una de las dependencias no esta disponible desde los repositorios, deberás de especificar un repositorio que las contenga usando otro rep:

$ ceylon run                             \
    --rep http://ceylon.example.net/repo \
    --rep http://repo.example.com        \
    net.example.foo/1.0                  \
    my options

El caso mas simple, es donde el módulo y sus dependencias están todos en uno (o mas) de los repositorios por defecto (tales como Herd o ~/.ceylon.repo).

$ ceylon run net.example.foo/1.0

Ecosistema del repositorio de módulos

Una de las ventajas mas agradables de esta arquitectura es que es posible corren un módulo “directamente desde internet”, solo tecleando, por ejemplo:

$ ceylon run --rep http://jboss.org/ceylon/modules org.jboss.ceylon.demo/1.0

Y todas las dependencias requeridas son automáticamente descargada como se necesiten.

Ceylon Herd es una es una comunidad de repositorios de módulos donde cualquiera puede contribuir reusando módulos. Por supuesto, el formato de repositorio de módulos es un standard abierto, así cualquier organización puede mantener su repositorio de módulos publico.

Aun hay mas

En la siguiente estación veremos el soporte de Ceylon para funciones de orden superior.