Translate

miércoles, 5 de abril de 2023

Analizando el Syntax Trees con LINQ


La idea principal sobre  Syntax Trees es que dada una cadena que contiene código C#, el compilador crea una representación de árbol (llamada Árbol de sintaxis) de la cadena. El poder de Roslyn es que nos permite consultar este árbol de sintaxis con LINQ.

Aquí hay una muestra en la que usamos Roslyn para crear un árbol de sintaxis a partir de una cadena. Debemos agregar referencias a Microsoft.CodeAnalysis y Microsoft.CodeAnalysis.CSharp. 


using Microsoft.CodeAnalysis.CSharp;

using Microsoft.CodeAnalysis.CSharp.Syntax;


var tree = CSharpSyntaxTree.ParseText(@"

    public class MyClass

    {

        public void MyMethod()

        {

        }

    }");


var syntaxRoot = tree.GetRoot();

var MyClass = syntaxRoot.DescendantNodes().OfType<ClassDeclarationSyntax>().First();

var MyMethod = syntaxRoot.DescendantNodes().OfType<MethodDeclarationSyntax>().First();


Console.WriteLine(MyClass.Identifier.ToString());

Console.WriteLine(MyMethod.Identifier.ToString());


Primero comenzamos analizando una cadena que contiene código C# y obteniendo la raíz de este árbol de sintaxis. Desde este punto, es extremadamente fácil recuperar elementos que nos gustaría usar LINQ. Dada la raíz del árbol, miramos todos los objetos descendientes y los filtramos por su tipo. Si bien solo hemos usado ClassDeclarationSyntax y MethodDeclarationSyntax, hay piezas correspondientes de sintaxis para cualquier característica de C#.

Intellisense de Visual Studio es extremadamente valioso para explorar los diversos tipos de sintaxis de C# que podemos usar.

Podemos componer expresiones LINQ más avanzadas como cabría esperar:


var tree = CSharpSyntaxTree.ParseText(@"

    public class MyClass

    {

        public void MyMethod()

        {

        }

        public void MyMethod(int n)

        {

        }

    }");


var syntaxRoot = tree.GetRoot();

var MyMethod = syntaxRoot.DescendantNodes().OfType<MethodDeclarationSyntax>()

    .Where(n => n.ParameterList.Parameters.Any()).First();


//Find the type that contains this method

var containingType = MyMethod.Ancestors().OfType<TypeDeclarationSyntax>().First();


Console.WriteLine(containingType.Identifier.ToString());

Console.WriteLine(MyMethod.ToString());


Arriba, comenzamos buscando todos los métodos y luego filtramos por aquellos que aceptan parámetros. Luego tomamos este método y avanzamos hacia arriba a través del árbol con el método Ancestors(), buscando el primer tipo que contiene este método.

Existen algunas limitaciones en el tipo de información que puede descubrir a un nivel puramente sintáctico y para superarlas debemos hacer uso del modelo semántico de Roslyn.

martes, 4 de abril de 2023

Roslyn, Compiler-as-a-Service parte 2

Los principales tipos de Syntax Nodes se derivan de la clase base SyntaxNode. El conjunto de Syntax Nodes no es extensible. Estas clases forman la construcción para crear declaraciones, sentencias, cláusulas y expresiones. No son terminales, lo que significa que tienen subnodos por los que se puede navegar a través de propiedades o métodos escritos.


/* tree is given after parsing the Hello World example with the

Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText method */

var syntaxRoot = tree.GetRoot();

var Demo = syntaxRoot.DescendantNodes().OfType<ClassDeclarationSyntax>().First();


Los Syntax Tokens son terminales y no pueden ser padres de otros nodos o tipos de tokens. Representan identificadores, palabras clave, literales y puntuaciones. Ofrece propiedades para acceder a los valores analizados desde el código fuente de entrada.

Syntax Trivia representan porciones de texto insignificantes, que pueden aparecer como espacios en blanco, comentarios y directivas de preprocesador en cualquier posición dentro de la entrada de origen. No forman parte de la sintaxis del lenguaje normal y no se agregarán como elementos secundarios de los nodos de sintaxis. Tampoco tienen un nodo principal. La clase base se llama SyntaxTrivia.

Los nodos, tokens y trivias contienen Spans para el posicionamiento y la cantidad de ocurrencias de caracteres para asociar la ubicación correcta desde la fuente de entrada. Esto se puede utilizar para la depuración o la determinación de información de errores.

Los Kinds o tipos se utilizan como propiedades para nodos, tokens y trivias para distinguir los tipos correspondientes y proporcionar las conversiones correctas. La clase SyntaxKind se puede representar como un tipo de enumeración en el lenguaje de destino C# o VB.

Al analizar el código fuente, pueden ocurrir errores, que pueden ubicarse y marcarse como incorrectos o incompletos, por ejemplo, falta un token o no es válido. El analizador puede omitir tokens no válidos y continuar buscando el siguiente token válido para continuar con el análisis. Los tokens omitidos se adjuntarán como Trivia Node of Kind SkippedToken.

Roslyn proporciona una rica API para crear unidades de compilación AST por código. Utiliza el patrón de fábrica y proporciona muchas clases y métodos estáticos o enumeraciones, que se pueden usar a través del encadenamiento para construir estas soluciones. El encadenamiento es un diseño de llamada de método fluido utilizado en lenguajes de programación orientados a objetos, donde cada método devuelve un objeto, lo que permite llamar a la siguiente declaración sin requerir variables que almacenen los resultados intermedios. El siguiente código cortado muestra un ejemplo de cómo usar la API de Roslyn para hacer una declaración de clase simple.

var tree = SyntaxFactory.CompilationUnit().AddMembers(

    SyntaxFactory.NamespaceDeclaration(

        SyntaxFactory.IdentifierName("Example")).AddMembers(

            SyntaxFactory.ClassDeclaration("Demo").AddMembers(

                SyntaxFactory.MethodDeclaration(

                    SyntaxFactory.PredefinedType(

                        SyntaxFactory.Token(SyntaxKind.VoidKeyword)), "Main")

                    .AddModifiers(SyntaxFactory.Token(SyntaxKind.StaticKeyword))

                    .AddModifiers(SyntaxFactory.Token(SyntaxKind.PublicKeyword))

                    .WithBody(SyntaxFactory.Block()).AddBodyStatements(

                         SyntaxFactory.ReturnStatement())

           )

     )

);

Aunque la representación del árbol sintáctico del código fuente de entrada ofrece muchas funciones, solo analiza las estructuras léxica y sintáctica. Para cubrir completamente todos los aspectos de los lenguajes de programación también es necesario definir su semántica, que marca su comportamiento. Las designaciones de variables locales y miembro pueden superponerse entre sí y diferir entre sus alcances y las reglas de acción deben integrarse dentro de esos cierres. Para ello, Roslyn utiliza símbolos. Un símbolo representa un elemento, que lleva los metadatos recibidos de la fuente de entrada. Se puede acceder a estos a través de la tabla de símbolos proporcionada, también representada en una estructura de árbol, comenzando desde el elemento raíz, que es el espacio de nombres global. Los símbolos se derivan de la interfaz ISymbol, mientras que el compilador proporciona las propiedades y los métodos. Los símbolos ofrecen espacios de nombres, tipos y miembros entre el código fuente y los metadatos y sus conceptos de lenguaje son similares a la API de Reflection utilizada por el sistema de tipos CLR. También es importante saber que el acceso al árbol del modelo semántico desencadena una compilación, lo que significa que es más costoso en comparación con el acceso al árbol sintáctico.

Cada símbolo contiene información sobre

  • la ubicación de la declaración (en el fuente o en los metadatos)
  • en qué espacio de nombres o tipo existe este símbolo
  • la información si el símbolo es abstracto, estático, sellado, etc.

El siguiente fragmento de código muestra cómo agregar información semántica al ejemplo de Hello World declarado anteriormente.


/* tree is given after parsing the Hello World example with the Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText method */ 

var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly. Location); 

var compilation = CSharpCompilation.Create("DemoCompilation", syntaxTrees: new[] { tree }, references: new[] { mscorlib }); 

var model = compilation.GetSemanticModel(tree)


Roslyn es una rica plataforma de servicio de compilación que permite un acceso profundo al marco .NET de Microsoft y sus subconjuntos de lenguajes admitidos. Aunque Roslyn ofrece una variedad de posibilidades de bajo nivel para construir AST, se abstrae muy bien de estas capas y proporciona una API limpia e intuitiva. 


Inscripciones Abiertas a Cursos Gugler 2023

 

PRENSA GUGLER  LAB 2023

  NOTICIAS ACTUALES

informacion  OFERTA ACADÉMICA


 Se encuentran abiertas las inscripciones para el primer cuatrimestre del año 2023, para todas las capacitaciones dictadas por el Laboratorio de Investigación Gugler. Podés asegurar tu lugar en el curso y comisión que desees !!!.

Las clases inician:

Martes 18/04 , Miércoles 19/04, Jueves 20/04 o Sábado 22/04, según el curso que elegiste

 

Inscribirteclic aquí

Cursos, Horarios y comisionesclic aquí.

 

 

Dictamos nuestros cursos en la Facultad de Ciencia y Tecnología, perteneciente a la Universidad Autónoma de Entre Ríos. En nuestro portafolio de capacitación encontrarás:

MODALIDAD PRESENCIAL

  • Cursos de Programación:
    Programación en Python.
    Programación web frontend.
     
  • Cursos de Mantenimiento/Reparación:
    Reparación y Mantenimiento de PC.

 MODALIDAD DISTANCIA

  • Cursos de Programación:
    Programación en PHP.
    Programación en Java.
informacion   MÁS INFORMACIÓN 

informacion LABORATORIO DE INVESTIGACIÓN GUGLER

Si deseas comunicarte con nosotros, te recordamos que podes hacerlo a través de los siguientes portales, en los cuales encontrarás información sobre Gugler.

TEL: (0343) - 4975066 Interno 119

Sitio Oficial:  www.gugler.com.ar

Campus:  campusvirtual.gugler.com.ar

Sistema de Gestión Cursos:  sgc.gugler.com.ar

Sistema de Gestión Cursos Móvil: Aquí 

Sistema de Documentación:  sgd.gugler.com.ar

Sistema de Validación:  giua.gugler.com.ar

                   Twitter                Facebook

Laboratorio Gugler

partir del 2012, la Facultad de Ciencia y Tecnología nos declaro:  "Laboratorio de Investigación".

 

El laboratorio ha ampliado las incumbencias de Gugler, ya que además de la capacitación, la promoción y difusión del software libre, ahora abarcará actividades como publicaciones y proyectos de investigación, así como también el dictado y participación en conferencias o exposiciones de ámbitos académicos y científicos.

 

Ante cualquier duda comunicarse con nosotros a gugler_contacto@uader.edu.ar

GUGLER PRESS

 


Roslyn, Compiler-as-a-Service

 


Roslyn expone el análisis del código del compilador para desarrolladores de C# y Visual Basic a través de tres capas principales de API. 

La capa Compiler Pipeline maneja el análisis de nivel inferior, el análisis de símbolos y el procesamiento de metadatos, incluidos los enlaces y la emisión de IL. La capa Compiler API ofrece acceso a la representación AST (abstract syntax tree) de los datos procesados y funciona como una abstracción de nivel superior para los desarrolladores de C# o Visual Basic. La capa Language Service proporciona un conjunto de herramientas de nivel superior para operar en los datos de la API del Compilador, incluidas las operaciones de navegación, la información y las posibilidades de formato.

Compiler Pipeline: Compiler Pipeline procesa la fuente de entrada en diferentes fases, comenzando con el proceso de análisis, donde la fuente se tokeniza en las reglas de lenguaje gramatical correspondientes. Posteriormente, los metadatos se analizan para extraer los símbolos requeridos, también llamada fase de declaración. El Binder hace coincidir los símbolos con los identificadores correspondientes y, al final, el Emisor construye un conjunto completo.

Compiler API: en equivalencia con las fases descritas anteriormente de Compiler Pipeline, Compiler API ofrece un modelo de objeto representativo en el lenguaje de destino actual para acceder a la información necesaria. La representación de la fase Parser es traducida por la Syntax Tree API como modelo AST, seguida de modelos de metadatos para los símbolos y enlaces, y la Emit API se abstrae del productor de código de bytes IL correspondiente.

Language Service: El Servicio Lingüístico se adapta a las capas y fases subyacentes. Por ejemplo, utiliza la tabla de símbolos para el Examinador de objetos o las funciones de navegación, y la representación del árbol de sintaxis para las funciones de esquematización y formato.

Roslyn está construido como un conjunto de APIs de dos capas, la capa de API del compilador y la capa de API del espacio de trabajo. La capa del compilador contiene los modelos de objetos que consisten en la información extraída sobre las fases individuales y los datos sintácticos y semánticos analizados desde el fuente. Ofrece una instantánea inmutable de una única invocación de un compilador. Esta capa es independiente de cualquier componente de Visual Studio. Esto permite que las herramientas de diagnóstico definidas por el usuario se conecten al proceso de compilación real para obtener información sobre errores y advertencias. La API del espacio de trabajo resuelve las dependencias del proyecto y utiliza la capa del compilador para proporcionar modelos de objetos para soluciones de análisis y refactorización.

La API del árbol de sintaxis es el componente central del marco de Roslyn. Vincula todas las funciones proporcionadas a las clases de modelos de objetos abstractos, conocidos como AST. El análisis de código, la refactorización, los componentes IDE, la información de origen, las construcciones gramaticales y muchas más funciones convergen en un punto, que es la API del árbol sintáctico. También es importante saber que todos los resultados analizados se pueden revertir a su texto de origen original (completamente de ida y vuelta). Además, el árbol de sintaxis es seguro para subprocesos e inmutable, lo que permite que múltiples usuarios interactúen simultáneamente sin interferir. Esto también significa que no se pueden enviar modificaciones en los árboles de sintaxis. Los métodos de fábricación proporcionan instantáneas adicionales del árbol de sintaxis para operar indirectamente con ellos, a costa de poca sobrecarga de memoria. Para ilustrar los principales componentes y características de la API del árbol sintáctico, el siguiente programa Hello World escrito en C# servirá como una ilustración práctica.


using System;

    namespace Demo {

        class Program {

            static void Main() {

                Console.WriteLine("Hello World!");

            }

        }

}

Roslyn también brinda posibilidades para visualizar secciones de código existentes como un árbol de sintaxis utilizando el SDK de la plataforma del compilador .NET, que está disponible como un paquete NuGet para Visual Studio. Después de instalar el paquete adicional, Visual Studio agrega un nuevo elemento de menú en la pestaña Ver/Otras ventanas/Visualizador de sintaxis. El visualizador de sintaxis crea el siguiente árbol de sintaxis a partir del código mostrado anteriormente, como se muestra en la figura 


También es posible ver una representación visual del árbol de sintaxis como se muestra en la figura. 


La leyenda en el lado derecho proporciona una descripción general de los elementos del árbol de sintaxis resultantes de un procedimiento de análisis.

Cada árbol de sintaxis de Roslyn consta de los siguientes elementos:

Nodos de sintaxis para declaraciones, sentencias, cláusulas y expresiones 

Tokens de sintaxis para representar terminales, los fragmentos de código más pequeños 

Sintaxis Trivia para espacios en blanco, comentarios y directivas de preprocesador, que son los elementos de código insignificantes que se pueden omitir después del análisis

Spanns para el posicionamiento del texto y la cantidad de caracteres utilizados por un nodo, token o trivia 

Tipos para identificar el elemento de sintaxis exacto correspondiente, que se puede convertir en enumeraciones específicas del lenguaje

Los errores de sintaxis, que no se ajustan a la gramática y se pueden adjuntar elementos de diagnóstico adicionales para el desarrollo o la depuración


lunes, 3 de abril de 2023

Primeros pasos con Phoenix parte 6


Hay un par de cosas interesantes para notar sobre el post anterior. No necesitábamos detener y reiniciar el servidor mientras realizábamos los cambios. ¡Sí, Phoenix tiene recarga de código caliente! Además, aunque nuestro archivo index.html.heex consta de una sola etiqueta de sección, la página que obtenemos es un documento HTML completo. La plantilla index en realidad se representa una pagina compuesta por el diseño globlal de lib/hello_web/components/layouts/root.html.heex y lib/hello_web/components/layouts/app.html.heex. Si abrimos este archivo, verá una línea que se ve así en la parte inferior:

<%= @inner_content %>

Que es donde se inyecta el html particular. 

Plug es una biblioteca y una especificación para unir aplicaciones web. Es una parte esencial de cómo Phoenix maneja las solicitudes. Cada conector define una parte del procesamiento de solicitudes. En el endpoint encontrarás un esqueleto más o menos así:


defmodule HelloWeb.Endpoint do

  use Phoenix.Endpoint, otp_app: :hello


  plug Plug.Static, ...

  plug Plug.RequestId

  plug Plug.Telemetry, ...

  plug Plug.Parsers, ...

  plug Plug.MethodOverride

  plug Plug.Head

  plug Plug.Session, ...

  plug HelloWeb.Router

end


Cada uno de estos plug tiene una responsabilidad específica. El último complemento es precisamente el módulo HelloWeb.Router. Esto permite que el endpoint delegue todo el procesamiento de solicitudes adicional al enrutador. Como sabemos ahora, su responsabilidad principal es mapear pares de verbo/ruta a controladores. Luego, el controlador le dice a una vista que represente una plantilla.

En este momento, puede estar pensando que pueden ser muchos pasos para simplemente renderizar una página. Sin embargo, a medida que nuestra aplicación crezca en complejidad, veremos que cada capa tiene un propósito distinto:

Endpoint (Phoenix.Endpoint): contiene la ruta común e inicial por la que pasan todas las solicitudes. Si desea que suceda algo en todas las solicitudes, se dirige al punto final.

Router (Phoenix.Router): es responsable de enviar verbos/ruta a los controladores. El enrutador también nos permite medir la funcionalidad. Por ejemplo, algunas páginas de su aplicación pueden requerir la autenticación del usuario, otras no.

Controlador (Phoenix.Controller): el trabajo del controlador es recuperar la información solicitada, hablar con su dominio comercial y preparar datos para la capa de presentación.

Vista: la vista maneja los datos estructurados del controlador y los convierte en una presentación para mostrar a los usuarios. Las vistas a menudo reciben el nombre del formato de contenido que representan.

Hagamos un resumen rápido y cómo los últimos tres componentes funcionan juntos agregando otra página.


viernes, 31 de marzo de 2023

El blog de educalix esta lleno de libros gratuitos!!!


Les quiero recomendar el blog de educalix, dado que esta repletos de libros muy buenos. 

Educalix es una empresa que brinda cursos y además tiene un blog en el cual cuelga un conjunto muy completo de libros. Es pueden encontrar libros de python, seguridad, php, arduino, etc... 

Dejo link: https://blog.educalix.com/

jueves, 30 de marzo de 2023

Primeros pasos con Phoenix parte 5


Cuando su navegador accede a http://localhost:4000/, envía una solicitud HTTP a cualquier servicio que se esté ejecutando en esa dirección, en este caso, nuestra aplicación Phoenix. La solicitud HTTP se compone de un verbo y una ruta. Por ejemplo, las siguientes solicitudes del navegador se traducen en:

BROWSER ADDRESS             VERBO PATH

http://localhost:4000/                 GET /

http://localhost:4000/hello         GET /hello

http://localhost:4000/hello/world GET /hello/world


Hay otros verbos HTTP. Por ejemplo, enviar un formulario generalmente usa el verbo POST.

Las aplicaciones web generalmente manejan las solicitudes asignando cada par de verbo/ruta a una parte específica de su código y esto lo hace el router. Por ejemplo, podemos asignar "/artículos" a una parte de nuestra aplicación que muestra todos los artículos. Por lo tanto, para agregar esto, nuestra primera tarea es agregar una nueva ruta.

El router asigna pares únicos de verbo/ruta HTTP a pares de controlador/acción que los manejarán. Los controladores en Phoenix son simplemente módulos Elixir. Las acciones son funciones que se definen dentro de estos controladores.

Phoenix genera un archivo de router para nosotros en lib/hello_web/router.ex. 

La ruta de nuestro "¡Bienvenido a Phoenix!"  tiene este aspecto.


    get "/", PageController, :home


Si ponemos  en el browser http://localhost:4000/ estamos llamando a / con el metodo GET por lo tanto será manejada por la función home en el módulo HelloWeb.PageController definido en lib/hello_web/controllers/page_controller.ex.

La página que vamos a construir dirá "¡Hola mundo, desde Phoenix!" cuando apuntamos nuestro navegador a http://localhost:4000/hello.

Lo primero que debemos hacer es crear la ruta de la página para una nueva página. Abramos lib/hello_web/router.ex en un editor de texto. 

Agreguemos una nueva ruta al enrutador que asigna una solicitud GET para /hello a la acción de índice de un HelloWeb.HelloController que se creará próximamente dentro del bloque do scope "/" del enrutador:


scope "/", HelloWeb do
  pipe_through :browser

  get "/", PageController, :home
  get "/hello", HelloController, :index
end


Los controladores son módulos de Elixir y las acciones son funciones de Elixir definidas en ellos. El propósito de las acciones es recopilar los datos y realizar las tareas necesarias para la representación. Nuestra ruta especifica que necesitamos un módulo HelloWeb.HelloController con una función index/2.

Para hacer que suceda la acción de índice, creemos un nuevo archivo lib/hello_web/controllers/hello_controller.ex y hagamos que tenga el siguiente aspecto:

defmodule HelloWorldWeb.HelloController do
  use HelloWorldWeb, :controller

  def index(conn, _params) do
    render(conn, :index)
  end
end


Todas las acciones del controlador toman dos argumentos. El primero es conn, una estructura que contiene una gran cantidad de datos sobre la solicitud. El segundo es params, que son los parámetros de solicitud. .

El núcleo de esta acción es render(conn, :index). Le dice a Phoenix que renderice la plantilla de índice. Los módulos responsables de renderizar se denominan vistas. De forma predeterminada, las vistas de Phoenix llevan el nombre del controlador (HelloController) y el formato (HTML en este caso), por lo que Phoenix espera que exista un HelloWeb.HelloHTML y defina una función index/1.

Las vistas de Phoenix actúan como capa de presentación. Por ejemplo, esperamos que el resultado del índice de representación sea una página HTML completa. Para hacernos la vida más fácil, a menudo usamos plantillas para crear esas páginas HTML.

Vamos a crear una nueva vista. Cree lib/hello_web/controllers/hello_html.ex y haga que se vea así:

defmodule HelloWorldWeb.HelloHTML do
  use HelloWorldWeb, :html
end

Para agregar plantillas a esta vista, podemos definirlas como componentes de función en el módulo o en archivos separados.

Comencemos definiendo un componente de función:

defmodule HelloWorldWeb.HelloHTML do
  use HelloWorldWeb, :html

  def index(assigns) do
    ~H"""
    Hello!
    """
  end
end

Definimos una función que recibe asignaciones como argumentos y usamos el sigilo ~H para poner los contenidos que queremos representar. Dentro del sigilo ~H, usamos un lenguaje de plantillas llamado HEEx, que significa "HTML+EEx". EEx es una biblioteca para incrustar Elixir que se distribuye como parte del propio Elixir. "HTML+EEx" es una extensión Phoenix de EEx compatible con HTML, con soporte para validación HTML. Este último lo protege de vulnerabilidades de seguridad como Cross-Site-Scripting sin trabajo adicional de su parte.

Un archivo de plantilla funciona de la misma manera. Los componentes de función son excelentes para plantillas más pequeñas y los archivos separados son una buena opción cuando tiene mucho marcado o sus funciones comienzan a parecer inmanejables.

Probémoslo definiendo una plantilla en su propio archivo. Primero elimine nuestra función def index(assigns) de arriba y reemplácela con una declaración embed_templates:

defmodule HelloWorldWeb.HelloHTML do
  use HelloWorldWeb, :html

  embed_templates "hello_html/*"
end


Aquí le estamos diciendo a Phoenix.Component que incruste todas las plantillas .heex que se encuentran en el directorio hermano hello_html en nuestro módulo como definiciones de funciones.

A continuación, debemos agregar archivos al directorio lib/hello_web/controllers/hello_html.

Tenga en cuenta que el nombre del controlador (HelloController), el nombre de la vista (HelloHTML) y el directorio de la plantilla (hello_html) siguen la misma convención de nomenclatura y se nombran uno tras otro. También se colocan juntos en el árbol de directorios:


lib/hello_web
├── controllers
│   ├── hello_controller.ex
│   ├── hello_html.ex
│   ├── hello_html
 |         ├── index.html.heex

Un archivo de plantilla tiene la siguiente estructura: NOMBRE.FORMATO.TEMPLATING_LANGUAGE. En nuestro caso, creemos un archivo index.html.heex en lib/hello_web/controllers/hello_html/index.html.heex:

<section>
  <h2>Hello World, from Phoenix!</h2>
</section>

Los archivos de plantilla se compilan en el módulo como componentes de función en sí mismos, no hay diferencia de tiempo de ejecución o rendimiento entre los dos estilos.

Ahora que tenemos la ruta, el controlador, la vista y la plantilla, deberíamos poder dirigir nuestros navegadores a http://localhost:4000/hello.

Para reiniciar el server hacemos : 

mix phx.server

sábado, 25 de marzo de 2023

viernes, 24 de marzo de 2023

Primeros pasos con Phoenix parte 4


El directorio lib/hello_web contiene las partes relacionadas con la web de nuestra aplicación. Se ve así cuando se expande:

lib/hello_web

├── controllers

│   ├── page_controller.ex

│   ├── page_html.ex

│   ├── error_html.ex

│   ├── error_json.ex

│   └── page_html

│       └── home.html.heex

├── components

│   ├── core_components.ex

│   ├── layouts.ex

│   └── layouts

│       ├── app.html.heex

│       └── root.html.heex

├── endpoint.ex

├── gettext.ex

├── router.ex

└── telemetry.ex


Todos los archivos que se encuentran actualmente en los directorios de controladores y componentes están ahí para crear el mensaje "¡Bienvenido a Phoenix!" página que vimos en http://localhost:4000/

Al observar los directorios de controladores y componentes, podemos ver que Phoenix proporciona funciones para manejar diseños y HTML y páginas de error listas para usar.

Además de los directorios mencionados, lib/hello_web tiene cuatro archivos en su raíz. lib/hello_web/endpoint.ex es el punto de entrada para las solicitudes HTTP. Una vez que el navegador accede a http://localhost:4000, el punto final comienza a procesar los datos, lo que finalmente conduce al enrutador, que se define en lib/hello_web/router.ex. El enrutador define las reglas para enviar solicitudes a los "controladores", que llama a un módulo de vista para devolver las páginas HTML a los clientes. 

A través de Telemetry, Phoenix puede recopilar métricas y enviar eventos de monitoreo de su aplicación. El archivo lib/hello_web/telemetry.ex define al supervisor responsable de administrar los procesos de telemetría. 

Finalmente, hay un archivo lib/hello_web/gettext.ex que proporciona internacionalización a través de Gettext. Si no le preocupa la internacionalización, puede omitir con seguridad este archivo y su contenido.

El directorio de assert contiene archivos de origen relacionados con los activos front-end, como JavaScript y CSS. Desde Phoenix v1.6, usamos esbuild para compilar activos, que es administrado por el paquete esbuild Elixir. La integración con esbuild está integrada en su aplicación. La configuración relevante se puede encontrar en su archivo config/config.exs.

Sus otros activos estáticos se colocan en la carpeta priv/static, donde se guarda priv/static/assets para los activos generados. Todo en priv/static es atendido por el complemento Plug.Static configurado en lib/hello_web/endpoint.ex. Cuando se ejecuta en modo dev (MIX_ENV=dev), Phoenix observa cualquier cambio que realice en el directorio de activos y luego se encarga de actualizar su aplicación fronend en su navegador mientras trabaja.

Tenga en cuenta que cuando crea su aplicación Phoenix por primera vez usando mix phx.new, es posible especificar opciones que afectarán la presencia y el diseño del directorio assert. De hecho, las aplicaciones de Phoenix pueden traer sus propias herramientas de front-end o no tener un front-end en absoluto (útil si está escribiendo una API, por ejemplo). 

Si la integración predeterminada de esbuild no cubre sus necesidades, por ejemplo, porque desea utilizar otra herramienta de compilación, puede cambiar a una compilación de activos personalizados.

En cuanto a CSS, Phoenix incluye Tailwind CSS Framework, que proporciona una configuración básica para los proyectos. Puede pasar a cualquier marco CSS de su elección. 

sábado, 18 de marzo de 2023

Primeros pasos con Phoenix parte 3

Seguimos con phoenix. Cuando usamos mix phx.new para generar una nueva aplicación Phoenix, crea una estructura de directorio como esta:

├── _build

├── assets

├── config

├── deps

├── lib

│   ├── hello

│   ├── hello.ex

│   ├── hello_web

│   └── hello_web.ex

├── priv

└── test

Repasaremos esos directorios uno por uno:

  • _build: un directorio creado por la herramienta de línea de comando mix que se incluye como parte de Elixir y que contiene todos los artefactos de compilación. Como hemos visto en "Up and Running", mix es la interfaz principal de su aplicación. Usamos Mix para compilar nuestro código, crear bases de datos, ejecutar nuestro servidor y más. Este directorio no debe registrarse en el control de versiones y puede eliminarse en cualquier momento. Eliminarlo obligará a Mix a reconstruir su aplicación desde cero.
  • assets: un directorio que guarda el código fuente de sus activos front-end, normalmente JavaScript y CSS. La herramienta esbuild agrupa automáticamente estas fuentes. Los archivos estáticos como imágenes y fuentes van en priv/static.
  • config: un directorio que contiene la configuración del proyecto. El archivo config/config.exs es el punto de entrada para la configuración. Al final de config/config.exs, importa la configuración específica del entorno, que se puede encontrar en config/dev.exs, config/test.exs y config/prod.exs. Finalmente, se ejecuta config/runtime.exs.
  • deps - un directorio con todas nuestras dependencias Mix. Puede encontrar todas las dependencias enumeradas en el archivo mix.exs, dentro de la definición de la función defp deps do. Este directorio no debe registrarse en el control de versiones y puede eliminarse en cualquier momento. Eliminarlo obligará a Mix a descargar todas las versiones desde cero.
  • lib: un directorio que contiene el código fuente de su aplicación. Este directorio se divide en dos subdirectorios, lib/hello y lib/hello_web. El directorio lib/hello será responsable de alojar toda su lógica de negocio y dominio y por lo general, interactúa directamente con la base de datos. Este es el "Modelo" en la arquitectura Model-View-Controller (MVC). lib/hello_web se encarga de exponer el dominio de tu negocio al mundo, en este caso, a través de una aplicación web. Contiene tanto la vista como el controlador de MVC. 
  • priv: un directorio que guarda todos los recursos que son necesarios en producción pero que no forman parte directamente de su código fuente. Por lo general, guarda scripts de bases de datos, archivos de traducción, imágenes, etc. Los activos generados, creados a partir de archivos en el directorio de activos, se colocan en priv/static/assets de forma predeterminada.
  • test - un directorio con todas nuestras pruebas de aplicaciones. A menudo refleja la misma estructura que se encuentra en lib.


El directorio lib/hello alberga todo el dominio de su negocio. Dado que nuestro proyecto aún no tiene ninguna lógica de negocio, el directorio está casi vacío. Solo encontrarás tres archivos:


lib/hello

├── application.ex

├── mailer.ex

└── repo.ex


El archivo lib/hello/application.ex define una aplicación Elixir llamada Hello.Application. Esto se debe a que las aplicaciones de Phoenix son simplemente aplicaciones de Elixir. El módulo Hello.Application define qué servicios forman parte de nuestra aplicación:


children = [

  # Start the Telemetry supervisor

  HelloWeb.Telemetry,

  # Start the Ecto repository

  Hello.Repo,

  # Start the PubSub system

  {Phoenix.PubSub, name: Hello.PubSub},

  # Start the Endpoint (http/https)

  HelloWeb.Endpoint

  # Start a worker by calling: Hello.Worker.start_link(arg)

  # {Hello.Worker, arg}

]


Por ahora, baste decir que nuestra aplicación inicia un repositorio de base de datos, un sistema PubSub para compartir mensajes entre procesos y nodos, y el punto final de la aplicación, que atiende de manera efectiva las solicitudes HTTP. Estos servicios se inician en el orden en que se definen y, cada vez que cierra su aplicación, se detienen en el orden inverso.

El archivo lib/hello/mailer.ex contiene el módulo Hello.Mailer, que define la interfaz principal para enviar correos electrónicos:

defmodule Hello.Mailer do

  use Swoosh.Mailer, otp_app: :hello

end


En el mismo directorio lib/hello, encontraremos un lib/hello/repo.ex. Define un módulo Hello.Repo que es nuestra interfaz principal para la base de datos. Si está utilizando Postgres (la base de datos predeterminada), verá algo como esto:


defmodule Hello.Repo do

  use Ecto.Repo,

    otp_app: :hello,

    adapter: Ecto.Adapters.Postgres

end


Como nosotros usamos mysql vemos esto: 


defmodule HelloWorld.Repo do

  use Ecto.Repo,

    otp_app: :hello_world,

    adapter: Ecto.Adapters.MyXQL

end



Libros gratuitos

 

Download FREE IT Guides!

 

Linux Bible, 10th Edition ($36.00 Value) FREE for a Limited Time

Linux Bible, 10th Edition is the ultimate hands-on Linux user guide, whether you're a true beginner or a more advanced user navigating recent changes. this updated tenth edition covers the latest versions of Red Hat Enterprise Linux

 
 

Teach Yourself VISUALLY Windows 11 ($19.00 Value) FREE for a Limited Time

Teach Yourself VISUALLY Windows 11 collects all the resources you need to master the day-to-day use of Microsoft’s new operating system and delivers them in a single resource. Fully illustrated, step-by-step

 
 

RESTful Architecture Cheatsheet

The web has become an integral part of our daily lives, and with it, web applications have also become increasingly popular. However, as the number of web applications has grown, so has the complexity of managing them

 
 

Modern API Development with Spring and Spring Boot ($33.99 Value) FREE for a Limited Time

Developers need to know how to adapt to these modern API design principles. Apps are now developed with APIs that enable ease of integration for the cloud environment and distributed systems.

 

jueves, 16 de marzo de 2023

Primeros pasos con Phoenix parte 2


Tema que en el post anterior cree un hola mundo pero con postgres y no tengo instalado postgres así que tiraba error por todos lados. 

Vamos hacer un hola mundo con mysql : 

 mix phx.new hello_world --module HelloWorld --database mysql

Y luego hacemos : 

 cd hello_world

Y para configurar la base usamos :

 mix ecto.create

A mi me tira este error : 

00:07:59.485 [error] GenServer #PID<0.297.0> terminating

** (MyXQL.Error) (1045) (ER_ACCESS_DENIED_ERROR) Access denied for user 'root'@'localhost' (using password: NO)

    (db_connection 2.4.3) lib/db_connection/connection.ex:100: DBConnection.Connection.connect/2

    (connection 1.1.0) lib/connection.ex:622: Connection.enter_connect/5

    (stdlib 4.2) proc_lib.erl:240: :proc_lib.init_p_do_apply/3

Last message: nil

State: MyXQL.Connection


Porque mi base mysql tiene password y no existe la base que esta queriendo consultar. Por ende vamos a el archivo hello_world/config/dev.exs y vamos a configurar los datos : 

config :hello_world, HelloWorld.Repo,
  username: "root",
  password: "acaVaElPassword",
  hostname: "localhost",
  database: "hello_world_dev",
  stacktrace: true,
  show_sensitive_data_on_connection_error: true,
  pool_size: 10

Como vimos la base se llama hello_world_dev y la creamos con el sql : 

CREATE SCHEMA `hello_world_dev` ;

O de forma visual con el workbeach. 

Ahora si ejecutamos : mix ecto.create obtendremos la siguiente salida: 

Compiling 15 files (.ex)

Generated hello_world app

The database for HelloWorld.Repo has already been created


Ahora podemos levantar el server sin problemas :

mix phx.server


Y si vamos a http://127.0.0.1:4000/ obtendremos la pagina de inicio :D 



miércoles, 15 de marzo de 2023

Libro Gratuito: Programación avanzada de Linux


Están regalando este libro y bueno simplemente me estoy haciendo eco de la noticia. 

Este libro es especial para la gente que sabe C y quiere programar para linux. 

Sin más, dejo el link: https://blog.educalix.com/wp-content/uploads/2023/03/advanced-linux-programming.pdf

lunes, 13 de marzo de 2023

Vamos a implementar un framework de procesamiento paralelo simple con Cats parte 2

Dado que en el post anterios refrescamos nuestra memoria de Futures, veamos cómo podemos dividir el trabajo en lotes. Podemos consultar la cantidad de CPU disponibles en nuestra máquina mediante una llamada API desde la biblioteca estándar de Java:

Runtime.getRuntime.availableProcessors

// res11: Int = 2

Podemos particionar una secuencia (en realidad cualquier cosa que implemente Vector) usando el método agrupado. Usaremos esto para dividir lotes de trabajo para cada CPU:


(1 to 10).toList.grouped(3).toList

// res12: List[List[Int]] = List(

//List(1, 2, 3),

//List(4, 5, 6),

//List(7, 8, 9),

//List(10)

// )


Implemente una versión paralela de foldMap llamada parallelFoldMap:


def parallelFoldMap[A, B: Monoid] (values: Vector[A]) (func: A => B): Future[B] = {

    // Calculate the number of items to pass to each CPU:

    val numCores = Runtime.getRuntime.availableProcessors

    val groupSize = (1.0 * values.size / numCores).ceil.toInt

    // Create one group for each CPU:

    val groups: Iterator[Vector[A]] = values.grouped(groupSize)

    // Create a future to foldMap each group:

    val futures: Iterator[Future[B]] =

        groups map { group =>

            Future {

                group.foldLeft(Monoid[B].empty)(_ |+| func(_))

            }

    }


    // foldMap over the groups to calculate a final result:

    Future.sequence(futures) map { iterable =>

    iterable.foldLeft(Monoid[B].empty)(_ |+| _)

    }

}

val result: Future[Int] = parallelFoldMap((1 to 1000000).toVector)(identity)

Await.result(result, 1.second)

// res14: Int = 1784293664


Aunque implementamos foldMap nosotros mismos arriba, el método también está disponible como parte de la clase de tipo Foldable:


import cats.Monoid

import cats.instances.int._

// for Monoid

import cats.instances.future._ // for Applicative and Monad

import cats.instances.vector._ // for Foldable and Traverse

import cats.syntax.foldable._// for combineAll and foldMap

import cats.syntax.traverse._// for traverse

import scala.concurrent._

import scala.concurrent.duration._

import scala.concurrent.ExecutionContext.Implicits.global


def parallelFoldMap[A, B: Monoid](values: Vector[A]) (func: A => B): Future[B] = {

    val numCores = Runtime.getRuntime.availableProcessors

    val groupSize = (1.0 * values.size / numCores).ceil.toInt

    values.grouped(groupSize).toVector.traverse(group => Future(group.toVector.foldMap(func)))

        .map(_.combineAll)

}

val future: Future[Int] = parallelFoldMap((1 to 1000).toVector)(_ * 1000)

Await.result(future, 1.second)

// res18: Int = 500500000

Vamos a implementar un framework de procesamiento paralelo simple con Cats

Vamos a implementar un framework de procesamiento paralelo simple pero poderoso utilizando Monoids, Functors y una serie de otras cosas.

Si ha utilizado Hadoop o Spark o ha trabajado en "grandes datos", habrá oído hablar de MapReduce, que es un modelo de programación para realizar el procesamiento de datos en paralelo en grupos de máquinas (también conocidos como "nodos"). Como sugiere el nombre, el modelo se construye alrededor de una fase de map, que es la misma función de map que conocemos de Scala y la clase de tipo Functor, y una fase de reducción, que generalmente llamamos plegar o fold en Scala.

Recuerde que la firma general para el mapa es aplicar una función A => B a una F[A], devolviendo una F[B]

map transforma cada elemento individual en una secuencia de forma independiente. Podemos paralelizar fácilmente porque no hay dependencias entre las transformaciones aplicadas a diferentes elementos (la firma de tipo de la función A => B).

Nuestro paso de reducción se convierte en un pliegue a la izquierda sobre los resultados del map distribuido.

Al distribuir el paso de reducción, perdemos el control sobre el orden de recorrido. Es posible que nuestra reducción general no sea completamente de izquierda a derecha: podemos reducir de izquierda a derecha en varias subsecuencias y luego combinar los resultados. Para asegurar la corrección necesitamos una operación de reducción que sea asociativa:

reduce(a1, reduce(a2, a3)) == reduce(reduce(a1, a2), a3)

Si tenemos asociatividad, podemos distribuir arbitrariamente el trabajo entre nuestros nodos siempre que las subsecuencias en cada nodo permanezcan en el mismo orden que el conjunto de datos inicial.

Nuestra operación de plegado nos obliga a sembrar el cálculo con un elemento de tipo B. Dado que el plegado se puede dividir en un número arbitrario de pasos paralelos, la semilla no debería afectar el resultado del cálculo. Esto naturalmente requiere que la semilla sea un elemento de identidad:

reduce(seed, a1) == reduce(a1, seed) == a1

En resumen, nuestro pliegue paralelo dará los resultados correctos si:

• requerimos que la función reductora sea asociativa;

• Sembramos el cálculo con la identidad de esta función.

¿Cómo suena este patrón? Así es, hemos completado el círculo de regreso a Monoid. El patrón de diseño monoide para trabajos de reducción de mapas está en el centro de los sistemas de big data recientes, como Summingbird de Twitter.

En este proyecto vamos a implementar un map-reduce de una sola máquina muy simple. Comenzaremos implementando un método llamado foldMap para modelar el flujo de datos que necesitamos.

Vimos foldMap es una de las operaciones derivadas que se encuentra encima de foldLeft y foldRight. Sin embargo, en lugar de usar Foldable, volveremos a implementar foldMap aquí nosotros mismos, ya que proporcionará información útil sobre la estructura de map‐reduce. Comience escribiendo la firma de foldMap. Debe aceptar los siguientes parámetros:

• una secuencia de tipo Vector[A];

• una función de tipo A => B, donde hay un Monoide para B;

Deberá agregar parámetros implícitos o límites de contexto para completar la firma de tipo.

import cats.Monoid

/** Single-threaded map-reduce function.

* Maps `func` over `values` and reduces using a `Monoid[B]`.

*/

def foldMap[A, B: Monoid](values: Vector[A])(func: A => B): B =

???

Ahora implemente el cuerpo de foldMap:

  1. comenzar con una secuencia de elementos de tipo A;
  2. mapear sobre la lista para producir una secuencia de elementos de tipo B;
  3. usa el Monoide para reducir los elementos a una sola B.

Aquí hay algunos resultados de muestra para referencia:


import cats.instances.int._ // for Monoid

foldMap(Vector(1, 2, 3))(identity)

// res1: Int = 6

import cats.instances.string._ // for Monoid

// Mapping to a String uses the concatenation monoid:

foldMap(Vector(1, 2, 3))(_.toString + "! ")

// res2: String = "1! 2! 3! "

// Mapping over a String to produce a String:

foldMap("Hello world!".toVector)(_.toString.toUpperCase)

// res3: String = "HELLO WORLD!"


Veamos la implementación: 


import cats.Monoid

import cats.syntax.semigroup._ // for |+|

def foldMap[A, B : Monoid](as: Vector[A])(func: A => B): B =

as.map(func).foldLeft(Monoid[B].empty)(_ |+| _)


Podemos hacer una pequeña alteración a este código para hacer todo en un solo paso:


def foldMap[A, B : Monoid](as: Vector[A])(func: A => B): B =

as.foldLeft(Monoid[B].empty)(_ |+| func(_))


Ahora que tenemos una implementación funcional de subproceso único de foldMap, veamos cómo distribuir el trabajo para que se ejecute en paralelo. Usaremos nuestra versión de subproceso único de foldMap como bloque de construcción. Escribiremos una implementación de CPU múltiple que simule la forma en que distribuiríamos el trabajo en un clúster de reducción de mapa:

  1. comenzamos con una lista inicial de todos los datos que necesitamos procesar;
  2. dividimos los datos en lotes, enviando un lote a cada CPU;
  3. las CPU ejecutan una fase de mapa a nivel de lote en paralelo;
  4. Las CPU ejecutan una fase de reducción de nivel de lote en paralelo, produciendo un local resultado de cada lote;
  5. reducimos los resultados de cada lote a un único resultado final.

Scala proporciona algunas herramientas simples para distribuir el trabajo entre subprocesos. Podríamos usar la biblioteca de colecciones paralelas para implementar una solución, pero desafiémonos a nosotros mismos profundizando un poco más e implementando el algoritmo nosotros mismos usando Futures.

Ya sabemos bastante sobre la naturaleza monádica de Futures. Tomemos un momento para un resumen rápido y para describir cómo se programan los futuros de Scala detrás de escena.

Los futuros se ejecutan en un grupo de subprocesos, determinado por un parámetro ExecutionContext implícito.

Cada vez que creamos un futuro, ya sea a través de una llamada a Future.apply o algún otro combinador, debemos tener un ExecutionContext implícito en el alcance:

import scala.concurrent.Future

import scala.concurrent.ExecutionContext.Implicits.global

val future1 = Future {

     (1 to 100).toList.foldLeft(0)(_ + _)

}

// future1: Future[Int] = Future(Success(5050))

val future2 = Future {

    (100 to 200).toList.foldLeft(0)(_ + _)

}

// future2: Future[Int] = Future(Success(15150))


En este ejemplo, hemos importado un ExecutionContext.Implicits.global. Este contexto predeterminado asigna un grupo de subprocesos con un subproceso por CPU en nuestra máquina. Cuando creamos un futuro, ExecutionContext lo programa para su ejecución. Si hay un subproceso libre en el grupo, Future comienza a ejecutarse de inmediato. La mayoría de las máquinas modernas tienen al menos dos CPU, por lo que en nuestro ejemplo es probable que future1 y future2 se ejecuten en paralelo.

Algunos combinadores crean nuevos Futuros que programan el trabajo en función de los resultados de otros Futuros. Los métodos map y flatMap, por ejemplo, programan cálculos que se ejecutan tan pronto como se calculan sus valores de entrada y hay una CPU disponible:

val future3 = future1.map(_.toString)

// future3: Future[String] = Future(Success(5050))

val future4 = for {

    a <- future1

    b <- future2

} yield a + b

// future4: Future[Int] = Future(Success(20200))


Podemos convertir una List[Future[A]] en una Future[List[A]] usando Future.sequence:


Future.sequence(List(Future(1), Future(2), Future(3)))

// res6: Future[List[Int]] = Future(Success(List(1, 2, 3)))


Podemos convertir una List[Future[A]] en una Future[List[A]] usando Future.sequence:


import cats.instances.future._ // for Applicative

import cats.instances.list._// for Traverse

import cats.syntax.traverse._// for sequence

List(Future(1), Future(2), Future(3)).sequence

// res7: Future[List[Int]] = Future(Success(List(1, 2, 3)))


Se requiere un ExecutionContext en cualquier caso. Finalmente, podemos usar Await.result para bloquear un futuro hasta que haya un resultado disponible:


import scala.concurrent._

import scala.concurrent.duration._

Await.result(Future(1), 1.second) // wait for the result

// res8: Int = 1


También hay implementaciones de Monad y Monoid para Future disponibles en cats.instances.future:


import cats.{Monad, Monoid}

import cats.instances.int._

// for Monoid

import cats.instances.future._ // for Monad and Monoid

Monad[Future].pure(42)

Monoid[Future[Int]].combine(Future(1), Future(2))


Me quedo medio largo el post, así que vamos a seguir en el proximo post.