<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Sergio Valente</title>
    <link>https://sergiovp.dev/</link>
    <description>Recent content on Sergio Valente</description>
    <image>
      <url>https://sergiovp.dev/%3Clink%20or%20path%20of%20image%20for%20opengraph,%20twitter-cards%3E</url>
      <link>https://sergiovp.dev/%3Clink%20or%20path%20of%20image%20for%20opengraph,%20twitter-cards%3E</link>
    </image>
    <generator>Hugo -- gohugo.io</generator>
    <language>es</language>
    <lastBuildDate>Sun, 01 Feb 2026 05:00:00 -0600</lastBuildDate><atom:link href="https://sergiovp.dev/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>The Glove80 is the last keyboard I will need</title>
      <link>https://sergiovp.dev/blog/the-glove80-is-the-last-keyboard-i-will-need/</link>
      <pubDate>Sun, 01 Feb 2026 05:00:00 -0600</pubDate>
      
      <guid>https://sergiovp.dev/blog/the-glove80-is-the-last-keyboard-i-will-need/</guid>
      <description>The Glove80 keyboard has transformed my typing experience.</description>
      <content:encoded><![CDATA[<p>Before the Glove80, I had some other mechanical keyboards. I slowly transitioned to keyboards with fewer keys. First a 100% Corsair and Keychron, then a 70% Keychron.</p>
<p>Eventually, I acquired the Glove80, which is some kind of 80% keyboard. And the difference is abysmal. The first experience with the Keychron was great, but IMHO, currently it feels overpriced. There still are good keyboards.</p>
<p>But Glove80 and other ergo keyboards are in a different category, not only in price but in a more important field: ergonomics. Using a Keychron without a palm rest is (at least for me) impossible. But the feeling of split keyboards is another thing. These are the main characteristics that made the Glove80 one of the best:</p>
<ul>
<li>Mechanical: Great for longevity and reliability.</li>
<li>Split: The best setup for ergonomics—be gentle with your wrists. Palm rest is included.</li>
<li>Low profile: This, alongside red switches or similar, reduces the fatigue of long writing sessions.</li>
<li>Concave key wells and columnar layout reduces the effort of fingers to reach distant keys.</li>
<li>Tenting: Adding some degree of tenting reduces pronation, improving the comfort of forearms and wrists.</li>
<li>ZMK firmware: Use whatever layout you want, including different layers, modifiers, etc. No more Karabiner configs for me.</li>
</ul>
<p>A side effect of using the Glove80 is that I had to learn touch typing. I learned to type in an empiric manner; this leads me to have several vices. Using a split columnar keyboard makes these problems even more apparent. It&rsquo;s funny how this design enforces you to press a key with the correct finger. It somehow forces you to type correctly. This, in turn, improved my typing speed.</p>
<p>I&rsquo;m far from the big numbers like 100 wpm, but I think ~70 wpm it&rsquo;s a pretty decent number.</p>
<p>Finally, it is much less noisy than regular mechanical keyboards.</p>
<p>This marks a turning point in my experience with keyboards. I will definitely not go back to conventional keyboards. These are expensive keyboards, but they are investments. In comfort and health. Much cheaper in the long run.</p>
<p>If you are curious about my layout config, here is: <a href="https://my.moergo.com/glove80/#/layout/user/9f6b775f-eb6a-4aff-b70f-95cd6fe4f64e">Glove80 ZMK layout config</a>.s</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Pensamientos sobre Testing</title>
      <link>https://sergiovp.dev/blog/pensamientos-sobre-testing/</link>
      <pubDate>Mon, 13 Oct 2025 23:00:00 -0600</pubDate>
      
      <guid>https://sergiovp.dev/blog/pensamientos-sobre-testing/</guid>
      <description>Ideas sobre testing automatizado, su utilidad y retos.</description>
      <content:encoded><![CDATA[<p>Este es un documento &ldquo;vivo&rdquo;. En él cual trato de plasmar mi pensamiento actual sobre pruebas automatizadas, de igual forma que mantener un registro conforme aprendo nuevas ideas y cambio de opinión.</p>
<h2 id="la-mentalidad-sobre-las-pruebas-automatizadas">La mentalidad sobre las pruebas automatizadas</h2>
<p>No soy capaz de recordar una sola mención sobre pruebas automatizadas durante mi paso por la universidad. No fue, sino la búsqueda de información sobre &ldquo;buenas prácticas&rdquo; lo que me llevo a conocer sobre este tema.</p>
<p>A pesar de leer sobre sus beneficios y creer en ellos, no era capaz de materializarlo en código. Tenía una desconexión entre la teoría y la practica. Además de una carencia fundamental de entendimiento, acepte lo que otros decían sobre los beneficios del testing sin realmente comprender e interiorizar dichos beneficios, era una creencia mecánica.</p>
<p>Durante mis primeras experiencias profesionales trabajé en proyectos y empresas donde escribir pruebas no era parte del trabajo, por lo que ningún miembro del equipo escribió una sola prueba. Incluyéndome por supuesto. Introducir el mismo error múltiples veces era algo común.</p>
<p>Eventualmente, llegué a una compañía donde era necesario escribir pruebas. Si bien &ldquo;creía&rdquo; en los beneficios de escribir pruebas, era una simple repetición de algún argumento que leí en algún lugar. Realmente no entendía por qué eran importantes. No había experimentado sus beneficios.</p>
<p>Así que, las primeras semanas o quizás meses, escribir pruebas se sentía como hacer el trabajo dos veces. Ya terminé mi funcionalidad y &ldquo;funciona&rdquo; en mi computadora. ¿Por qué debería escribir más código para algo que ya sé que funciona?</p>
<p>Hasta que un día, realizando un cambio aparentemente sencillo, y a modo de simple comprobación, ejecute la suite de pruebas con total convencimiento de que nada cambiaria. Había realizado un trabajo perfecto.</p>
<p>Pero una de las pruebas dijo lo contrario. Una prueba estaba fallando, en una funcionalidad que ni siquiera estaba modificando. ¿Cómo es posible que una prueba de una funcionalidad &ldquo;no relacionada&rdquo; este fallando?</p>
<p>Una rápida inspección de la prueba fallida, y del módulo que estaba probando me dio la respuesta. Había codigo que se reutilizaba en ambos módulos y yo había cambiado su comportamiento.</p>
<p>Me tomo menos de un minuto hacer el cambio necesario para que funcionara en ambos lugares. Las pruebas automatizadas me salvaron de introducir un error de manera inadvertida, en un cambio que yo consideraba trivial.</p>
<p>Fue en ese momento, que mi mentalidad cambio. Y de pronto escribir pruebas, nunca más se sintió como trabajar dos veces, se volvió parte natural del ciclo de desarrollo. Escribir una nueva funcionalidad y las respectivas pruebas ahora son partes de la misma actividad.</p>
<p>Sin mencionar que el tiempo que me toma desarrollar algo nuevo no cambio, a pesar de que la intuición nos lleva a pensar que al implicar trabajo extra, implica, por lo tanto, tiempo extra. Sin embargo, al menos en mi experiencia, este trabajo extra se ve compensado por el hecho de que mantener una batería de pruebas saludable ayuda a mantener la velocidad de desarrollo conforme el proyecto se vuelve más grande, los requerimientos cambian y se introducen nuevas funcionalidades.</p>
<p>Aunque es cierto que en proyectos legacy, tratar de incluir pruebas es mucho mas dificil debido a la complejidad y deuda tecnica acumulada. Suele dar mejores resultados utilizar tecnicas para trabajar con codigo legado y agregar pruebas al codigo nuevo, desplazando poco a poco el codigo anterior mas dificil de probar.</p>
<h2 id="pirámide-de-pruebas">Pirámide de pruebas</h2>
<p>La pirámide de pruebas<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> es un concepto muy debatido. Habla sobre los distintos niveles de pruebas que podemos incluir, desde las pruebas unitarias hasta las pruebas e2e (end to end). Con un número increíble de tipos de pruebas entre estos dos niveles.</p>
<p>Uno de los puntos de debate es que tantas pruebas de cada tipo necesitamos, al mismo tiempo que buscamos mantener una batería de pruebas rápida y confiable.</p>
<p>Mientras que las pruebas unitarias son rápidas de escribir y ejecutar, no suelen proveer retroalimentación de como el usuario usa nuestro software. Aunque claro, se puede hablar horas sobre que tan unitario es lo unitario<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.</p>
<p>En cambio, las pruebas e2e, si bien prueban flujos completos tal cual lo haría un usuario, son muy caras de escribir y ejecutar. Sobre todo en los tiempos de Microservicios o aplicaciones separadas para el backed y el frontend. Al grado que algunas empresas han decidido abandonar este tipo de pruebas. <sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<p>Lo importante, como menciona Kent Beck <sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> es escribir el menor número de pruebas necesarias, para alcanzar el nivel de confianza requerido en nuestro proyecto.</p>
<p>La confianza de que al realizar un cambio y mis pruebas estén en verde todo se sigue comportando como se espera o, por el contrario, si alguna prueba está en rojo, algo cambio.</p>
<p>Esta confianza, ademas, nos ayuda a integrar cambios más rápido. Lo que a su vez nos permite reducir el riesgo de cada cambio y cada nueva funcionalidad introducida.</p>
<p>Nos da la confianza para entregar software lo más rápido posible.</p>
<p>En general encuentro que las pruebas de integración y las pruebas unitarias sociables hacen un gran trabajo brindando esa confianza que buscamos. Por lo que suelo escribirlas más, incluyendo aquellas que atacan a una base de datos de pruebas. Prefiriendolas sobre pruebas unitarias solitarias que aíslan completamente los colaboradores y otros subsistemas como la base de datos.</p>
<p>Si bien atacar directamente a una base de datos real puede ralentizar la ejecución de nuestras pruebas. Hoy en día bases de datos como Postgres soportan la ejecución concurrente de pruebas <sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup> y hay servidores de CI sofisticados capaces de paralelizar la ejecución de una masiva bateria de pruenas<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>. De otra forma, no podríamos verificar el esquema o las restricciones en nuestra base de datos.</p>
<p>Sin embargo, esto puede enfrentarse a sus límites (aunque cada vez mas lejanos) en sistemas muy grandes y complejos. Hay proyectos que pueden tomar horas en correr en el pipeline de CI por la gran cantidad de pruebas que tienen.</p>
<p>Un caso especial son los sistemas de terceros, como una API REST externa. En cuyo caso suelo recurrir al uso de mocks y stubs.</p>
<h2 id="mocks">Mocks</h2>
<p>WIP</p>
<h2 id="refactorización">Refactorización</h2>
<p>WIP</p>
<h2 id="confianza">Confianza</h2>
<p>WIP</p>
<h2 id="tdd">TDD</h2>
<p>Test Driven Development es de esas cosas de las que se lee mucho, pero se ve poco en la practica, o al menos en mi experiencia personal. Seguramente hay empresas y lugares donde es una practica comun, pero no he podido verlo de primera mano.</p>
<p>Al inicio, escribir una prueba antes de saber si quiera como se va a llamar nuestra clase o modulo puede parecer carente de sentido. Aun no sabemos como va a lucir ese código, como podria comenzar escribiendo una prueba? Es algo contraintuitivo.</p>
<p>Pero es precisamente este ejercicio, el que nos empuja a pensar en el diseño del codigo y su interfaz, por adelantado en lugar de hacerlo reaccionando a nuestro intento inicial.</p>
<p>Eh encontrado que es mas facil iniciar con TDD al resolver un bug. Cuando se reporta un bug, primero se escribe una prueba que reproduzca el error, entonces debugueas y corrijes el error. Si la prueba pasa efectivamente se corrijio el error. Ademas esto garantiza que no se vuelva a introducir el mismo error en el futuro.</p>
<p>De esta forma es mas sencillo adoptar el pensamiento de escribir primero la prueba. Un cambio de pensamiento que es incomodo al principio, porque implica dejar de hacer las cosas de la forma en la que estamos acostumbrados.</p>
<p>Lo relaciono al tipo de incomodidad que se experimenta la primera vez que trabajas con un lenguaje funcional si vienes de lenguajes orientados a objetos. Cuando comienzas a experimentar caracteristicas como la inmutabilidad, es extraño, no es &ldquo;intuitivo&rdquo;. Eso no significa que sea malo, o mas dificil. Pero es diferente y hay que acostumbrarse y adapar nuestra forma de pensar.</p>
<p>Cuando comenzamos a hacer algo nuevo siempre es dificil al principio. Solo se supera practicando. Como dicen: La frecuencia reduce la dificultad <sup id="fnref:7"><a href="#fn:7" class="footnote-ref" role="doc-noteref">7</a></sup>.</p>
<p>Otro uso util de TDD y las pruebas en general es que ayudan al diseño del código. Al menos de manera indirecta. No es que te digan como diseñar tu código, pero escribir una prueba hace emerger los smell codes del diseño en forma de un setup complejo, o dificultad para probar el comportamiento buscado.</p>
<p>Si una prueba es dificil de escribir, significa que el código es dificil de probar. Si es dificil de probar probablemente es porque hace demasiadas cosas o tiene demasiadas dependencias o alguno de los tantos code smells existentes. Es decir, podemos mejorar el diseño de ese código.</p>
<p>Como con cualquier práctica hay promotores y detractores. Aunque soy un promotor, lo trato como cualquier regla o principio:</p>
<p>&ldquo;Every rule has its exceptions, and every principle has its limits&rdquo;</p>
<p>Esta frase del libro A Philosophy of Software Design siempre me recuerda que hay que desarrollar criterio para aplicar cualquier herramienta o pieza de conocimiento. Tanto para tener cautela, como para desafiar el &ldquo;estatus quo&rdquo;.</p>
<h2 id="integración-continua">Integración continua</h2>
<p>WIP</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p><a href="https://martinfowler.com/articles/practical-test-pyramid.html">https://martinfowler.com/articles/practical-test-pyramid.html</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p><a href="https://martinfowler.com/bliki/IntegrationTest.html">https://martinfowler.com/bliki/IntegrationTest.html</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p><a href="https://building.nubank.com/es/why-we-killed-our-end-to-end-test-suite/">https://building.nubank.com/es/why-we-killed-our-end-to-end-test-suite/</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p><a href="https://stackoverflow.com/a/153565">https://stackoverflow.com/a/153565</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5" role="doc-endnote">
<p><a href="https://hexdocs.pm/ecto_sql/Ecto.Adapters.SQL.Sandbox.html#module-database-support">https://hexdocs.pm/ecto_sql/Ecto.Adapters.SQL.Sandbox.html#module-database-support</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6" role="doc-endnote">
<p><a href="https://martinfowler.com/bliki/FrequencyReducesDifficulty.html">https://martinfowler.com/bliki/FrequencyReducesDifficulty.html</a>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:7" role="doc-endnote">
<p><a href="https://blog.bytebytego.com/i/165590160/cicd-process">https://blog.bytebytego.com/i/165590160/cicd-process</a>&#160;<a href="#fnref:7" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content:encoded>
    </item>
    
    <item>
      <title>Kubernetes 3: ReplicaSets y Deployments</title>
      <link>https://sergiovp.dev/blog/kubernetes-3-replicasets-y-deployments/</link>
      <pubDate>Sun, 01 Dec 2024 23:00:00 -0600</pubDate>
      
      <guid>https://sergiovp.dev/blog/kubernetes-3-replicasets-y-deployments/</guid>
      <description>Como definir deployments y replicas</description>
      <content:encoded><![CDATA[<p>Ahora toca el turno de hablar de los ReplicaSet <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> y los Deployment <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>.</p>
<h2 id="replicaset">ReplicaSet</h2>
<blockquote>
<p>El objeto de un ReplicaSet es el de mantener un conjunto estable de réplicas de Pods ejecutándose en todo momento. Así, se usa en numerosas ocasiones para garantizar la disponibilidad de un número específico de Pods idénticos.</p>
</blockquote>
<p>Como vemos, los objetos <em>ReplicaSet</em> nos permiten realizar escalamiento horizontal de nuestros pods. Dicho esto, lo más común es no manejar directamente nuestros <em>ReplicaSets</em>, si no hacerlo mediante un <em>Deployment Controller</em>, ya que esto nos da varias conveniencias.</p>
<h2 id="deployment">Deployment</h2>
<blockquote>
<p>Un controlador de Deployment proporciona actualizaciones declarativas para los Pods y los ReplicaSets.</p>
<p>Cuando describes el estado deseado en un objeto Deployment, el controlador del Deployment se encarga de cambiar el estado actual al estado deseado de forma controlada. Puedes definir Deployments
para crear nuevos ReplicaSets, o eliminar Deployments existentes y adoptar todos sus recursos con nuevos Deployments.</p>
</blockquote>
<h2 id="definiendo-un-deployment">Definiendo un Deployment</h2>
<p>Ahora veamos como definir un <em>deployment</em>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yml" data-lang="yml"><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">apps/v1</span>
<span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Deployment</span>
<span style="color:#f92672">metadata</span>:
  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">nginx-deployment</span>
  <span style="color:#f92672">labels</span>:
    <span style="color:#f92672">app</span>: <span style="color:#ae81ff">nginx</span>
<span style="color:#f92672">spec</span>:
  <span style="color:#f92672">replicas</span>: <span style="color:#ae81ff">3</span>
  <span style="color:#f92672">selector</span>:
    <span style="color:#f92672">matchLabels</span>:
      <span style="color:#f92672">app</span>: <span style="color:#ae81ff">nginx</span>
  <span style="color:#f92672">template</span>:
    <span style="color:#f92672">metadata</span>:
      <span style="color:#f92672">labels</span>:
        <span style="color:#f92672">app</span>: <span style="color:#ae81ff">nginx</span>
    <span style="color:#f92672">spec</span>:
      <span style="color:#f92672">containers</span>:
      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">nginx</span>
        <span style="color:#f92672">image</span>: <span style="color:#ae81ff">nginx:1.14.2</span>
        <span style="color:#f92672">ports</span>:
        - <span style="color:#f92672">containerPort</span>: <span style="color:#ae81ff">80</span>
</code></pre></div><p>Una particularidad que tenemos en este caso respecto a los ejemplos previos es el uso de <strong>template</strong>. Esto es debido a que un objeto puede definir a su vez otros objetos.</p>
<p>En este ejemplo, primero definimos un objeto de tipo <em>Deployment</em>, pero también debemos definir el <em>ReplicaSet</em>, que a su vez debe definir el <em>Pod</em>. Todo debe ir en la jerarquía correcta.</p>
<p>Veamos los pasos del deplyment.yml:</p>
<ul>
<li>Primero definimos un objeto de tipo <em>Deployment</em> con el nombre <em>nginx-deployment</em></li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yml" data-lang="yml"><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">apps/v1</span>
<span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Deployment</span>
<span style="color:#f92672">metadata</span>:
  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">nginx-deployment</span>
  <span style="color:#f92672">labels</span>:
    <span style="color:#f92672">app</span>: <span style="color:#ae81ff">nginx</span>
</code></pre></div><ul>
<li>Definimos el numero de replicas mediante <em>replicas</em>,</li>
<li>El campo <code>selector.matchLabels.app</code> identifica los pods que debe gestionar. Por lo que debe coincidir con el <strong>label</strong> del pod.</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yml" data-lang="yml"><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">apps/v1</span>
<span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Deployment</span>
<span style="color:#f92672">metadata</span>:
  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">nginx-deployment</span>
  <span style="color:#f92672">labels</span>:
    <span style="color:#f92672">app</span>: <span style="color:#ae81ff">nginx</span>
<span style="color:#f92672">spec</span>:
  <span style="color:#f92672">replicas</span>: <span style="color:#ae81ff">3</span>
  <span style="color:#f92672">selector</span>:
    <span style="color:#f92672">matchLabels</span>:
      <span style="color:#f92672">app</span>: <span style="color:#ae81ff">nginx</span>
</code></pre></div><ul>
<li>Finalmente definimos el pod mediante el campo <strong>template</strong>. Utilizando el label <code>nginx</code></li>
<li>Con el campo <strong>spec</strong> definimos el contenedor que ejecutara el pod, el nombre y la imagen.</li>
<li>Ademas abrimos el puesto 80</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yml" data-lang="yml"><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">apps/v1</span>
<span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Deployment</span>
<span style="color:#f92672">metadata</span>:
  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">nginx-deployment</span>
  <span style="color:#f92672">labels</span>:
    <span style="color:#f92672">app</span>: <span style="color:#ae81ff">nginx</span>
<span style="color:#f92672">spec</span>:
  <span style="color:#f92672">replicas</span>: <span style="color:#ae81ff">3</span>
  <span style="color:#f92672">selector</span>:
    <span style="color:#f92672">matchLabels</span>:
      <span style="color:#f92672">app</span>: <span style="color:#ae81ff">nginx</span>
  <span style="color:#f92672">template</span>:
    <span style="color:#f92672">metadata</span>:
      <span style="color:#f92672">labels</span>:
        <span style="color:#f92672">app</span>: <span style="color:#ae81ff">nginx</span>
    <span style="color:#f92672">spec</span>:
      <span style="color:#f92672">containers</span>:
      - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">nginx</span>
        <span style="color:#f92672">image</span>: <span style="color:#ae81ff">nginx:1.14.2</span>
        <span style="color:#f92672">ports</span>:
        - <span style="color:#f92672">containerPort</span>: <span style="color:#ae81ff">80</span>
</code></pre></div><p>Ahora creamos nuestro deployment:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">▶ kubectl apply -f deployment.yml
deployment.apps/nginx-deployment created
</code></pre></div><p>Si obtenemos los pods de nuestro cluster, veremos que ya 3 ejecutandose al mismo tiempo:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">▶ kubectl get pods
NAME                               READY   STATUS    RESTARTS   AGE
nginx-deployment-d556bf558-bq846   1/1     Running   <span style="color:#ae81ff">0</span>          58s
nginx-deployment-d556bf558-h4qc5   1/1     Running   <span style="color:#ae81ff">0</span>          58s
nginx-deployment-d556bf558-pw87w   1/1     Running   <span style="color:#ae81ff">0</span>          58s
</code></pre></div><section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p><a href="https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/">https://kubernetes.io/docs/concepts/workloads/controllers/replicaset/</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p><a href="https://kubernetes.io/docs/concepts/workloads/controllers/deployment/">https://kubernetes.io/docs/concepts/workloads/controllers/deployment/</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content:encoded>
    </item>
    
    <item>
      <title>Kubernetes 2: ConfigMaps y Secrets</title>
      <link>https://sergiovp.dev/blog/kubernetes-2-configmaps-y-secrets/</link>
      <pubDate>Wed, 20 Nov 2024 11:23:00 -0600</pubDate>
      
      <guid>https://sergiovp.dev/blog/kubernetes-2-configmaps-y-secrets/</guid>
      <description>Como configurar variables de entorno y secretos en Kubernetes</description>
      <content:encoded><![CDATA[<p>En la entrada pasada creamos un pod utilizando la herramienta <a href="https://kind.sigs.k8s.io/">kind</a> de manera local. En este post veremos como configurar variables de entorno que puedan ser utilizadas por nuestros pods.</p>
<h2 id="configmaps">ConfigMaps</h2>
<blockquote>
<p>Un <em>ConfigMap</em> es un objeto que se utiliza para almacenar datos no confidenciales en pares clave-valor. Los pods pueden utilizar ConfigMaps como variables de entorno, argumentos de línea de comandos o como archivos de configuración en un volumen.<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
</blockquote>
<p>De esta forma no es necesario depender de archivos <code>.env</code> o similares para inyectar variables de entorno a nuestra aplicación.</p>
<p>Dado que los <em>ConfigMaps</em> no proveen ningún tipo de cifrado, no deben ser utilizados para almacenar información sensible como tokens, passwords, etc.</p>
<h2 id="secrets">Secrets</h2>
<blockquote>
<p>Un secreto es un objeto que contiene una pequeña cantidad de datos confidenciales, como una contraseña, un token o una clave. De lo contrario, dicha información podría incluirse en una especificación de pod o en una imagen de contenedor. El uso de un secreto significa que no necesita incluir datos confidenciales en el código de su aplicación.<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup></p>
</blockquote>
<h2 id="creando-un-objeto-configmap">Creando un objeto ConfigMap</h2>
<p>Para esto, vamos a crear un nuevo archivo <code>configmap.yml</code> para crear nuestro objeto de tipo <code>ConfigMap</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yml" data-lang="yml"><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">v1</span>
<span style="color:#f92672">kind</span>: <span style="color:#ae81ff">ConfigMap</span>
<span style="color:#f92672">metadata</span>:
  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">myconfigmap</span>
<span style="color:#f92672">data</span>:
  <span style="color:#f92672">APP_NAME</span>: <span style="color:#ae81ff">myapp</span>
  <span style="color:#f92672">APP_ENV</span>: <span style="color:#ae81ff">development</span>
</code></pre></div><p>Como vemos, a diferencia del yml para crear nuestro pod, usamos la llave <code>data</code>, para definir nuestros valores.</p>
<p>Y procedemos a crear el objeto:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">▶ kubectl apply -f configmap.yml
configmap/myconfigmap created
</code></pre></div><p>Ahora, para poder usarlo en nuestros pods, debemos hacer referencia a el y recrear el pod.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yml" data-lang="yml"><span style="color:#f92672">apiversión</span>: <span style="color:#ae81ff">v1</span>
<span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Pod</span>
<span style="color:#f92672">metadata</span>:
  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">mypod</span>
<span style="color:#f92672">spec</span>:
  <span style="color:#f92672">containers</span>:
  - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">nginx</span>
    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">nginx:latest</span>
    <span style="color:#f92672">envFrom</span>:
    - <span style="color:#f92672">configMapRef</span>:
        <span style="color:#f92672">name</span>: <span style="color:#ae81ff">myconfigmap</span>
</code></pre></div><p>Primero eliminamos el pod existente y posteriormente lo creamos nuevamente:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">▶ kubectl delete -f pod.yml
pod <span style="color:#e6db74">&#34;mypod&#34;</span> deleted

▶ kubectl apply -f pod.yml
pod/mypod created
</code></pre></div><p>Ahora, podemos entrar a nuestro pod mediante su nombre y verificar que podemos leer las variables de entorno:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">▶ kubectl exec -it mypod -- bash
root@mypod:/# echo $APP_NAME
myapp
root@mypod:/# echo $APP_ENV
development
root@mypod:/#
</code></pre></div><h2 id="creando-un-objeto-secret">Creando un objeto Secret</h2>
<p>Para gestionar secrets es muy parecido, pero son un tipo distinto de objeto, por lo que ahora crearemos un archivo <code>secrets.yml</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yml" data-lang="yml"><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">v1</span>
<span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Secret</span>
<span style="color:#f92672">metadata</span>:
  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">mysecret</span>
<span style="color:#f92672">type</span>: <span style="color:#ae81ff">Opaque</span>
<span style="color:#f92672">data</span>:
  <span style="color:#f92672">username</span>: <span style="color:#ae81ff">YWRtaW4K</span> <span style="color:#75715e"># admin</span>
  <span style="color:#f92672">password</span>: <span style="color:#ae81ff">cGFzc3dvcmQK</span> <span style="color:#75715e"># password</span>
</code></pre></div><p>Una peculiaridad es que en lugar de ir en texto plano, deben estar en base64.</p>
<p>Ahora modificamos nuestro <code>pod.yml</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yml" data-lang="yml"><span style="color:#f92672">apiVersion</span>: <span style="color:#ae81ff">v1</span>
<span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Pod</span>
<span style="color:#f92672">metadata</span>:
  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">mypod</span>
<span style="color:#f92672">spec</span>:
  <span style="color:#f92672">containers</span>:
  - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">nginx</span>
    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">nginx:latest</span>
    <span style="color:#f92672">envFrom</span>:
    - <span style="color:#f92672">configMapRef</span>:
        <span style="color:#f92672">name</span>: <span style="color:#ae81ff">myconfigmap</span>
    <span style="color:#f92672">env</span>:
    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">USERNAME</span>
      <span style="color:#f92672">valueFrom</span>:
        <span style="color:#f92672">secretKeyRef</span>:
          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">mysecret</span>
          <span style="color:#f92672">key</span>: <span style="color:#ae81ff">username</span>
    - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">PASSWORD</span>
      <span style="color:#f92672">valueFrom</span>:
        <span style="color:#f92672">secretKeyRef</span>:
          <span style="color:#f92672">name</span>: <span style="color:#ae81ff">mysecret</span>
          <span style="color:#f92672">key</span>: <span style="color:#ae81ff">password</span>
</code></pre></div><p>Agregamos al <em>spec</em> la llave <em>env</em>. Donde por cada secret definimos:</p>
<ul>
<li><code>name</code>: Con este name podremos acceder al secret como variable de entorno en nuestro pod</li>
<li><code>valueFrom.name</code>:  el nombre que le dimos a nuestro objeto Secret (<code>mysecret</code>)</li>
<li><code>valueFrom.name.key</code>: la llave que usamos en <code>secrets.yml</code> para definir el secret, en este caso <code>username</code> y <code>password</code></li>
</ul>
<p>Ahora realizamos el mismo proceso, eliminar y crear nuevamente nuestro pod:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">▶ kubectl delete -f pod.yml
pod <span style="color:#e6db74">&#34;mypod&#34;</span> deleted

▶ kubectl apply -f pod.yml
pod/mypod created
</code></pre></div><p>Finalmente, entramos nuevamente a nuestro pod y podremos acceder a nuestros <em>secrets</em> como variables de entorno:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">▶ kubectl exec -it mypod -- bash
root@mypod:/# echo $USERNAME
admin
root@mypod:/# echo $PASSWORD
password
root@mypod:/#
</code></pre></div><section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p><a href="https://kubernetes.io/docs/concepts/configuration/configmap/">https://kubernetes.io/docs/concepts/configuration/configmap/</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p><a href="https://kubernetes.io/docs/concepts/configuration/secret/">https://kubernetes.io/docs/concepts/configuration/secret/</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content:encoded>
    </item>
    
    <item>
      <title>Kubernetes 1: Conceptos basicos</title>
      <link>https://sergiovp.dev/blog/kubernetes-1-conceptos-basicos/</link>
      <pubDate>Tue, 12 Nov 2024 23:00:00 -0600</pubDate>
      
      <guid>https://sergiovp.dev/blog/kubernetes-1-conceptos-basicos/</guid>
      <description>Breve introducción sobre los conceptos básicos de Kubernetes y sus componentes</description>
      <content:encoded><![CDATA[<p>Kubernetes es una plataforma para administrar servicios. Uno de sus principales casos de uso es el de orquestar contenedores.</p>
<p>Es decir, si tienes una plataforma que requiere confiabilidad y escalabilidad (entre otras) y que se empaqueta como imágenes de Docker. Kubernetes te permite administrar tu plataforma mediante diversas herramientas.</p>
<h2 id="componentes">Componentes</h2>
<p><img loading="lazy" src="https://res.cloudinary.com/escribocodigo/image/upload/b_rgb:ffffff/v1731518164/components-of-kubernetes_dvayw4.svg" alt="Kubernetes components"  />
</p>
<p>Tenemos tres grandes conceptos: los clusters, el Control Plane y los nodos <sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup></p>
<p>El control plane se encarga de gestionar las tareas relacionadas al cluster, como guardar metadatos sobre los nodos, decidir en que nodo crear los nuevos pods, verificar la salud de los nodos, etc.</p>
<p>Generalmente, dado que se suelen usar servicios administrados como EKS, solemos interactuar poco de manera directa con el <em>Control Plane</em></p>
<p>En los nodos tenemos el runtime de los contenedores (como <em>containerd</em>), <em>kubelete</em> que verifica y notifica al control plane mediante el kube-apiserver la salud de los pods, etc.</p>
<p>Finalmente los <em>pods</em> que se ejecutan dentro de los nodos donde cada nodo puede tener múltiples <em>pods</em> corriendo al mismo tiempo.</p>
<h2 id="objetos">Objetos</h2>
<p>Los objetos son entidades persistentes. Son utilizadas para representar el estado del clúster <sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup>. Estos son los <em>pods</em>, los <em>deployments</em>, los <em>controllers</em>, etc. Los objetos y los nodos son los elementos con los que más solemos interacturar.</p>
<p>Por otro lado, tenemos dos formas de gestionar los objetos: la primera es mediante la CLI kubectl (imperativa) y mediante archivos de configuración YML (declarativa).</p>
<h2 id="creando-nuestro-primer-cluster">Creando nuestro primer cluster</h2>
<p>Para propósitos de aprendizaje vamos a utilizar la herramienta kind<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup>, que nos permite crear clusters de manera local. Aunque hay otras como minikube<sup id="fnref:4"><a href="#fn:4" class="footnote-ref" role="doc-noteref">4</a></sup> o el mismo Docker Desktop<sup id="fnref:5"><a href="#fn:5" class="footnote-ref" role="doc-noteref">5</a></sup>. Al igual que necesitamos instalar la herramienta kubectl<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>.</p>
<p>Ademas debemos tener instalado Docker<sup id="fnref:6"><a href="#fn:6" class="footnote-ref" role="doc-noteref">6</a></sup>.</p>
<p>MacOS:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash"><span style="color:#75715e"># kind</span>
brew install kind

<span style="color:#75715e"># kubectl</span>
curl -LO <span style="color:#e6db74">&#34;https://dl.k8s.io/release/</span><span style="color:#66d9ef">$(</span>curl -L -s https://dl.k8s.io/release/stable.txt<span style="color:#66d9ef">)</span><span style="color:#e6db74">/bin/darwin/arm64/kubectl&#34;</span>
</code></pre></div><p>Ubuntu:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash"><span style="color:#75715e"># kind</span>
<span style="color:#75715e">#!/bin/bash</span>

<span style="color:#75715e"># For AMD64 / x86_64</span>
<span style="color:#f92672">[</span> <span style="color:#66d9ef">$(</span>uname -m<span style="color:#66d9ef">)</span> <span style="color:#f92672">=</span> x86_64 <span style="color:#f92672">]</span> <span style="color:#f92672">&amp;&amp;</span> curl -Lo ./kind https://kind.sigs.k8s.io/dl/v0.20.0/kind-linux-amd64
chmod +x ./kind
sudo cp ./kind /usr/local/bin/kind
rm -rf kind
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash"><span style="color:#75715e"># kubectl</span>
curl -LO <span style="color:#e6db74">&#34;https://dl.k8s.io/release/</span><span style="color:#66d9ef">$(</span>curl -L -s https://dl.k8s.io/release/stable.txt<span style="color:#66d9ef">)</span><span style="color:#e6db74">/bin/linux/amd64/kubectl&#34;</span>
</code></pre></div><p>Ahora podemos ejecutar el siguiente comando, lo que creara nuestro primer cluster con el nombre <em>kind</em>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">▶ kind create cluster
Creating cluster <span style="color:#e6db74">&#34;kind&#34;</span> ...
 ✓ Ensuring node image <span style="color:#f92672">(</span>kindest/node:v1.31.2<span style="color:#f92672">)</span> 🖼
 ✓ Preparing nodes 📦
 ✓ Writing configuration 📜
 ✓ Starting control-plane 🕹️
 ✓ Installing CNI 🔌
 ✓ Installing StorageClass 💾
Set kubectl context to <span style="color:#e6db74">&#34;kind-kind&#34;</span>
You can now use your cluster with:

kubectl cluster-info --context kind-kind

Have a question, bug, or feature request? Let us know! https://kind.sigs.k8s.io/#community 🙂
</code></pre></div><p>Si revisamos los contenedores que se están ejecutando veremos uno para el control plane del cluster, y cuando tengamos nodos veremos un contenedor por cada nodo o worker.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">▶ docker ps | grep <span style="color:#e6db74">&#34;kind&#34;</span>
695e351dd59d   kindest/node:v1.31.2    <span style="color:#e6db74">&#34;/usr/local/bin/entr…&#34;</span>   About a minute ago   Up About a minute      127.0.0.1:58969-&gt;6443/tcp                                                kind-control-plane
</code></pre></div><p>Ahora podemos definir un objeto pod, mediante el enfoque declarativo en un archivo <code>pod.yml</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-yml" data-lang="yml"><span style="color:#f92672">apiversión</span>: <span style="color:#ae81ff">v1</span>
<span style="color:#f92672">kind</span>: <span style="color:#ae81ff">Pod</span>
<span style="color:#f92672">metadata</span>:
  <span style="color:#f92672">name</span>: <span style="color:#ae81ff">mypod</span>
<span style="color:#f92672">spec</span>:
  <span style="color:#f92672">containers</span>:
  - <span style="color:#f92672">name</span>: <span style="color:#ae81ff">nginx</span>
    <span style="color:#f92672">image</span>: <span style="color:#ae81ff">nginx:latest</span>
</code></pre></div><p>Aquí tenemos la versión mínima para definir nuestro objeto pod:</p>
<ul>
<li>La versión del api</li>
<li><code>kind</code> para definir el tipo de objeto</li>
<li><code>metadata</code> donde definimos el nombre de nuestro pod</li>
<li><code>spec</code> donde definimos cosas como los contenedores que se van a ejecutar, si definimos volúmenes, etc.</li>
</ul>
<p>Ahora aplicamos los cambios con <em>kubectl</em>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">▶ kubectl apply -f pod.yml
pod/mypod created
</code></pre></div><p>Por lo que si ahora obtenemos los pods de nuestro cluster veremos el nuevo pod creandose:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">▶ kubectl get pods
NAME    READY   STATUS    RESTARTS   AGE
mypod   1/1     Running   <span style="color:#ae81ff">0</span>          14s
</code></pre></div><p>Por el momento no acceder al nginx del pod ya que no esta expuesto, para propósitos de <em>debug</em> podemos hacer <em>port-forward</em> a un puerto en nuestra maquina local para poder verificar que efectivamente hay un nginx ejecutandose:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">▶ kubectl port-forward pod/mypod 8888:80
Forwarding from 127.0.0.1:8888 -&gt; <span style="color:#ae81ff">80</span>
Forwarding from <span style="color:#f92672">[</span>::1<span style="color:#f92672">]</span>:8888 -&gt; <span style="color:#ae81ff">80</span>
</code></pre></div><p>Ahora podemos visitar  <code>localhost:8080</code> y ver el mensaje de bienvenida de Nginx</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">▶ curl http://localhost:8888
&lt;!DOCTYPE html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;title&gt;Welcome to nginx!&lt;/title&gt;
&lt;style&gt;
html <span style="color:#f92672">{</span> color-scheme: light dark; <span style="color:#f92672">}</span>
body <span style="color:#f92672">{</span> width: 35em; margin: <span style="color:#ae81ff">0</span> auto;
font-family: Tahoma, Verdana, Arial, sans-serif; <span style="color:#f92672">}</span>
&lt;/style&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;Welcome to nginx!&lt;/h1&gt;
&lt;p&gt;If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.&lt;/p&gt;

&lt;p&gt;For online documentation and support please refer to
&lt;a href<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;http://nginx.org/&#34;</span>&gt;nginx.org&lt;/a&gt;.&lt;br/&gt;
Commercial support is available at
&lt;a href<span style="color:#f92672">=</span><span style="color:#e6db74">&#34;http://nginx.com/&#34;</span>&gt;nginx.com&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Thank you <span style="color:#66d9ef">for</span> using nginx.&lt;/em&gt;&lt;/p&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre></div><p>Con esto vimos algunos conceptos básicos y creamos nuestro primer pod :D.</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p><a href="https://kubernetes.io/docs/concepts/overview/components/">https://kubernetes.io/docs/concepts/overview/components/</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p><a href="https://kubernetes.io/docs/concepts/overview/working-with-objects/">https://kubernetes.io/docs/concepts/overview/working-with-objects/</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p><a href="https://kind.sigs.k8s.io/">https://kind.sigs.k8s.io/</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:4" role="doc-endnote">
<p><a href="https://minikube.sigs.k8s.io/docs/">https://minikube.sigs.k8s.io/docs/</a>&#160;<a href="#fnref:4" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:5" role="doc-endnote">
<p><a href="https://docs.docker.com/desktop/features/kubernetes/">https://docs.docker.com/desktop/features/kubernetes/</a>&#160;<a href="#fnref:5" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:6" role="doc-endnote">
<p><a href="https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/">https://kubernetes.io/docs/tasks/tools/install-kubectl-linux/</a>&#160;<a href="#fnref:6" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content:encoded>
    </item>
    
    <item>
      <title>Pipelines de CI en proyectos de Elixir</title>
      <link>https://sergiovp.dev/blog/pipelines-de-ci-en-proyectos-de-elixir/</link>
      <pubDate>Mon, 29 Jul 2024 18:00:00 -0600</pubDate>
      
      <guid>https://sergiovp.dev/blog/pipelines-de-ci-en-proyectos-de-elixir/</guid>
      <description>Una tarea para mantener la calidad de nuestro código Elixir</description>
      <content:encoded><![CDATA[<p>La integración continua (CI) es una de las prácticas más importantes que trajo consigo la revolución DevOps y su esfuerzo por reducir los ciclos de retroalimentación cuando desarrollamos software.</p>
<p>Para esto los pipelines con verificaciones de calidad y conformidad ejecutados por un servidor de integración continua se volvieron una parte fundamental. Al permitirnos verificar, al menos hasta cierto punto que los cambios que estamos integrando cumplen con los criterios establecidos.</p>
<p>Elixir tiene lo propio y hoy en día contamos con diversas herramientas que nos permiten automatizar diversos aspectos de nuestro código, ponerlos en un pipeline y ejecutarlos automáticamente en cada confirmación.</p>
<p>Por otro lado, me gustaría aprovechar la oportunidad para poner todo este pipeline en un solo script, de forma que podamos utilizarlo fácilmente tanto en desarrollo como en el servidor de CI.</p>
<p>Lo primero es configurar la opción  <code>preferred_envs</code> en nuestro archivo <code>mix.exs</code> para no tener que incluir <code>MIX_ENV=test</code> en cada tarea.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">def</span> cli <span style="color:#66d9ef">do</span>
  [
    <span style="color:#e6db74">preferred_envs</span>: [<span style="color:#e6db74">ci</span>: <span style="color:#e6db74">:test</span>]
  ]
<span style="color:#66d9ef">end</span>
</code></pre></div><p>Ademas de incluir un alias en el mismo archivo <code>mix.exs</code> para ejecutar nuestra tarea mediante <code>mix ci</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defp</span> aliases <span style="color:#66d9ef">do</span>
  [
    <span style="color:#75715e"># ... otros aliases</span>
    <span style="color:#e6db74">ci</span>: [<span style="color:#e6db74">&#34;CI&#34;</span>]
  ]
<span style="color:#66d9ef">end</span>
</code></pre></div><p>Ahora creamos nuestro archivo <code>lib/mix/tasks/ci.ex</code> y comenzamos a escribir nuestra tarea.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">Mix.Tasks.CI</span> <span style="color:#66d9ef">do</span>
  <span style="color:#a6e22e">@moduledoc</span> <span style="color:#e6db74">&#34;&#34;&#34;
</span><span style="color:#e6db74">  Run all the checks and tests for the project
</span><span style="color:#e6db74">  &#34;&#34;&#34;</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Mix.Task</span>

  <span style="color:#a6e22e">@preferred_cli_env</span> <span style="color:#e6db74">:test</span>

  <span style="color:#a6e22e">@impl</span> <span style="color:#a6e22e">Mix.Task</span>
  <span style="color:#66d9ef">def</span> run(_args) <span style="color:#66d9ef">do</span>
   ...
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><h2 id="verificaciones-de-las-dependencias">Verificaciones de las dependencias</h2>
<p>El primer paso de nuestro pipeline es obtener las dependencias.</p>
<p>Podemos verificar nuestro archivo <code>mix.exs</code> por dependencias que han sido retiradas utilizando la tarea <code>mix hex.audit</code>, de forma que podamos reemplazarlas.</p>
<p>Despues, por defecto, contamos con la tarea <code>deps.unlock --check-unused</code> que verifica que no incluyamos dependencias en nuestro archivo <code>mix.exs</code> que no estén siendo utilizadas.</p>
<p>Además, la dependencia <a href="https://github.com/mirego/mix_audit">mix_audit</a> nos permite escanear las dependencias en busca de vulnerabilidades reportadas con la tarea <code>mix deps.audit</code>.</p>
<p>De forma que podemos incluirlas en nuestro archivo de CI:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">Mix.Tasks.CI</span> <span style="color:#66d9ef">do</span>
  <span style="color:#a6e22e">@moduledoc</span> <span style="color:#e6db74">&#34;&#34;&#34;
</span><span style="color:#e6db74">  Run all the checks and tests for the project
</span><span style="color:#e6db74">  &#34;&#34;&#34;</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Mix.Task</span>

  <span style="color:#a6e22e">@preferred_cli_env</span> <span style="color:#e6db74">:test</span>

  <span style="color:#a6e22e">@impl</span> <span style="color:#a6e22e">Mix.Task</span>
  <span style="color:#66d9ef">def</span> run(_args) <span style="color:#66d9ef">do</span>
    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.get&#34;</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;cmd&#34;</span>, [<span style="color:#e6db74">&#34;MIX_ENV=dev&#34;</span>, <span style="color:#e6db74">&#34;mix hex.audit&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.unlock&#34;</span>, [<span style="color:#e6db74">&#34;--check-unused&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.audit&#34;</span>, [<span style="color:#e6db74">&#34;--format&#34;</span>, <span style="color:#e6db74">&#34;human&#34;</span>])
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><h2 id="verificaciones-del-compilador">Verificaciones del compilador</h2>
<p>Elixir incluye un argumento muy útil llamado <code>--warnings-as-errors</code>. Lo que nos permite tratar las advertencias del compilador como errores, por lo que no nos permitirá avanzar hasta que sean resueltas.</p>
<p>Aunque personalmente me gusta activar esta opción dentro del archivo <code>mix.exs</code>, haciendo que esto funcione todo el tiempo. Algunas personas consideran esta opción algo intrusiva<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>, ya que suele crear fricción durante el desarrollo al detener el flujo, por ejemplo por olvidar un <code>_</code> en una variable que no usamos en ese momento.</p>
<p>Aunque existe una incomodidad inicial, encuentro que esta fricción desaparece después de un corto periodo de adaptación, volviéndonos más disciplinados al escribir código. Sin embargo, considero indispensable que esta opción esté activa (al menos) en el servidor de CI.</p>
<p>De otra forma, nuestra consola estará plagada de advertencias que dificultan la lectura de lo que es realmente importante.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">Mix.Tasks.CI</span> <span style="color:#66d9ef">do</span>
  <span style="color:#a6e22e">@moduledoc</span> <span style="color:#e6db74">&#34;&#34;&#34;
</span><span style="color:#e6db74">  Run all the checks and tests for the project
</span><span style="color:#e6db74">  &#34;&#34;&#34;</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Mix.Task</span>

  <span style="color:#a6e22e">@preferred_cli_env</span> <span style="color:#e6db74">:test</span>

  <span style="color:#a6e22e">@impl</span> <span style="color:#a6e22e">Mix.Task</span>
  <span style="color:#66d9ef">def</span> run(_args) <span style="color:#66d9ef">do</span>
    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.get&#34;</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;cmd&#34;</span>, [<span style="color:#e6db74">&#34;MIX_ENV=dev&#34;</span>, <span style="color:#e6db74">&#34;mix hex.audit&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.unlock&#34;</span>, [<span style="color:#e6db74">&#34;--check-unused&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.audit&#34;</span>, [<span style="color:#e6db74">&#34;--format&#34;</span>, <span style="color:#e6db74">&#34;human&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;compile&#34;</span>, [<span style="color:#e6db74">&#34;--warnings-as-errors&#34;</span>])
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><h2 id="verificaciones-de-formato">Verificaciones de formato</h2>
<p>Elixir cuenta con una herramienta <code>mix format</code> que nos permite formatear nuestro código de forma automática. Sin embargo, no es raro que se nos olvide formatear nuestro código antes de confirmar, por lo que podemos incluirlo en nuestro script.</p>
<p>Por otro lado, también podemos incluir <a href="https://github.com/rrrene/credo">Credo</a>, una herramienta de análisis estático que nos permite verificar la calidad de nuestro código. Cuenta
con muchas reglas que podemos configurar o desactivar, incluso podemos crear nuestras propias reglas. Aunque no todas vienen activadas por defecto, por lo que vale mucho la pena revisar al documentación a fondo.</p>
<p>Ademas credo cuanta con un modo estricto que podemos activar con la opción <code>--strict</code> que nos permite ver todas las reglas sin importar su prioridad.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">Mix.Tasks.CI</span> <span style="color:#66d9ef">do</span>
  <span style="color:#a6e22e">@moduledoc</span> <span style="color:#e6db74">&#34;&#34;&#34;
</span><span style="color:#e6db74">  Run all the checks and tests for the project
</span><span style="color:#e6db74">  &#34;&#34;&#34;</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Mix.Task</span>

  <span style="color:#a6e22e">@preferred_cli_env</span> <span style="color:#e6db74">:test</span>

  <span style="color:#a6e22e">@impl</span> <span style="color:#a6e22e">Mix.Task</span>
  <span style="color:#66d9ef">def</span> run(_args) <span style="color:#66d9ef">do</span>
    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.get&#34;</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;cmd&#34;</span>, [<span style="color:#e6db74">&#34;MIX_ENV=dev&#34;</span>, <span style="color:#e6db74">&#34;mix hex.audit&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.unlock&#34;</span>, [<span style="color:#e6db74">&#34;--check-unused&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.audit&#34;</span>, [<span style="color:#e6db74">&#34;--format&#34;</span>, <span style="color:#e6db74">&#34;human&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;compile&#34;</span>, [<span style="color:#e6db74">&#34;--warnings-as-errors&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;format&#34;</span>, [<span style="color:#e6db74">&#34;--check-formatted&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;credo&#34;</span>, [<span style="color:#e6db74">&#34;--strict&#34;</span>])
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><h2 id="verificaciones-de-tipado">Verificaciones de tipado</h2>
<p>Elixir es un lenguaje de tipado dinámico, por lo que si bien tenemos opciones como las anotaciones de tipos y las especificaciones de tipos, estas no son verificadas por el compilador. Sin embargo, podemos utilizar <a href="https://github.com/jeremyjh/dialyxir">dialyxir</a> o alguna de sus alternativas<sup id="fnref:2"><a href="#fn:2" class="footnote-ref" role="doc-noteref">2</a></sup> para verificar el tipado de nuestro código.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">Mix.Tasks.CI</span> <span style="color:#66d9ef">do</span>
  <span style="color:#a6e22e">@moduledoc</span> <span style="color:#e6db74">&#34;&#34;&#34;
</span><span style="color:#e6db74">  Run all the checks and tests for the project
</span><span style="color:#e6db74">  &#34;&#34;&#34;</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Mix.Task</span>

  <span style="color:#a6e22e">@preferred_cli_env</span> <span style="color:#e6db74">:test</span>

  <span style="color:#a6e22e">@impl</span> <span style="color:#a6e22e">Mix.Task</span>
  <span style="color:#66d9ef">def</span> run(_args) <span style="color:#66d9ef">do</span>
    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.get&#34;</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;cmd&#34;</span>, [<span style="color:#e6db74">&#34;MIX_ENV=dev&#34;</span>, <span style="color:#e6db74">&#34;mix hex.audit&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.unlock&#34;</span>, [<span style="color:#e6db74">&#34;--check-unused&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.audit&#34;</span>, [<span style="color:#e6db74">&#34;--format&#34;</span>, <span style="color:#e6db74">&#34;human&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;compile&#34;</span>, [<span style="color:#e6db74">&#34;--warnings-as-errors&#34;</span>]

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;format&#34;</span>, [<span style="color:#e6db74">&#34;--check-formatted&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;credo&#34;</span>, [<span style="color:#e6db74">&#34;--strict&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;dialyzer&#34;</span>)
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><h2 id="verificaciones-de-migraciones">Verificaciones de migraciones</h2>
<p>La biblioteca <a href="https://github.com/artur-sulej/excellent_migrations">excellent_migrations</a> nos permite revisar las migraciones en busca de operaciones que puedan ser problemáticas.
Creo que es una herramienta muy útil para evitar problemas en producción.</p>
<p>Otra verificación útil que podemos agregar es que las migraciones sean reversibles. Como menciona Saša Jurić<sup id="fnref:3"><a href="#fn:3" class="footnote-ref" role="doc-noteref">3</a></sup></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">Mix.Tasks.CI</span> <span style="color:#66d9ef">do</span>
  <span style="color:#a6e22e">@moduledoc</span> <span style="color:#e6db74">&#34;&#34;&#34;
</span><span style="color:#e6db74">  Run all the checks and tests for the project
</span><span style="color:#e6db74">  &#34;&#34;&#34;</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Mix.Task</span>

  <span style="color:#a6e22e">@preferred_cli_env</span> <span style="color:#e6db74">:test</span>

  <span style="color:#a6e22e">@impl</span> <span style="color:#a6e22e">Mix.Task</span>
  <span style="color:#66d9ef">def</span> run(_args) <span style="color:#66d9ef">do</span>
    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.get&#34;</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;cmd&#34;</span>, [<span style="color:#e6db74">&#34;MIX_ENV=dev&#34;</span>, <span style="color:#e6db74">&#34;mix hex.audit&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.unlock&#34;</span>, [<span style="color:#e6db74">&#34;--check-unused&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.audit&#34;</span>, [<span style="color:#e6db74">&#34;--format&#34;</span>, <span style="color:#e6db74">&#34;human&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;compile&#34;</span>, [<span style="color:#e6db74">&#34;--warnings-as-errors&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;format&#34;</span>, [<span style="color:#e6db74">&#34;--check-formatted&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;credo&#34;</span>, [<span style="color:#e6db74">&#34;--strict&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;dialyzer&#34;</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;ecto.create&#34;</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;excellent_migrations.migrate&#34;</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;ecto.rollback&#34;</span>, [<span style="color:#e6db74">&#34;--all&#34;</span>])
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><h2 id="verificaciones-de-pruebas">Verificaciones de pruebas</h2>
<p>Después de las verificaciones anteriores, ahora podemos ejecutar nuestras pruebas. Adicionalmente, podemos incluir la opción <code>--cover</code> para verificar la cobertura de nuestras pruebas y la opción <code>--warnings-as-errors</code> para tratar las advertencias como errores.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">Mix.Tasks.CI</span> <span style="color:#66d9ef">do</span>
  <span style="color:#a6e22e">@moduledoc</span> <span style="color:#e6db74">&#34;&#34;&#34;
</span><span style="color:#e6db74">  Run all the checks and tests for the project
</span><span style="color:#e6db74">  &#34;&#34;&#34;</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Mix.Task</span>

  <span style="color:#a6e22e">@preferred_cli_env</span> <span style="color:#e6db74">:test</span>

  <span style="color:#f92672">require</span> <span style="color:#a6e22e">Logger</span>

  <span style="color:#a6e22e">@impl</span> <span style="color:#a6e22e">Mix.Task</span>
  <span style="color:#66d9ef">def</span> run(_args) <span style="color:#66d9ef">do</span>
    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.get&#34;</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;cmd&#34;</span>, [<span style="color:#e6db74">&#34;MIX_ENV=dev&#34;</span>, <span style="color:#e6db74">&#34;mix hex.audit&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.unlock&#34;</span>, [<span style="color:#e6db74">&#34;--check-unused&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.audit&#34;</span>, [<span style="color:#e6db74">&#34;--format&#34;</span>, <span style="color:#e6db74">&#34;human&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;compile&#34;</span>, [<span style="color:#e6db74">&#34;--warnings-as-errors&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;format&#34;</span>, [<span style="color:#e6db74">&#34;--check-formatted&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;credo&#34;</span>, [<span style="color:#e6db74">&#34;--strict&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;dialyzer&#34;</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;ecto.create&#34;</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;excellent_migrations.migrate&#34;</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;ecto.rollback&#34;</span>, [<span style="color:#e6db74">&#34;--all&#34;</span>])

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;test&#34;</span>, [<span style="color:#e6db74">&#34;--cover&#34;</span>, <span style="color:#e6db74">&#34;--warnings-as-errors&#34;</span>])
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>También podemos incluir algunos mensajes para proveer algo de información sobre lo que está sucediendo.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">Mix.Tasks.CI</span> <span style="color:#66d9ef">do</span>
  <span style="color:#a6e22e">@moduledoc</span> <span style="color:#e6db74">&#34;&#34;&#34;
</span><span style="color:#e6db74">  Run all the checks and tests for the project
</span><span style="color:#e6db74">  &#34;&#34;&#34;</span>

  <span style="color:#a6e22e">@preferred_cli_env</span> <span style="color:#e6db74">:test</span>

  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Mix.Task</span>

  <span style="color:#f92672">require</span> <span style="color:#a6e22e">Logger</span>

  <span style="color:#a6e22e">@impl</span> <span style="color:#a6e22e">Mix.Task</span>
  <span style="color:#66d9ef">def</span> run(_args) <span style="color:#66d9ef">do</span>
    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;Getting dependencies&#34;</span>, <span style="color:#e6db74">ansi_color</span>: <span style="color:#e6db74">:cyan</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.get&#34;</span>)

     <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;Checking for retired dependencies&#34;</span>, <span style="color:#e6db74">ansi_color</span>: <span style="color:#e6db74">:cyan</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;cmd&#34;</span>, [<span style="color:#e6db74">&#34;MIX_ENV=dev&#34;</span>, <span style="color:#e6db74">&#34;mix hex.audit&#34;</span>])

    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;Checking unused dependencies&#34;</span>, <span style="color:#e6db74">ansi_color</span>: <span style="color:#e6db74">:cyan</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.unlock&#34;</span>, [<span style="color:#e6db74">&#34;--check-unused&#34;</span>])

    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;Checking for reported vulnerabilities&#34;</span>, <span style="color:#e6db74">ansi_color</span>: <span style="color:#e6db74">:cyan</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;deps.audit&#34;</span>, [<span style="color:#e6db74">&#34;--format&#34;</span>, <span style="color:#e6db74">&#34;human&#34;</span>])

    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;Compiling code&#34;</span>, <span style="color:#e6db74">ansi_color</span>: <span style="color:#e6db74">:cyan</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;compile&#34;</span>, [<span style="color:#e6db74">&#34;--warnings-as-errors&#34;</span>])

    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;checking formatting&#34;</span>, <span style="color:#e6db74">ansi_color</span>: <span style="color:#e6db74">:cyan</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;format&#34;</span>, [<span style="color:#e6db74">&#34;--check-formatted&#34;</span>])

    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;Running credo&#34;</span>, <span style="color:#e6db74">ansi_color</span>: <span style="color:#e6db74">:cyan</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;credo&#34;</span>, [<span style="color:#e6db74">&#34;--strict&#34;</span>])

    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;Running dialyzer&#34;</span>, <span style="color:#e6db74">ansi_color</span>: <span style="color:#e6db74">:cyan</span>)
    
    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;dialyzer&#34;</span>)

    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;Creating test database&#34;</span>, <span style="color:#e6db74">ansi_color</span>: <span style="color:#e6db74">:cyan</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;ecto.create&#34;</span>)

    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;Checking for migrations&#34;</span>, <span style="color:#e6db74">ansi_color</span>: <span style="color:#e6db74">:cyan</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;excellent_migrations.migrate&#34;</span>)

    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;Checking for reversibility of migrations&#34;</span>, <span style="color:#e6db74">ansi_color</span>: <span style="color:#e6db74">:cyan</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;ecto.rollback&#34;</span>, [<span style="color:#e6db74">&#34;--all&#34;</span>])

    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>info(<span style="color:#e6db74">&#34;Running tests&#34;</span>, <span style="color:#e6db74">ansi_color</span>: <span style="color:#e6db74">:cyan</span>)

    <span style="color:#a6e22e">Mix.Task</span><span style="color:#f92672">.</span>run(<span style="color:#e6db74">&#34;test&#34;</span>, [<span style="color:#e6db74">&#34;--cover&#34;</span>, <span style="color:#e6db74">&#34;--warnings-as-errors&#34;</span>])
<span style="color:#66d9ef">end</span>
</code></pre></div><p>Esta tarea es una referencia, dependiendo del proyecto o el equipo podemos agregar o eliminar verificaciones. Otro aspecto es que probablemente llegue un punto en el que ejecutar todo el pipeline en local sea demasiado lento, por lo que podemos crear una tarea destinada a ser ejecutada en local con las verificaciones más importantes y dejar el pipeline completo para el servidor de CI.</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p><a href="https://blog.rentpathcode.com/elixir-warnings-as-errors-sometimes-f5a8d2c96b15">Elixir Warnings as Errors…Sometimes</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:2" role="doc-endnote">
<p><a href="https://fly.io/phoenix-files/adding-dialyzer-without-the-pain/">Adding Dialyzer without the Pain</a>&#160;<a href="#fnref:2" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
<li id="fn:3" role="doc-endnote">
<p><a href="https://medium.com/very-big-things/towards-maintainable-elixir-the-development-process-205ee257c109">Towards Maintainable Elixir: The Development Process</a>&#160;<a href="#fnref:3" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content:encoded>
    </item>
    
    <item>
      <title>Arquitectura hexagonal en Phoenix</title>
      <link>https://sergiovp.dev/blog/arquitectura-hexagonal-en-phoenix/</link>
      <pubDate>Sun, 07 Apr 2024 19:09:34 -0600</pubDate>
      
      <guid>https://sergiovp.dev/blog/arquitectura-hexagonal-en-phoenix/</guid>
      <description>Ideas sobre arquitectura hexagonal en aplicaciones con Elixir/Phoenix y Aplicaciones Umbrella</description>
      <content:encoded><![CDATA[<p>La arquitectura hexagonal o de puertos y adaptadores consta de 3 capas:</p>
<ul>
<li><strong>Infraestructura:</strong> Lo que conecta nuestra capa de aplicación con la entrada y salida de información: base de datos, HTTP, message&rsquo;s queues, etc.</li>
<li><strong>Aplicación:</strong> Donde residen nuestros casos de uso</li>
<li><strong>Dominio:</strong> Aquí encontramos cosas como nuestras entidades</li>
</ul>
<h2 id="por-que-puertos-y-adaptadores">Por que puertos y adaptadores?</h2>
<p>Podemos decir que los puertos son las interfaces de entrada y salida de nuestra aplicación. Una API JSON, un consumidor de un Message Queue, etc.</p>
<p>Por su parte, los adaptadores conectan nuestra lógica de negocio para que pueda ser consumida o ejecutada por otros actores (usuarios o sistemas).</p>
<p>Digamos que un controlador es un adaptador.</p>
<p>¿Cómo se reflejan estas capas en una aplicación típica de Phoenix?</p>
<p><img loading="lazy" src="https://res.cloudinary.com/escribocodigo/image/upload/v1712533452/SCR-20240407-nbcf_xfqao0.png" alt="Default Phoenix directory structure"  />
</p>
<p>Nuestros <em>schemas</em> como el de <em>user</em> son parte del dominio.</p>
<p>Mientras que los <em>context modules</em> como el de <em>accounts</em> podríamos decir que forman parte de la capa de aplicación.</p>
<p>Finalmente, lo que está contenido en <code>app_web/</code> formaría parte de la capa de infraestructura, al igual que el modulo <em>repo</em> y el <em>mailer</em>.</p>
<h2 id="capa-de-dominio">Capa de dominio</h2>
<p>¿Cómo modelamos la capa de dominio?</p>
<p>Respuesta corta: Usamos Domain-Driven Design (DDD) para descubrir los subdominios del negocio y diseñar bounded contexts. Chris McCord abordo este tema en su keynote en la ElixirConf 2017<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup>:</p>
<pre tabindex="0"><code>app/
├─ ecommerce/
│  ├─ accounts/
│  │  ├─ schemas/
│  │  │  ├─ user.ex
│  │  │  ├─ user_token.ex
│  │  ├─ accounts.ex
│  ├─ catalog/
│  │  ├─ schemas/
│  │  │  ├─ category
│  │  │  ├─ product.ex
│  │  ├─ catalog.ex
│  ├─ shopping_cart/
│  │  ├─ schemas/
│  │  │  ├─ cart.ex
│  │  │  ├─ cart_item.ex
│  │  ├─ shopping_cart.ex
├─ backoffice/
│  ├─ support/
│  │  ├─ schemas/
│  │  │  ├─ ticket.ex
│  │  ├─ tickets.ex
├─ app_web/
│  ├─ controllers/
│  │  ├─ ecommerce/
│  │  │  ├─ product_controller.ex
│  │  │  ├─ category_controller.ex
│  │  ├─ backoffice/
│  │  │  ├─ ticket_controller.ex
│  │  │  ├─ product_controller.ex
│  │  │  ├─ category_controller.ex
├─ README.md
</code></pre><p>En este ejemplo hay dos grandes contextos: <code>ecommerce</code> y <code>backoffice</code>. Pero eso no significa que no puedan surgir más, como <code>marketing</code>, <code>ventas</code>, etc.</p>
<p>A su vez, tanto <code>ecommerce</code> como <code>backoffice</code> contienen el directorio <code>schemas/</code> (dominio)  y un <code>context module</code> (aplicación): API&rsquo;s públicas para interactuar con los esquemas.</p>
<p>Esta separación en contextos nos permite aumentar la cohesión de nuestro código. Al tener todos los conceptos relacionados cerca: usuario y token, producto y categoría, etc. Mientras que como veremos más adelante, tener una capa de aplicación nos permite reducir el acoplamiento, al separar los casos de uso de quien los llama, sea una interfaz de usuario o una interfaz para sistemas externo.</p>
<p>Una pregunta que puede surgir es: ¿Por qué tenemos un <code>product_controller.ex</code> tanto en  <code>ecommerce</code> como en <code>backoffice</code>?</p>
<p>Porque una entidad no necesariamente significan lo mismo en distintos contextos.  Un producto puede diferir en algunas características para un cliente en <code>ecommerce</code> que para un usuario interno en el <code>backoffice</code>.</p>
<p>Un <code>lead</code> puede ser apenas un registro en un formulario para marketing, mientras que para ventas es alguien que paso por el embudo de ventas, conteniendo mucha más información.</p>
<h2 id="capa-de-aplicación">Capa de aplicación</h2>
<p>Tener una capa de aplicación nos permite tener límites bien definidos acerca de cómo acceder a la lógica de negocio. Registro, Login, etc.</p>
<p>Los generadores de Phoenix crean context modules que podríamos considerar como nuestra capa de aplicación. Por ejemplo <code>Accounts</code>.</p>
<pre tabindex="0"><code>app/
├─ ecommerce/
│  ├─ accounts/
│  │  ├─ accounts.ex
│  ├─ catalog/
│  │  ├─ catalog.ex
│  ├─ shopping_cart/
│  │  ├─ shopping_cart.ex
├─ backoffice/
│  ├─ support/
│  │  ├─ tickets.ex
├─ app_web/
├─ README.md
</code></pre><p>Son la API publica para interactuar con nuestros esquemas de ecto.</p>
<p>Si quisiéramos introducir nuevos casos de uso podemos hacer algo como lo siguiente.</p>
<p>Vamos a crear un nuevo directorio <code>services</code></p>
<pre tabindex="0"><code>app/
├─ ecommerce/
│  ├─ accounts/
│  │  ├─ services/
│  │  │  ├─ user_creator.ex
...
</code></pre><p><code>services</code> es basicamente un <a href="https://martinfowler.com/eaaCatalog/serviceLayer.html">service layer</a>:</p>
<blockquote>
<p>Defines an application&rsquo;s boundary with a layer of services that establishes a set of available operations and coordinates the application&rsquo;s response in each operation.</p>
</blockquote>
<p>Es una forma de encapsular nuestros casos de uso, en este caso crear un usuario.</p>
<p>El módulo <code>user_creator.ex</code> es el que define las operaciones y coordina la respuesta, siguiendo el patrón <code>ThingDoer-do_thing</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">App.Ecommerce.Accounts.UserCreator</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">alias</span> <span style="color:#a6e22e">App.Ecommerce.Accounts</span>
  <span style="color:#f92672">alias</span> <span style="color:#a6e22e">App.Ecommerce.Accounts.User</span>

  <span style="color:#f92672">alias</span> <span style="color:#a6e22e">App.Ecommerce.Accounts.ConfirmationEmailJob</span>
  <span style="color:#f92672">alias</span> <span style="color:#a6e22e">App.Ecommerce.Accounts.UserEmail</span>
  <span style="color:#f92672">alias</span> <span style="color:#a6e22e">App.Mailer</span>


  <span style="color:#66d9ef">def</span> create_user(email, password) <span style="color:#66d9ef">do</span>
    with {<span style="color:#e6db74">:ok</span>, user} <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">Accounts</span><span style="color:#f92672">.</span>create_user(email, password),
         {<span style="color:#e6db74">:ok</span>, _job} <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">ConfirmationEmailJob</span><span style="color:#f92672">.</span>enqueue(user) <span style="color:#66d9ef">do</span>
      {<span style="color:#e6db74">:ok</span>, user}
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>

  <span style="color:#66d9ef">def</span> send_confirmation_email(%<span style="color:#a6e22e">User</span>{} <span style="color:#f92672">=</span> user) <span style="color:#66d9ef">do</span>
    email <span style="color:#f92672">=</span> <span style="color:#a6e22e">UserEmail</span><span style="color:#f92672">.</span>confirmation(user)

    <span style="color:#a6e22e">Mailer</span><span style="color:#f92672">.</span>deliver(email)
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>El patrón <code>ThingDoer-do_thing</code> nos permite evitar funciones genéricas y ambiguas como: <code>call</code>, <code>run</code>, o <code>execute</code>, haciendo que nuestro código comunique su comportamiento.  Cabe mencionar que el módulo <code>UserCreator</code> no contiene lógica en sí mismo, sino que orquesta los módulos necesarios para cumplir con el caso de uso.</p>
<p>Otro beneficio del <em>service layer</em> es que proporciona una <a href="https://martinfowler.com/bliki/LegacySeam.html">costura</a>:</p>
<blockquote>
<p>a seam is a place where you can alter behavior in your program without editing in that place</p>
</blockquote>
<p>Si nuestra lógica de negocio acerca de crear usuarios cambia, dichos cambios no tendrán nada que ver con el controlador o el background job.</p>
<h2 id="capa-de-infraestructura">Capa de infraestructura</h2>
<p>La responsabilidad de la capa de infraestructura es validar y normalizar la información que se recibe, de forma que la aplicación y el dominio puedan trabajar con ella.</p>
<p>Como nuestra capa de aplicación define las operaciones disponibles, estas se pueden reutilizar en distintos <em>adaptadores</em>, lo que permite que nuestra aplicación reciba información y responda en el formato adecuado sin importar de donde provenga, ya sea una API JSON, un background job, un Liveview, un evento de dominio, message queues, etc.</p>
<p>Infraestructura es otro nombre para lo que Saša Jurić llama <em>Interfaz</em> en su serie de artículos <a href="https://medium.com/very-big-things/towards-maintainable-elixir-the-core-and-the-interface-c267f0da43">Towards Maintainable Elixir: The Core and the Interface</a>.</p>
<p>En la sección anterior agregamos algunos modulos nuevos y sus respectivos directorios:</p>
<pre tabindex="0"><code>app/
├─ ecommerce/
│  ├─ accounts/
│  │  ├─ jobs/
│  │  │  ├─ confirmation_email_job.ex
│  │  ├─ emails/
│  │  │  ├─ user_email.ex
...
</code></pre><p><code>jobs</code> Es un directorio para definir background jobs.</p>
<p>El módulo <code>confirmation_email_job.ex</code> es un background job para agregar confiabilidad al envío del email de confirmación.</p>
<p><code>emails</code> Bibliotecas como <a href="https://github.com/swoosh/phoenix_swoosh">swoosh</a> nos permiten construir emails mediante funciones, de esta forma el módulo <code>user_email.ex</code> puede definir distintos tipos de emails relacionados con el usuario.</p>
<p>Por otro lado, los <code>backgrounds jobs</code> son parte de nuestra capa de infraestructura, por lo que evitamos definir lógica de negocio en ellos. En su lugar se comunican con la capa de aplicación por medio de nuestro service layer :D</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">App.Accounts.ConfirmationEmailJob</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Oban.Worker</span>, <span style="color:#e6db74">queue</span>: <span style="color:#e6db74">:default</span>

  <span style="color:#f92672">alias</span> __MODULE__
  <span style="color:#f92672">alias</span> <span style="color:#a6e22e">App.Ecommerce.Accounts</span>
  <span style="color:#f92672">alias</span> <span style="color:#a6e22e">App.Ecommerce.Accounts.UserCreator</span>

  <span style="color:#66d9ef">def</span> enqueue(%{<span style="color:#e6db74">id</span>: id} <span style="color:#f92672">=</span> args) <span style="color:#66d9ef">do</span>
    args
    <span style="color:#f92672">|&gt;</span> <span style="color:#a6e22e">ConfirmationEmailJob</span><span style="color:#f92672">.</span>new()
    <span style="color:#f92672">|&gt;</span> <span style="color:#a6e22e">Oban</span><span style="color:#f92672">.</span>insert()
  <span style="color:#66d9ef">end</span>

  <span style="color:#a6e22e">@impl</span> <span style="color:#a6e22e">Oban.Worker</span>
  <span style="color:#66d9ef">def</span> perform(%<span style="color:#a6e22e">Oban.Job</span>{<span style="color:#e6db74">args</span>: %{<span style="color:#e6db74">&#34;id&#34;</span> <span style="color:#f92672">=&gt;</span> user_id} <span style="color:#f92672">=</span> args}) <span style="color:#66d9ef">do</span>
    user <span style="color:#f92672">=</span> <span style="color:#a6e22e">Accounts</span><span style="color:#f92672">.</span>get_user!(user_id)

    <span style="color:#a6e22e">UserCreator</span><span style="color:#f92672">.</span>send_confirmation_email(user)
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>Supongamos que nuestra aplicación es SSR por lo que nuestras respuestas son en HTML:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">AppWeb.UserSchema</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Goal</span>

  defparams <span style="color:#e6db74">:new</span> <span style="color:#66d9ef">do</span>
    required <span style="color:#e6db74">:email</span>, <span style="color:#e6db74">:string</span>
    required <span style="color:#e6db74">:password</span>, <span style="color:#e6db74">:string</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>

<span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">AppWeb.Ecommerce.UserController</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">AppWeb</span>, <span style="color:#e6db74">:controller</span>

  <span style="color:#f92672">alias</span> <span style="color:#a6e22e">AppWeb.UserSchema</span>

  <span style="color:#f92672">alias</span> <span style="color:#a6e22e">App.Ecommerce.Accounts.UserCreator</span>

  <span style="color:#66d9ef">def</span> create(conn, %{<span style="color:#e6db74">&#34;user&#34;</span> <span style="color:#f92672">=&gt;</span> user_params}) <span style="color:#66d9ef">do</span>
    with {<span style="color:#e6db74">:ok</span>, attrs} <span style="color:#f92672">&lt;-</span> validate(<span style="color:#e6db74">:new</span>, user_params),
    {<span style="color:#e6db74">:ok</span>, user} <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">UserCreator</span><span style="color:#f92672">.</span>create_user(attrs<span style="color:#f92672">.</span>email, attrs<span style="color:#f92672">.</span>password) <span style="color:#66d9ef">do</span>
      {<span style="color:#e6db74">:ok</span>, user} <span style="color:#f92672">-&gt;</span>
        conn
        <span style="color:#f92672">|&gt;</span> put_flash(<span style="color:#e6db74">:info</span>, <span style="color:#e6db74">&#34;User created successfully.&#34;</span>)
        <span style="color:#f92672">|&gt;</span> redirect(<span style="color:#e6db74">to</span>: <span style="color:#e6db74">~p&#34;/users/</span><span style="color:#e6db74">#{</span>user<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)
    <span style="color:#66d9ef">else</span> <span style="color:#f92672">-&gt;</span>
      {<span style="color:#e6db74">:error</span>, %<span style="color:#a6e22e">Ecto.Changeset</span>{} <span style="color:#f92672">=</span> changeset} <span style="color:#f92672">-&gt;</span>
        render(conn, <span style="color:#e6db74">:new</span>, <span style="color:#e6db74">changeset</span>: changeset)
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>

</code></pre></div><p>Pero, ademas abrimos la posibilidad a que otras aplicaciones consuman nuestra lógica de crear usuarios mediante un API REST, donde nuestra respuesta es en formato JSON:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">AppWeb.UserSchema</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Goal</span>

  defparams <span style="color:#e6db74">:new</span> <span style="color:#66d9ef">do</span>
    required <span style="color:#e6db74">:email</span>, <span style="color:#e6db74">:string</span>
    required <span style="color:#e6db74">:password</span>, <span style="color:#e6db74">:string</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>

<span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">AppWeb.API.Ecommerce.UserController</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">AppWeb</span>, <span style="color:#e6db74">:controller</span>

  <span style="color:#f92672">alias</span> <span style="color:#a6e22e">AppWeb.UserSchema</span>
  <span style="color:#f92672">alias</span> <span style="color:#a6e22e">App.Ecommerce.Accounts.UserCreator</span>

  action_fallback <span style="color:#a6e22e">AppWeb.FallbackController</span>

  <span style="color:#66d9ef">def</span> create(conn, %{<span style="color:#e6db74">&#34;user&#34;</span> <span style="color:#f92672">=&gt;</span> user_params}) <span style="color:#66d9ef">do</span>
    with {<span style="color:#e6db74">:ok</span>, attrs} <span style="color:#f92672">&lt;-</span> validate(<span style="color:#e6db74">:new</span>, user_params),
    {<span style="color:#e6db74">:ok</span>, user} <span style="color:#f92672">&lt;-</span> <span style="color:#a6e22e">UserCreator</span><span style="color:#f92672">.</span>create_user(attrs<span style="color:#f92672">.</span>email, attrs<span style="color:#f92672">.</span>password) <span style="color:#66d9ef">do</span>
      conn
      <span style="color:#f92672">|&gt;</span> put_status(<span style="color:#e6db74">:created</span>)
      <span style="color:#f92672">|&gt;</span> put_resp_header(<span style="color:#e6db74">&#34;location&#34;</span>, <span style="color:#e6db74">~p&#34;/api/users/</span><span style="color:#e6db74">#{</span>user<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)
      <span style="color:#f92672">|&gt;</span> render(<span style="color:#e6db74">:show</span>, <span style="color:#e6db74">user</span>: user)
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>Creo que se entiende la idea ;). El punto de entrada a nuestra lógica de negocio es el mismo y el código al rededor es acerca de como responder adecuadamente al formato solicitado.</p>
<p>En estos ejemplos el controlador recibe un mapa que puede contener campos desconocidos o puede haber campos faltantes. Usando la biblioteca <a href="https://hex.pm/packages/goal">Goal</a> nos aseguramos que los campos que necesita nuestra capa de aplicación estén presentes y sean de un tipo correcto, además transforma las llaves a átomos :D.</p>
<p>Por lo que nuestro caso de uso no tendrá que lidiar con información invalida y como es convención en Phoenix, las reglas de negocio como que el email sea único y el password sea suficientemente largo se encuentran más cerca del dominio mediante Ecto Changesets.</p>
<p>Esto tiene la ventaja de que nos aseguramos que estas reglas se cumplan sin importar la interfaz de entrada.</p>
<p>Nótese que ninguno de los módulos contiene <code>Schemas</code>, <code>.Services</code>, <code>.Emails</code> o <code>.Jobs</code>, de la misma forma en que en Phoenix los controladores no llevan <code>.Controllers</code>.</p>
<p>Sería un poco raro ver un controlador <code>App.Ecommerce.Controllers.UserController</code>, de la misma forma evitamos cosas como <code>App.Accounts.Jobs.ConfirmationEmailJob</code>.</p>
<h2 id="otros-menesteres">Otros menesteres</h2>
<p>Hay algunos conceptos como la autorización que pueden ser transversales a nuestra aplicación.</p>
<pre tabindex="0"><code class="language-ascii" data-lang="ascii">app/
├─ ecommerce/
├─ backoffice/
│─ access_control.ex
...
</code></pre><p>Podemos promover ese tipo de módulos a nuestra &ldquo;raíz&rdquo;, dando a entender que abarca toda nuestra aplicación.</p>
<p>Otro caso común es cuando nuestros módulos,  como <code>Accounts</code> o <code>Catalog</code> comienzan a tener cada vez más métodos para cubrir distintos escenarios o para abarcar un escenario complejo como la búsqueda y filtrado de información.</p>
<p>Si nos vemos en la necesidad de recibir parámetros para personalizar nuestras consultas, valdría la pena separar toda esta lógica en módulos nuevos.</p>
<pre tabindex="0"><code class="language-ascii" data-lang="ascii">app/
├─ ecommerce/
│  ├─ catalog/
│  │  ├─ finders/
│  │  │  ├─ product_finder.ex
...
</code></pre><p>¿Un pipeline de Brodway? No hay problema :D</p>
<pre tabindex="0"><code class="language-ascii" data-lang="ascii">app/
├─ ecommerce/
│  ├─ catalog/
│  │  ├─ finders/
│  │  │  ├─ product_finder.ex
│  │  ├─ pipeplines/
│  │  │  ├─ profit_pipeline.ex
...
</code></pre><p>¿Un cliente para una API externa?</p>
<p>En este caso promovemos el directorio <code>http</code> al primer nivel, ya que puede no ser exclusivo de un contexto y forma parte de la infraestructura.</p>
<pre tabindex="0"><code class="language-ascii" data-lang="ascii">app/
├─ ecommerce/
│  ├─ catalog/
│  │  ├─ finders/
│  │  │  ├─ product_finder.ex
│  │  ├─ pipeplines/
│  │  │  ├─ profit_pipeline.ex
├─ http/
│  ├─ crm_api.ex
...
</code></pre><p>También podemos usar el conocido directorio <code>shared/</code>, donde también podríamos promover nuestro modulo de autorización:</p>
<pre tabindex="0"><code class="language-ascii" data-lang="ascii">app/
├─ ecommerce/
│  ├─ catalog/
│  │  ├─ finders/
│  │  │  ├─ product_finder.ex
│  │  ├─ pipeplines/
│  │  │  ├─ profit_pipeline.ex
├─ shared/
│  ├─ http/
│  │  ├─ crm_api.ex
</code></pre><p>Otra forma de seguir escalando nuestra estructura de directorios seria agrupar nuestros módulos por la capa a la que pertenecen:</p>
<pre tabindex="0"><code class="language-ascii" data-lang="ascii">app/
├─ ecommerce/
│  ├─ accounts/
│  │  ├─ domain/
│  │  │  ├─ schemas/
│  │  │  │  ├─ user.ex
│  │  │  │  ├─ user_token.ex
│  │  ├─ application/
│  │  │  ├─ accounts.ex
│  │  │  ├─ user_creator.ex
│  │  ├─ infrastructure/
│  │  │  ├─ jobs
│  │  │  │  ├─ confirmation_email_job.ex
│  │  │  ├─ emails
│  │  │  │  ├─ user_email.ex
├─ shared/
│  ├─ infrastructure/
│  │  ├─ http/
│  │  │  ├─ crm_api.ex
│  ├─ application/
│  │  ├─ access_control.ex
...
</code></pre><p>Una opción adicional cuando nuestros casos de uso crecen, es organizarlos por el tipo de operación que realizan:</p>
<pre tabindex="0"><code class="language-ascii" data-lang="ascii">app/
├─ ecommerce/
│  ├─ accounts/
│  │  ├─ application/
│  │  │  ├─ accounts.ex
│  │  │  ├─ create/
│  │  │  │  ├─ user_creator.ex
...
</code></pre><p>Dependiendo de nuestra aplicación, esto podría ser excesivo o necesario. Todo depende del contexto.</p>
<h2 id="umbrella-al-rescate">Umbrella al rescate</h2>
<p>Como hemos visto, una aplicación puede crecer rápidamente y salirse de control. Una solución común es separar una aplicación en múltiples servicios, algunos patrones con los que se decide como separar estas nuevas aplicaciones pueden ser por <a href="https://microservices.io/patterns/decomposition/decompose-by-subdomain.html">subdominio</a> o por <a href="https://microservices.io/patterns/decomposition/decompose-by-business-capability.html">business capability</a>.</p>
<p>Aunque tiene sus ventajas, construir servicios separados en repositorios separados conlleva nueva complejidad en cuanto al desarrollo y comunicación de los nuevos servicios.</p>
<p>Afortunadamente, Elixir cuenta con una herramienta llamada <a href="https://hexdocs.pm/elixir/dependencies-and-umbrella-projects.html">proyectos umbrella</a>. Algo así como una aplicación de aplicaciones.</p>
<pre tabindex="0"><code>my_app_umbrella/
├─ apps/
│  ├─ ecommerce/
│  │  ├─ accounts/
│  │  │  ├─ domain/
│  │  │  │  ├─ schemas/
│  │  │  │  │  ├─ user.ex
│  │  │  │  │  ├─ user_token.ex
│  │  │  ├─ application/
│  │  │  │  ├─ accounts.ex
│  │  │  │  ├─ create/
│  │  │  │  │  ├─ user_creator.ex
│  │  │  ├─ infrastructure/
│  │  │  │  ├─ jobs
│  │  │  │  │  ├─ confirmation_email_job.ex
│  │  │  │  ├─ emails
│  │  │  │  │  ├─ user_email.ex
│  │  ├─ catalog/
│  │  │  ├─ domain/
│  │  │  │  ├─ schemas/
│  │  │  │  │  ├─ category
│  │  │  │  │  ├─ product.ex
│  │  │  ├─ application/
│  │  │  │  ├─ find/
│  │  │  │  │  ├─ product_finder.ex
│  │  │  │  ├─ catalog.ex
│  │  │  ├─ infrastructure/
│  │  │  │  ├─ pipeplines/
│  │  │  │  │  ├─ profit_pipeline.ex
│  │  ├─ shopping_cart/
│  │  │  ├─ domain/
│  │  │  │  ├─ schemas/
│  │  │  │  │  ├─ cart.ex
│  │  │  │  │  ├─ cart_item.ex
│  │  │  ├─ application/
│  │  │  │  ├─ shopping_cart.ex
│  │  ├─ shared/
│  │  │  ├─ infrastructure/
│  │  │  │  ├─ http/
│  │  │  │  │  ├─ crm_api.ex
│  │  │  ├─ application/
│  │  │  │  ├─ access_control.ex
│  ├─ ecommerce_web/
│  │  ├─ schemas/
│  │  │  ├─ user_schema.ex
│  │  ├─ live/
│  │  ├─ controllers/
│  │  │  ├─ product_controller.ex
│  │  │  ├─ category_controller.ex
│  ├─ backoffice/
│  │  ├─ support/
│  │  │  ├─ domain/
│  │  │  │  ├─ schemas/
│  │  │  │  │  ├─ ticket.ex
│  │  │  ├─ application/
│  │  │  │  │  ├─ tickets.ex
│  ├─ backoffice_web/
│  │  ├─ schemas/
│  │  ├─ live/
│  │  ├─ controllers/
│  │  │  ├─ product_controller.ex
├─ README.md
</code></pre><p>Con un proyecto umbrella ahora tenemos distintas aplicaciones: <code>ecommerce</code>, <code>ecommerce_web</code>, <code>backoffice</code> y <code>backoffice_web</code>. Con esto eliminamos la necesidad de separar nuestra aplicación original en múltiples repositorios si se vuelve muy grande, lo que agregaría mucho trabajo para manejar y orquestar el desarrollo.</p>
<p>Los proyectos umbrella nos proveen algunas ventajas:</p>
<ul>
<li>Desarrollo monorepo</li>
<li>Puedes construir, probar y desplegar cada aplicación por separado si asi lo deseas.</li>
<li>Una aplicación puede tener disponible otra si esta define como una dependencia interna (dentro de la umbrella)</li>
</ul>
<p>Dado que comparten dependencias y la configuración de las mismas, es importante tomarlo en cuenta, ya que al no ser aplicaciones totalmente independientes pudiera causar conflictos.</p>
<p>¿Cuándo comenzar con una aplicación de Phoenix normal y cuando con una umbrella?</p>
<p>Si tu proyecto está empezando y no sabes a ciencia cierta hacia donde va o que tan grande puede ser, comienza simple, una aplicación de Phoenix será suficiente y con un poco de disciplina, promover directorios hacia su propio contexto o su propia app será sencillo.</p>
<p>Si lo que estás desarrollando es un producto con varios subdominios, aunque comiences con uno solo, pero esperas desarrollar el resto eventualmente. O cuando ese subdominio es considerablemente complejo (es un core subdomain) puedes comenzar con un proyecto umbrella.</p>
<h2 id="tu-aplicación-es-tuya-y-tú-sabes-de-qué-trata">Tu aplicación es tuya y tú sabes de qué trata</h2>
<p>Los frameworks suelen ofrecer una base para comenzar a organizar tu aplicación, sin embargo, no conocen muchos detalles que tú si, quien la desarrolla. No saben de qué trata, que tan grande puede ser que tan simple o compleja es la lógica de negocio. Por eso no pueden ofrecer una guía que sirva para todos los casos y para todos los tamaños.</p>
<p>Tú eres el dueño de tu aplicación y conoces mejor tus necesidades.  ¿No te gusta la idea de separar en <code>ecommerce</code> y <code>backoffice</code>? Bien, elimínalas, es tu aplicación :D.</p>
<p>¿No necesitas que tu estructura de directorios te diga a que capa pertenecen los módulos? Excelente!</p>
<p>El punto es que la arquitectura de nuestra aplicación eventualmente necesita evolucionar, pero rara vez esto sucede. Generalmente, se permanece con la arquitectura y estructura original, aunque esta ya no cumpla los requerimientos del producto.</p>
<p>Tener una heurística base nos permite evaluar opciones e implementar la que mejor se ajuste a nuestros requerimientos y  nuestra experiencia :D</p>
<p>Links de interes:</p>
<ul>
<li><a href="https://medium.com/very-big-things/towards-maintainable-elixir-the-core-and-the-interface-c267f0da43">https://medium.com/very-big-things/towards-maintainable-elixir-the-core-and-the-interface-c267f0da43</a></li>
<li><a href="https://ulisses.dev/elixir/2022/03/04/elixir-modules-files-directories-and-naming-conventions.html">https://ulisses.dev/elixir/2022/03/04/elixir-modules-files-directories-and-naming-conventions.html</a></li>
<li><a href="https://remote.com/blog/introducing-phx_gen_solid">https://remote.com/blog/introducing-phx_gen_solid</a></li>
<li><a href="https://martinfowler.com/eaaCatalog/serviceLayer.html">https://martinfowler.com/eaaCatalog/serviceLayer.html</a></li>
<li><a href="https://aaronrenner.io/2019/09/18/application-layering-a-pattern-for-extensible-elixir-application-design.html">https://aaronrenner.io/2019/09/18/application-layering-a-pattern-for-extensible-elixir-application-design.html</a></li>
</ul>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p><a href="https://www.youtube.com/watch?v=tMO28ar0lW8&amp;t=886s">https://www.youtube.com/watch?v=tMO28ar0lW8&amp;t=886s</a>&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content:encoded>
    </item>
    
    <item>
      <title>Formularios sin Changeset en Phoenix LiveView</title>
      <link>https://sergiovp.dev/blog/formularios-sin-changeset-en-phoenix-liveview/</link>
      <pubDate>Wed, 30 Aug 2023 23:09:34 -0600</pubDate>
      
      <guid>https://sergiovp.dev/blog/formularios-sin-changeset-en-phoenix-liveview/</guid>
      <description>Como crear formularios sin changeset en Phoenix LiveView</description>
      <content:encoded><![CDATA[<p>La forma estándar de crear <a href="https://hexdocs.pm/phoenix_live_view/form-bindings.html">formularios en LiveView</a> es utilizar el helper <code>to_form/1</code> pasándole una estructura changeset, por ejemplo:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir">to_form(<span style="color:#a6e22e">Accounts</span><span style="color:#f92672">.</span>change_user(%<span style="color:#a6e22e">User</span>{})))
</code></pre></div><p>Sin embargo, pueden surgir situaciones donde tengamos un formulario sin tener que respaldarlo con un changeset.</p>
<p>Porque saltarse el changeset?</p>
<p><code>Ecto.Changeset</code> es una herramienta para trabajar con información de distintas fuentes. Proporciona validaciones, castings y manejo de errores. Normalmente creamos formularios basados en un schema de Ecto, es decir que tienen una tabla en nuestra base de datos. Sin embargo, hay situaciones en las que generar un changeset puede ser excesivo o innecesario, ya sea basado en un schema o en su forma schemaless.</p>
<p>Si simplemente queremos capturar datos sin necesidad de validaciones complejas, es posible que quieras evitar usar un changeset.</p>
<p>Vamos a poner como ejemplo un formulario de contacto (asumiendo que ya tienes un proyecto de Phoenix corriendo):</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyAppWeb.ContactLive</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">MyAppWeb</span>, <span style="color:#e6db74">:live_view</span>

  <span style="color:#a6e22e">@impl</span> <span style="color:#66d9ef">true</span>
  <span style="color:#66d9ef">def</span> mount(_params, _session, socket) <span style="color:#66d9ef">do</span>
    params <span style="color:#f92672">=</span> %{<span style="color:#e6db74">&#34;name&#34;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;&#34;</span>, <span style="color:#e6db74">&#34;subject&#34;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;&#34;</span>, <span style="color:#e6db74">&#34;body&#34;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;&#34;</span>, <span style="color:#e6db74">&#34;email&#34;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;&#34;</span>}
    {<span style="color:#e6db74">:ok</span>, assign(socket, <span style="color:#e6db74">:form</span>, to_form(params))}
  <span style="color:#66d9ef">end</span>

  <span style="color:#a6e22e">@impl</span> <span style="color:#66d9ef">true</span>
  <span style="color:#66d9ef">def</span> render(assigns) <span style="color:#66d9ef">do</span>
    <span style="color:#e6db74">~H</span><span style="color:#e6db74">&#34;&#34;&#34;
</span><span style="color:#e6db74">    &lt;div&gt;
</span><span style="color:#e6db74">      &lt;.header&gt;
</span><span style="color:#e6db74">        &lt;:subtitle&gt;Contact&lt;/:subtitle&gt;
</span><span style="color:#e6db74">      &lt;/.header&gt;
</span><span style="color:#e6db74">
</span><span style="color:#e6db74">      &lt;.simple_form for={@form} id=&#34;contact_form&#34; phx-submit=&#34;save&#34;&gt;
</span><span style="color:#e6db74">        &lt;.input field={@form[:name]} label=&#34;Name&#34; /&gt;
</span><span style="color:#e6db74">        &lt;.input field={@form[:subject]} label=&#34;Subject&#34; /&gt;
</span><span style="color:#e6db74">        &lt;.input field={@form[:body]} type=&#34;textarea&#34; label=&#34;Body&#34; /&gt;
</span><span style="color:#e6db74">        &lt;.input field={@form[:email]} type=&#34;email&#34; label=&#34;Email&#34; /&gt;
</span><span style="color:#e6db74">
</span><span style="color:#e6db74">        &lt;:actions&gt;
</span><span style="color:#e6db74">          &lt;.button phx-disable-with=&#34;Saving...&#34;&gt;Send&lt;/.button&gt;
</span><span style="color:#e6db74">        &lt;/:actions&gt;
</span><span style="color:#e6db74">      &lt;/.simple_form&gt;
</span><span style="color:#e6db74">    &lt;/div&gt;
</span><span style="color:#e6db74">    &#34;&#34;&#34;</span>
  <span style="color:#66d9ef">end</span>

  <span style="color:#a6e22e">@impl</span> <span style="color:#66d9ef">true</span>
  <span style="color:#66d9ef">def</span> handle_event(<span style="color:#e6db74">&#34;save&#34;</span>, params, socket) <span style="color:#66d9ef">do</span>
    {<span style="color:#e6db74">:noreply</span>, socket}
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>Y en el <code>router.ex</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"> scope <span style="color:#e6db74">&#34;/&#34;</span>, <span style="color:#a6e22e">MyAppWeb</span> <span style="color:#66d9ef">do</span>
    pipe_through <span style="color:#e6db74">:browser</span>

    get <span style="color:#e6db74">&#34;/&#34;</span>, <span style="color:#a6e22e">PageController</span>, <span style="color:#e6db74">:home</span>

    live <span style="color:#e6db74">&#34;/contact&#34;</span>, <span style="color:#a6e22e">ContactLive</span> <span style="color:#75715e"># &lt;-- add this</span>
  <span style="color:#66d9ef">end</span>
</code></pre></div><p>Si entramos a http://localhost:4000/contact tendremos nuestro formulario de contacto:</p>
<p><img loading="lazy" src="https://res.cloudinary.com/escribocodigo/image/upload/v1693589107/phoenix_liveivew_contact_form_kg4srs.png" alt="Formulario de contacto sin changeset en LiveView"  />
</p>
<p>Como vemos la única diferencia es que al helper <code>to_form/1</code> le pasaremos un mapa en lugar de un changeset:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#75715e"># with changeset</span>
changeset <span style="color:#f92672">=</span> <span style="color:#a6e22e">Accounts</span><span style="color:#f92672">.</span>change_user(%<span style="color:#a6e22e">User</span>{}))
to_form(changeset)

<span style="color:#75715e"># without changeset</span>
params <span style="color:#f92672">=</span> %{<span style="color:#e6db74">&#34;name&#34;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;&#34;</span>, <span style="color:#e6db74">&#34;subject&#34;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;&#34;</span>, <span style="color:#e6db74">&#34;body&#34;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;&#34;</span>, <span style="color:#e6db74">&#34;email&#34;</span> <span style="color:#f92672">=&gt;</span> <span style="color:#e6db74">&#34;&#34;</span>}
to_form(params)
</code></pre></div><p>Si probamos el formulario podremos ver lo siguiente en los logs:</p>
<p><img loading="lazy" src="https://res.cloudinary.com/escribocodigo/image/upload/v1693589447/logs_handle_event_save_form_without_changeset_udt2id.png" alt="Logs recibiendo los parámetros del formulario"  />
</p>
<p>De esta forma podemos seguir aprovechando el poder de los helpers y los nuevos componentes del modulo <code>CoreComponents</code>.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Como mantener integridad de los datos con Ruby on Rails y Postgres</title>
      <link>https://sergiovp.dev/blog/como-mantener-integridad-de-los-datos-con-ruby-on-rails-y-postgres/</link>
      <pubDate>Wed, 21 Jun 2023 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/como-mantener-integridad-de-los-datos-con-ruby-on-rails-y-postgres/</guid>
      <description>Como implementar funcionalidades de postgres con Ruby on Rails para un correcto diseño de la base de datos</description>
      <content:encoded><![CDATA[<p>Este post es una adaptación a Ruby on Rails del post <a href="https://sergiovp.dev/blog/como-mantener-integridad-de-los-datos-con-elixir-phoenix-y-postgres/">con ejemplos en Elixir y  Phoenix</a>.</p>
<p>Una de las formas más comunes de almacenar datos en software es utilizando bases de datos relaciones. Aún y con el surgimiento de propuestas como las bases de datos NoSQL, el modelo relacional sigue estando presente porque es útil para la mayoría de los casos.</p>
<p>¿Qué es la integridad de los datos?</p>
<p>Se refiere a que la información almacenada en una base de datos sea completa y correcta. Cuando insertamos, actualizamos o eliminamos información la integridad puede perderse.</p>
<p>Existen distintos tipos de integridad:</p>
<ul>
<li>Integridad de dominio: Que los datos obligatorios estén presentes y sean del tipo esperado.</li>
<li>Integridad de entidad: establece que la clave primaria de una tabla debe tener un valor único para cada fila de la tabla.</li>
<li>La integridad referencial garantiza que la relación entre dos tablas permanezca sincronizada durante las operaciones y se asegura que los registros de las tablas relacionadas son válidos.</li>
</ul>
<p>Vamos a imaginar el caso de un banco que tiene las siguientes tablas: <code>users</code>, <code>accounts</code> <code>deposits</code>, <code>withdrwals</code>.</p>
<p>Primero vamos a crear las migraciones que van a crear nuestras tablas</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">rails g model User email:string name:string
rails g model Account balance:decimal active:boolean user_id:references:users
rails g model Deposit amount:decimal account_id:references:accounts
rails g model Withdrawal amount:decimal account_id:references:accounts
</code></pre></div><p>Por ahora tenemos las siguientes migraciones:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CreateUsers</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Migration</span><span style="color:#f92672">[</span><span style="color:#ae81ff">7</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0</span><span style="color:#f92672">]</span>
  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">change</span>
    create_table <span style="color:#e6db74">:users</span> <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>t<span style="color:#f92672">|</span>
      t<span style="color:#f92672">.</span>string <span style="color:#e6db74">:email</span>
      t<span style="color:#f92672">.</span>string <span style="color:#e6db74">:name</span>
      t<span style="color:#f92672">.</span>timestamps
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CreateAccounts</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Migration</span><span style="color:#f92672">[</span><span style="color:#ae81ff">7</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0</span><span style="color:#f92672">]</span>
  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">change</span>
    create_table <span style="color:#e6db74">:accounts</span> <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>t<span style="color:#f92672">|</span>
      t<span style="color:#f92672">.</span>decimal <span style="color:#e6db74">:balance</span>
      t<span style="color:#f92672">.</span>boolean <span style="color:#e6db74">:active</span>
      t<span style="color:#f92672">.</span>references <span style="color:#e6db74">:user</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">foreign_key</span>: <span style="color:#66d9ef">true</span>
      t<span style="color:#f92672">.</span>timestamps
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CreateDeposits</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Migration</span><span style="color:#f92672">[</span><span style="color:#ae81ff">7</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0</span><span style="color:#f92672">]</span>
  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">change</span>
    create_table <span style="color:#e6db74">:sales</span> <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>t<span style="color:#f92672">|</span>
      t<span style="color:#f92672">.</span>decimal <span style="color:#e6db74">:amount</span>
      t<span style="color:#f92672">.</span>references <span style="color:#e6db74">:account</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">foreign_key</span>: <span style="color:#66d9ef">true</span>

      t<span style="color:#f92672">.</span>timestamps
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CreateWithdrawals</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Migration</span><span style="color:#f92672">[</span><span style="color:#ae81ff">7</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0</span><span style="color:#f92672">]</span>
  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">change</span>
    create_table <span style="color:#e6db74">:withdrawal</span> <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>t<span style="color:#f92672">|</span>
      t<span style="color:#f92672">.</span>decimal <span style="color:#e6db74">:amount</span>
      t<span style="color:#f92672">.</span>references <span style="color:#e6db74">:account</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">foreign_key</span>: <span style="color:#66d9ef">true</span>

      t<span style="color:#f92672">.</span>timestamps
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><h2 id="campos-vacíos-o-nulos">Campos vacíos o nulos</h2>
<p>Como no queremos que un usuario pueda registrarse sin proporcionar su email y su nombre, forzaremos esto en la base de datos agregando la restricción <code>null: false</code> a la definición de las columnas.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CreateUsers</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Migration</span><span style="color:#f92672">[</span><span style="color:#ae81ff">7</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0</span><span style="color:#f92672">]</span>
  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">change</span>
    create_table <span style="color:#e6db74">:users</span> <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>t<span style="color:#f92672">|</span>
      t<span style="color:#f92672">.</span>string <span style="color:#e6db74">:email</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      t<span style="color:#f92672">.</span>string <span style="color:#e6db74">:name</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      t<span style="color:#f92672">.</span>timestamps
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>Otro lugar importante donde evitar la presencia de valores nulos es en las llaves foráneas. Además de incluir la misma opción en otros campos donde sea necesario:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CreateAccounts</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Migration</span><span style="color:#f92672">[</span><span style="color:#ae81ff">7</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0</span><span style="color:#f92672">]</span>
  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">change</span>
    create_table <span style="color:#e6db74">:accounts</span> <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>t<span style="color:#f92672">|</span>
      t<span style="color:#f92672">.</span>decimal <span style="color:#e6db74">:balance</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      t<span style="color:#f92672">.</span>boolean <span style="color:#e6db74">:active</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      t<span style="color:#f92672">.</span>references <span style="color:#e6db74">:user</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">foreign_key</span>: <span style="color:#66d9ef">true</span>
      t<span style="color:#f92672">.</span>timestamps
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CreateDeposits</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Migration</span><span style="color:#f92672">[</span><span style="color:#ae81ff">7</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0</span><span style="color:#f92672">]</span>
  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">change</span>
    create_table <span style="color:#e6db74">:deposits</span> <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>t<span style="color:#f92672">|</span>
      t<span style="color:#f92672">.</span>decimal <span style="color:#e6db74">:amount</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      t<span style="color:#f92672">.</span>references <span style="color:#e6db74">:account</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">foreign_key</span>: <span style="color:#66d9ef">true</span>

      t<span style="color:#f92672">.</span>timestamps
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CreateWithdrawals</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Migration</span><span style="color:#f92672">[</span><span style="color:#ae81ff">7</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0</span><span style="color:#f92672">]</span>
  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">change</span>
    create_table <span style="color:#e6db74">:withdrawals</span> <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>t<span style="color:#f92672">|</span>
      t<span style="color:#f92672">.</span>decimal <span style="color:#e6db74">:amount</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      t<span style="color:#f92672">.</span>references <span style="color:#e6db74">:account</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">foreign_key</span>: <span style="color:#66d9ef">true</span>

      t<span style="color:#f92672">.</span>timestamps
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><h2 id="valores-por-defecto">Valores por defecto</h2>
<p>En algunos casos como los <a href="https://sergiovp.dev/blog/siempre-asigna-un-valor-por-defecto-a-los-campos-booleanos/">campos booleanos es buena práctica definir un valor por defecto</a>. Como es el caso del campo <code>active</code> en la tabla <code>accounts</code>.</p>
<p>Además, considerando que tenemos un campo <code>balance</code> que almacena el balance de la cuenta, podríamos asumir que nuestro caso de uso es que al crear una cuenta se cree con un balance de 0 (no nulo). De esta forma tendremos que lidiar con menos validaciones si queremos realizar operaciones aritméticas con dicho valor.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CreateAccounts</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Migration</span><span style="color:#f92672">[</span><span style="color:#ae81ff">7</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0</span><span style="color:#f92672">]</span>
  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">change</span>
    create_table <span style="color:#e6db74">:accounts</span> <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>t<span style="color:#f92672">|</span>
      t<span style="color:#f92672">.</span>decimal <span style="color:#e6db74">:balance</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">default</span>: <span style="color:#ae81ff">0</span>
      t<span style="color:#f92672">.</span>boolean <span style="color:#e6db74">:active</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">default</span>: <span style="color:#66d9ef">false</span>
      t<span style="color:#f92672">.</span>references <span style="color:#e6db74">:user</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">foreign_key</span>: <span style="color:#66d9ef">true</span>
      t<span style="color:#f92672">.</span>timestamps
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><h2 id="campos-únicos">Campos únicos</h2>
<p>En la tabla <code>users</code> guardamos información como el email y el nombre del usuario. Como no queremos que haya 2 usuarios registrados con el mismo email, vamos a agregar esta restricción en nuestra tabla:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CreateUsers</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Migration</span><span style="color:#f92672">[</span><span style="color:#ae81ff">7</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0</span><span style="color:#f92672">]</span>
  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">change</span>
    create_table <span style="color:#e6db74">:users</span> <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>t<span style="color:#f92672">|</span>
      t<span style="color:#f92672">.</span>string <span style="color:#e6db74">:email</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      t<span style="color:#f92672">.</span>string <span style="color:#e6db74">:name</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      t<span style="color:#f92672">.</span>timestamps
    <span style="color:#66d9ef">end</span>

    add_index <span style="color:#e6db74">:users</span>, <span style="color:#e6db74">:email</span>, <span style="color:#e6db74">unique</span>: <span style="color:#66d9ef">true</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>La línea <code>add_index :users, :email, unique: true</code> hace 2 cosas:</p>
<ol>
<li>Crea un índice en la columna <code>email</code> de la tabla users</li>
<li>Este índice es único</li>
</ol>
<p>Esto hará que nuestra base de datos responda con un error si intentamos crear un registro con el mismo valor de <code>email</code> que un registro existente.</p>
<p>Otra funcionalidad útil es que podemos crear esta misma restricción con múltiples columnas. Es decir, negar la creación de un registro donde la combinación de dos o más columnas sea igual al de otro registro. Solo hay que agregar las columnas:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby">add_index <span style="color:#e6db74">:users</span>, <span style="color:#f92672">[</span><span style="color:#e6db74">:email</span>, <span style="color:#e6db74">:name</span><span style="color:#f92672">]</span>, <span style="color:#e6db74">unique</span>: <span style="color:#66d9ef">true</span>
</code></pre></div><h2 id="registros-huérfanos">Registros huérfanos</h2>
<p>En caso de que permitamos que los usuarios eliminen registros de la base de datos o esto suceda por algún proceso interno, podríamos perder integridad referencial.</p>
<p>Por ejemplo, si eliminamos una cuenta de nuestra base de datos, puede que los depósitos y retiros de dicho usuario permanezcan, pero sin saber a que cuenta hacían referencia.</p>
<p>Tenemos varias opciones:</p>
<ul>
<li><code>:cascade</code> Elimina todos los registros relacionados. Alias de la opción <code>CASCADE</code></li>
<li><code>:nullify</code> Establece la llave foránea en todos los registros relacionados en NULL.</li>
<li><code>:restrict</code> Genera un error si se intenta eliminar un registro padre que tiene registros relacionados.</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby">
<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CreateAccounts</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Migration</span><span style="color:#f92672">[</span><span style="color:#ae81ff">7</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0</span><span style="color:#f92672">]</span>
  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">change</span>
    create_table <span style="color:#e6db74">:accounts</span> <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>t<span style="color:#f92672">|</span>
      t<span style="color:#f92672">.</span>decimal <span style="color:#e6db74">:balance</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">default</span>: <span style="color:#ae81ff">0</span>
      t<span style="color:#f92672">.</span>boolean <span style="color:#e6db74">:active</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">default</span>: <span style="color:#66d9ef">false</span>
      t<span style="color:#f92672">.</span>references <span style="color:#e6db74">:user</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">foreign_key</span>: <span style="color:#66d9ef">true</span>, <span style="color:#e6db74">foreign_key</span>: {<span style="color:#e6db74">on_delete</span>: <span style="color:#e6db74">:restrict</span>}
      t<span style="color:#f92672">.</span>timestamps
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CreateDeposits</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Migration</span><span style="color:#f92672">[</span><span style="color:#ae81ff">7</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0</span><span style="color:#f92672">]</span>
  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">change</span>
    create_table <span style="color:#e6db74">:deposits</span> <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>t<span style="color:#f92672">|</span>
      t<span style="color:#f92672">.</span>decimal <span style="color:#e6db74">:amount</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      t<span style="color:#f92672">.</span>references <span style="color:#e6db74">:account</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">foreign_key</span>: <span style="color:#66d9ef">true</span>, <span style="color:#e6db74">foreign_key</span>: {<span style="color:#e6db74">on_delete</span>: <span style="color:#e6db74">:restrict</span>}

      t<span style="color:#f92672">.</span>timestamps
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CreateWithdrawals</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Migration</span><span style="color:#f92672">[</span><span style="color:#ae81ff">7</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0</span><span style="color:#f92672">]</span>
  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">change</span>
    create_table <span style="color:#e6db74">:withdrawals</span> <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>t<span style="color:#f92672">|</span>
      t<span style="color:#f92672">.</span>decimal <span style="color:#e6db74">:amount</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      t<span style="color:#f92672">.</span>references <span style="color:#e6db74">:account</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">foreign_key</span>: <span style="color:#66d9ef">true</span>, <span style="color:#e6db74">foreign_key</span>: {<span style="color:#e6db74">on_delete</span>: <span style="color:#e6db74">:restrict</span>}

      t<span style="color:#f92672">.</span>timestamps
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>La mejor opción dependerá de nuestro caso de uso. Por ahora evitaremos su eliminación usando la opción <code>on_delete: :restrict</code>.</p>
<h2 id="valores-inválidos">Valores inválidos</h2>
<p>Por ejemplo, cuando se manejan valores monetarios, como en la tabla donde registramos los depósitos y la tabla de retiros.  En el caso del campo <code>balance</code> de la tabla <code>accounts</code> un valor menor a 0 podríamos considerarlo inválido.</p>
<p>Además, en el caso de los depósitos y retiros, no tiene mucho sentido que el monto de una de estas operaciones sea 0 y mucho menos menor a 0.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CreateDeposits</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Migration</span><span style="color:#f92672">[</span><span style="color:#ae81ff">7</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0</span><span style="color:#f92672">]</span>
  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">change</span>
    create_table <span style="color:#e6db74">:deposits</span> <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>t<span style="color:#f92672">|</span>
      t<span style="color:#f92672">.</span>decimal <span style="color:#e6db74">:amount</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      t<span style="color:#f92672">.</span>references <span style="color:#e6db74">:account</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">foreign_key</span>: <span style="color:#66d9ef">true</span>, <span style="color:#e6db74">foreign_key</span>: {<span style="color:#e6db74">on_delete</span>: <span style="color:#e6db74">:restrict</span>}

      t<span style="color:#f92672">.</span>timestamps
    <span style="color:#66d9ef">end</span>

    reversible <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>dir<span style="color:#f92672">|</span>
      dir<span style="color:#f92672">.</span>up <span style="color:#66d9ef">do</span>
        execute <span style="color:#e6db74">&lt;&lt;-SQL
</span><span style="color:#e6db74"></span>		  <span style="color:#66d9ef">ALTER</span> <span style="color:#66d9ef">TABLE</span> deposits
	      <span style="color:#66d9ef">ADD</span> <span style="color:#66d9ef">CONSTRAINT</span> deposits_amount_must_be_positive
	      <span style="color:#66d9ef">CHECK</span> (amount <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">0</span>)
        <span style="color:#66d9ef">SQL</span>
      <span style="color:#66d9ef">end</span>

      dir<span style="color:#f92672">.</span>down <span style="color:#66d9ef">do</span>
        execute <span style="color:#e6db74">&lt;&lt;-SQL
</span><span style="color:#e6db74"></span>          <span style="color:#66d9ef">ALTER</span> <span style="color:#66d9ef">TABLE</span> deposits
          <span style="color:#66d9ef">DROP</span> <span style="color:#66d9ef">CONSTRAINT</span> deposits_amount_must_be_positive
        <span style="color:#66d9ef">SQL</span>
      <span style="color:#66d9ef">end</span>
	  <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CreateWithdrawals</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Migration</span><span style="color:#f92672">[</span><span style="color:#ae81ff">7</span><span style="color:#f92672">.</span><span style="color:#ae81ff">0</span><span style="color:#f92672">]</span>
  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">change</span>
    create_table <span style="color:#e6db74">:withdrawals</span> <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>t<span style="color:#f92672">|</span>
      t<span style="color:#f92672">.</span>decimal <span style="color:#e6db74">:amount</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      t<span style="color:#f92672">.</span>references <span style="color:#e6db74">:account</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">foreign_key</span>: <span style="color:#66d9ef">true</span>, <span style="color:#e6db74">foreign_key</span>: {<span style="color:#e6db74">on_delete</span>: <span style="color:#e6db74">:restrict</span>}

      t<span style="color:#f92672">.</span>timestamps
    <span style="color:#66d9ef">end</span>

    reversible <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>dir<span style="color:#f92672">|</span>
      dir<span style="color:#f92672">.</span>up <span style="color:#66d9ef">do</span>
        execute <span style="color:#e6db74">&lt;&lt;-SQL
</span><span style="color:#e6db74"></span>		  <span style="color:#66d9ef">ALTER</span> <span style="color:#66d9ef">TABLE</span> withdrawals
	      <span style="color:#66d9ef">ADD</span> <span style="color:#66d9ef">CONSTRAINT</span> withdrawals_amount_must_be_positive
	      <span style="color:#66d9ef">CHECK</span> (amount <span style="color:#f92672">&gt;</span> <span style="color:#ae81ff">0</span>)
        <span style="color:#66d9ef">SQL</span>
      <span style="color:#66d9ef">end</span>

      dir<span style="color:#f92672">.</span>down <span style="color:#66d9ef">do</span>
        execute <span style="color:#e6db74">&lt;&lt;-SQL
</span><span style="color:#e6db74"></span>          <span style="color:#66d9ef">ALTER</span> <span style="color:#66d9ef">TABLE</span> withdrawals
          <span style="color:#66d9ef">DROP</span> <span style="color:#66d9ef">CONSTRAINT</span> withdrawals_amount_must_be_positive
        <span style="color:#66d9ef">SQL</span>
      <span style="color:#66d9ef">end</span>
	  <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><h2 id="valores-inválidos-entre-tablas">Valores inválidos entre tablas</h2>
<p>Cuando tenemos una acción de retiro, asumimos que la cantidad que se retira se resta del balance de la cuenta de la cual se efectúa el retiro.</p>
<p>Para evitar que se realice un retiro por un monto mayor al balance de la cuenta podemos agregar una verificación:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">CreateWithdrawals</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Migration</span><span style="color:#f92672">[</span><span style="color:#ae81ff">6</span><span style="color:#f92672">.</span><span style="color:#ae81ff">1</span><span style="color:#f92672">]</span>
  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">change</span>
    create_table <span style="color:#e6db74">:withdrawals</span> <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>t<span style="color:#f92672">|</span>
      t<span style="color:#f92672">.</span>decimal <span style="color:#e6db74">:amount</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      t<span style="color:#f92672">.</span>references <span style="color:#e6db74">:account</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">foreign_key</span>: <span style="color:#66d9ef">true</span>, <span style="color:#e6db74">foreign_key</span>: {<span style="color:#e6db74">on_delete</span>: <span style="color:#e6db74">:restrict</span>}

      t<span style="color:#f92672">.</span>timestamps
    <span style="color:#66d9ef">end</span>

    reversible <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>dir<span style="color:#f92672">|</span>
      dir<span style="color:#f92672">.</span>up <span style="color:#66d9ef">do</span>
      <span style="color:#75715e">#...previous constrains</span>

        execute <span style="color:#e6db74">&lt;&lt;-SQL
</span><span style="color:#e6db74"></span>          <span style="color:#66d9ef">CREATE</span> <span style="color:#66d9ef">OR</span> <span style="color:#66d9ef">REPLACE</span> <span style="color:#66d9ef">FUNCTION</span> validate_withdrawal_amount()
          <span style="color:#66d9ef">RETURNS</span> <span style="color:#66d9ef">TRIGGER</span> <span style="color:#66d9ef">AS</span> $$
            <span style="color:#66d9ef">BEGIN</span>
              <span style="color:#66d9ef">IF</span> <span style="color:#66d9ef">NEW</span><span style="color:#f92672">.</span>amount <span style="color:#f92672">&gt;</span> (<span style="color:#66d9ef">SELECT</span> balance <span style="color:#66d9ef">FROM</span> accounts <span style="color:#66d9ef">WHERE</span> id <span style="color:#f92672">=</span> <span style="color:#66d9ef">NEW</span><span style="color:#f92672">.</span>account_id) <span style="color:#66d9ef">THEN</span>
              <span style="color:#66d9ef">RAISE</span> <span style="color:#66d9ef">EXCEPTION</span> <span style="color:#e6db74">&#39;Withdrawal amount is greater than account balance&#39;</span>;
              <span style="color:#66d9ef">END</span> <span style="color:#66d9ef">IF</span>;
              <span style="color:#66d9ef">RETURN</span> <span style="color:#66d9ef">NEW</span>;
            <span style="color:#66d9ef">END</span>;
            $$ <span style="color:#66d9ef">LANGUAGE</span> plpgsql;
        <span style="color:#66d9ef">SQL</span>

        execute <span style="color:#e6db74">&lt;&lt;-SQL
</span><span style="color:#e6db74"></span>          <span style="color:#66d9ef">CREATE</span> <span style="color:#66d9ef">TRIGGER</span> withdrawal_amount_validation
          <span style="color:#66d9ef">BEFORE</span> <span style="color:#66d9ef">INSERT</span> <span style="color:#66d9ef">ON</span> withdrawals
          <span style="color:#66d9ef">FOR</span> <span style="color:#66d9ef">EACH</span> <span style="color:#66d9ef">ROW</span> <span style="color:#66d9ef">EXECUTE</span> <span style="color:#66d9ef">FUNCTION</span> validate_withdrawal_amount();
        <span style="color:#66d9ef">SQL</span>
      <span style="color:#66d9ef">end</span>

      dir<span style="color:#f92672">.</span>down <span style="color:#66d9ef">do</span>
        execute <span style="color:#e6db74">&lt;&lt;-SQL
</span><span style="color:#e6db74"></span>          <span style="color:#66d9ef">DROP</span> <span style="color:#66d9ef">TRIGGER</span> withdrawal_amount_validation <span style="color:#66d9ef">IF</span> <span style="color:#66d9ef">EXISTS</span> <span style="color:#66d9ef">ON</span> withdrawals;
        <span style="color:#66d9ef">SQL</span>

        execute <span style="color:#e6db74">&lt;&lt;-SQL
</span><span style="color:#e6db74"></span>          <span style="color:#66d9ef">DROP</span> <span style="color:#66d9ef">FUNCTION</span> validate_withdrawal_amount();
        <span style="color:#66d9ef">SQL</span>
      <span style="color:#66d9ef">end</span>
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>Dado que en estos ejemplos se está usando Postgres como base de datos, debemos crear una función que se encargue de realizar la verificación, además de un trigger que se encargue de llamar a dicha función cada vez que se realice un <code>insert</code> en la tabla <code>withdrawals</code>.</p>
<p>Esto debido a que PostgreSQL no soporta <em>sub queries</em> en los <code>CONSTRAINT</code>.</p>
<h2 id="operaciones-parciales">Operaciones parciales</h2>
<p>En caso de que un usuario realice un retiro, hay 2 operaciones que deben realizarse:</p>
<ol>
<li>Crear el registro en la tabla <code>withdrawals</code> con la información del retiro</li>
<li>Actualizar el balance en la tabla <code>accounts</code> restando el monto del retiro al balance de la cuenta</li>
</ol>
<p>Queremos evitar que una de las operaciones se complete correctamente, pero la otra no. Quedando la base de datos con un estado incorrecto. Por ejemplo, tener un registro de retiro, pero que este monto no sea restado del balance de la cuenta.</p>
<p>Esto lo logramos con transacciones de la base de datos:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby"><span style="color:#66d9ef">ActiveRecord</span><span style="color:#f92672">::</span><span style="color:#66d9ef">Base</span><span style="color:#f92672">.</span>transaction <span style="color:#66d9ef">do</span>
  withdrawal<span style="color:#f92672">.</span>save!
  withdrawal<span style="color:#f92672">.</span>account<span style="color:#f92672">.</span>subtract_balance!(withdrawal<span style="color:#f92672">.</span>amount)
<span style="color:#66d9ef">end</span>
</code></pre></div><p>De esta forma nos aseguramos que ambas operaciones se ejecuten correctamente o de lo contrario se lanzara una excepción, realizando un rollback y ninguno de los cambios será confirmado.</p>
<p>Así como estos escenarios hipotéticos, pueden surgir muchos más dependiendo de información que estemos almacenando en nuestra aplicación.</p>
<p>También es importante distinguir los casos donde una restricción es más una regla de negocio que puede cambiar o ajustarse de manera más o menos frecuente. Se debe evaluar si la base de datos es el lugar correcto para dicha restricción o, por el contrario, nos restaría flexibilidad en el futuro y la capa de dominio sería un mejor lugar para manejarla.</p>
<p>¿Qué hay de agregar estas validaciones en los modelos/dominio? Seguro, pero como menciona David Bryant Copeland en su libro <a href="https://sustainable-rails.com/">Sustainable Web Development with Ruby on Rails</a>: Las validaciones son geniales para la experiencia de usuario, pero estas no proporcionan integridad de los datos.1</p>
<h2 id="referencias">Referencias</h2>
<p><a href="https://doriankarter.com/avoiding-data-loss-understanding-the-ondelete-option-in-elixir-migrations/">Avoiding Data Loss: Understanding the <code>:on_delete</code> Option in Elixir Migrations</a>
<a href="https://ashton.codes/the-difference-between-on_delete-restrict-nullify-and-cascade/">The difference between on_delete: :restrict, :nullify, and :cascade</a></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Como mantener integridad de los datos con Elixir Phoenix y Postgres</title>
      <link>https://sergiovp.dev/blog/como-mantener-integridad-de-los-datos-con-elixir-phoenix-y-postgres/</link>
      <pubDate>Mon, 15 May 2023 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/como-mantener-integridad-de-los-datos-con-elixir-phoenix-y-postgres/</guid>
      <description>Como implementar funcionalidades de postgres con Phoenix para un correcto diseño de la base de datos</description>
      <content:encoded><![CDATA[<p>Una de las formas más comunes de almacenar datos en software es utilizando bases de datos relaciones. Aún y con el surgimiento de propuestas como las bases de datos NoSQL, el modelo relacional sigue estando presente porque es útil para la mayoría de los casos.</p>
<p>¿Qué es la integridad de los datos?</p>
<p>Se refiere a que la información almacenada en una base de datos sea completa y correcta. Cuando insertamos, actualizamos o eliminamos información la integridad puede perderse.</p>
<p>Existen distintos tipos de integridad:</p>
<ul>
<li>Integridad de dominio: Que los datos obligatorios estén presentes y sean del tipo esperado.</li>
<li>Integridad de entidad: establece que la clave primaria de una tabla debe tener un valor único para cada fila de la tabla.</li>
<li>La integridad referencial garantiza que la relación entre dos tablas permanezca sincronizada durante las operaciones y se asegura que los registros de las tablas relacionadas son válidos.</li>
</ul>
<p>Vamos a imaginar el caso de un banco que tiene las siguientes tablas: <code>users</code>, <code>accounts</code> <code>deposits</code>, <code>withdrwals</code>.</p>
<p>Primero vamos a crear las migraciones que van a crear nuestras tablas</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-bash" data-lang="bash">mix phx.gen.schema User users email:string name:string
mix phx.gen.schema Account accounts balance:decimal active:boolean user_id:references:users
mix phx.gen.schema Deposit deposits amount:decimal account_id:references:accounts
mix phx.gen.schema Withdrawal withdrawals amount:decimal account_id:references:accounts
</code></pre></div><p>Por ahora tenemos las siguientes migraciones:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyApp.Repo.Migrations.CreateUsers</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Ecto.Migration</span>

  <span style="color:#66d9ef">def</span> change <span style="color:#66d9ef">do</span>
    create table(<span style="color:#e6db74">:users</span>) <span style="color:#66d9ef">do</span>
      add <span style="color:#e6db74">:email</span>, <span style="color:#e6db74">:string</span>
      add <span style="color:#e6db74">:name</span>, <span style="color:#e6db74">:string</span>

      timestamps()
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyApp.Repo.Migrations.CreateAccounts</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Ecto.Migration</span>

  <span style="color:#66d9ef">def</span> change <span style="color:#66d9ef">do</span>
    create table(<span style="color:#e6db74">:accounts</span>) <span style="color:#66d9ef">do</span>
      add <span style="color:#e6db74">:balance</span>, <span style="color:#e6db74">:decimal</span>
      add <span style="color:#e6db74">:active</span>, <span style="color:#e6db74">:boolean</span>, <span style="color:#e6db74">default</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      add <span style="color:#e6db74">:user_id</span>, references(<span style="color:#e6db74">:users</span>, <span style="color:#e6db74">on_delete</span>: <span style="color:#e6db74">:nothing</span>)

      timestamps()
    <span style="color:#66d9ef">end</span>

    create index(<span style="color:#e6db74">:accounts</span>, [<span style="color:#e6db74">:user_id</span>])
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyApp.Repo.Migrations.CreateDeposits</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Ecto.Migration</span>

  <span style="color:#66d9ef">def</span> change <span style="color:#66d9ef">do</span>
    create table(<span style="color:#e6db74">:deposits</span>) <span style="color:#66d9ef">do</span>
      add <span style="color:#e6db74">:amount</span>, <span style="color:#e6db74">:decimal</span>
      add <span style="color:#e6db74">:account_id</span>, references(<span style="color:#e6db74">:accounts</span>, <span style="color:#e6db74">on_delete</span>: <span style="color:#e6db74">:nothing</span>)

      timestamps()
    <span style="color:#66d9ef">end</span>

    create index(<span style="color:#e6db74">:deposits</span>, [<span style="color:#e6db74">:account_id</span>])
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyApp.Repo.Migrations.CreateWithdrawals</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Ecto.Migration</span>

  <span style="color:#66d9ef">def</span> change <span style="color:#66d9ef">do</span>
    create table(<span style="color:#e6db74">:withdrawals</span>) <span style="color:#66d9ef">do</span>
      add <span style="color:#e6db74">:amount</span>, <span style="color:#e6db74">:decimal</span>
      add <span style="color:#e6db74">:account_id</span>, references(<span style="color:#e6db74">:accounts</span>, <span style="color:#e6db74">on_delete</span>: <span style="color:#e6db74">:nothing</span>)

      timestamps()
    <span style="color:#66d9ef">end</span>

    create index(<span style="color:#e6db74">:withdrawals</span>, [<span style="color:#e6db74">:account_id</span>])
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><h2 id="campos-vacíos-o-nulos">Campos vacíos o nulos</h2>
<p>Como no queremos que un usuario pueda registrarse sin proporcionar su email y su nombre, forzaremos esto en la base de datos agregando la restricción <code>null: false</code> a la definición de las columnas.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyApp.Repo.Migrations.CreateUsers</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Ecto.Migration</span>

  <span style="color:#66d9ef">def</span> change <span style="color:#66d9ef">do</span>
    create table(<span style="color:#e6db74">:users</span>) <span style="color:#66d9ef">do</span>
      add <span style="color:#e6db74">:email</span>, <span style="color:#e6db74">:string</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      add <span style="color:#e6db74">:name</span>, <span style="color:#e6db74">:string</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>

      timestamps()
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>Otro lugar importante donde evitar la presencia de valores nulos es en las llaves foráneas. Además de incluir la misma opción en otros campos donde sea necesario:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyApp.Repo.Migrations.CreateAccounts</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Ecto.Migration</span>

  <span style="color:#66d9ef">def</span> change <span style="color:#66d9ef">do</span>
    create table(<span style="color:#e6db74">:accounts</span>) <span style="color:#66d9ef">do</span>
      add <span style="color:#e6db74">:balance</span>, <span style="color:#e6db74">:decimal</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      add <span style="color:#e6db74">:active</span>, <span style="color:#e6db74">:boolean</span>, <span style="color:#e6db74">default</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      add <span style="color:#e6db74">:user_id</span>, references(<span style="color:#e6db74">:users</span>, <span style="color:#e6db74">on_delete</span>: <span style="color:#e6db74">:nothing</span>), <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>

      timestamps()
    <span style="color:#66d9ef">end</span>

    create index(<span style="color:#e6db74">:accounts</span>, [<span style="color:#e6db74">:user_id</span>])
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyApp.Repo.Migrations.CreateDeposits</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Ecto.Migration</span>

  <span style="color:#66d9ef">def</span> change <span style="color:#66d9ef">do</span>
    create table(<span style="color:#e6db74">:deposits</span>) <span style="color:#66d9ef">do</span>
      add <span style="color:#e6db74">:amount</span>, <span style="color:#e6db74">:decimal</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      add <span style="color:#e6db74">:account_id</span>, references(<span style="color:#e6db74">:accounts</span>, <span style="color:#e6db74">on_delete</span>: <span style="color:#e6db74">:nothing</span>), <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>

      timestamps()
    <span style="color:#66d9ef">end</span>

    create index(<span style="color:#e6db74">:deposits</span>, [<span style="color:#e6db74">:account_id</span>])
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyApp.Repo.Migrations.CreateWithdrawals</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Ecto.Migration</span>

  <span style="color:#66d9ef">def</span> change <span style="color:#66d9ef">do</span>
    create table(<span style="color:#e6db74">:withdrawals</span>) <span style="color:#66d9ef">do</span>
      add <span style="color:#e6db74">:amount</span>, <span style="color:#e6db74">:decimal</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      add <span style="color:#e6db74">:account_id</span>, references(<span style="color:#e6db74">:accounts</span>, <span style="color:#e6db74">on_delete</span>: <span style="color:#e6db74">:nothing</span>), <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>

      timestamps()
    <span style="color:#66d9ef">end</span>

    create index(<span style="color:#e6db74">:withdrawals</span>, [<span style="color:#e6db74">:account_id</span>])
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><h2 id="valores-por-defecto">Valores por defecto</h2>
<p>En algunos casos como los <a href="https://sergiovp.dev/blog/siempre-asigna-un-valor-por-defecto-a-los-campos-booleanos/">campos booleanos es buena práctica definir un valor por defecto</a>.  En el caso del campo <code>active</code> en la tabla <code>accounts</code>, los generadores de Phoenix agregaron dos restricciones al definir el campo como booleano.</p>
<p>Además, considerando que tenemos un campo <code>balance</code> que almacena el balance de la cuenta, podríamos asumir que nuestro caso de uso es que al crear una cuenta se cree con un balance de 0 (no nulo). De esta forma tendremos que lidiar con menos validaciones si queremos realizar operaciones aritméticas con dicho valor.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyApp.Repo.Migrations.CreateAccounts</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Ecto.Migration</span>

  <span style="color:#66d9ef">def</span> change <span style="color:#66d9ef">do</span>
    create table(<span style="color:#e6db74">:accounts</span>) <span style="color:#66d9ef">do</span>
      add <span style="color:#e6db74">:balance</span>, <span style="color:#e6db74">:decimal</span>, <span style="color:#e6db74">default</span>: <span style="color:#ae81ff">0</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      add <span style="color:#e6db74">:active</span>, <span style="color:#e6db74">:boolean</span>, <span style="color:#e6db74">default</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      add <span style="color:#e6db74">:user_id</span>, references(<span style="color:#e6db74">:users</span>, <span style="color:#e6db74">on_delete</span>: <span style="color:#e6db74">:nothing</span>), <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>

      timestamps()
    <span style="color:#66d9ef">end</span>

    create index(<span style="color:#e6db74">:accounts</span>, [<span style="color:#e6db74">:user_id</span>])
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><h2 id="campos-únicos">Campos únicos</h2>
<p>En la tabla <code>users</code> guardamos información como el email y el nombre del usuario. Como no queremos que haya 2 usuarios registrados con el mismo email, vamos a agregar esta restricción en nuestra tabla:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyApp.Repo.Migrations.CreateUsers</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Ecto.Migration</span>

  <span style="color:#66d9ef">def</span> change <span style="color:#66d9ef">do</span>
    create table(<span style="color:#e6db74">:users</span>) <span style="color:#66d9ef">do</span>
      add <span style="color:#e6db74">:email</span>, <span style="color:#e6db74">:string</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      add <span style="color:#e6db74">:name</span>, <span style="color:#e6db74">:string</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>

      timestamps()
    <span style="color:#66d9ef">end</span>

    create unique_index(<span style="color:#e6db74">:users</span>, [<span style="color:#e6db74">:email</span>])
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>La línea <code>create unique_index(:users, [:email])</code> hace 2 cosas:</p>
<ol>
<li>Crea un índice en la columna <code>email</code> de la tabla users</li>
<li>Este índice es único</li>
</ol>
<p>Esto hará que nuestra base de datos responda con un error si intentamos crear un registro con el mismo valor de <code>email</code> que un registro existente.</p>
<p>Otra funcionalidad útil es que podemos crear esta misma restricción con múltiples columnas. Es decir, negar la creación de un registro donde la combinación de dos o más columnas sea igual al de otro registro. Solo hay que agregar las columnas:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby">create unique_index(<span style="color:#e6db74">:users</span>, <span style="color:#f92672">[</span><span style="color:#e6db74">:email</span>, <span style="color:#e6db74">:name</span><span style="color:#f92672">]</span>)
</code></pre></div><h2 id="registros-huérfanos">Registros huérfanos</h2>
<p>En caso de que permitamos que los usuarios eliminen registros de la base de datos o esto suceda por algún proceso interno, podríamos perder integridad referencial.</p>
<p>Por ejemplo, si eliminamos una cuenta de nuestra base de datos, puede que los depósitos y retiros de dicho usuario permanezcan, pero sin saber a que cuenta hacían referencia.</p>
<p>Los generadores de Phoenix agregaron la opción <code>on_delete: :nothing</code> en todas las llaves foraneas, que es la opción por defecto. Pero tenemos varias opciones:</p>
<ul>
<li><code>:delete_all</code> Elimina todos los registros relacionados. Alias de la opción <code>CASCADE</code></li>
<li><code>:nilify_all</code> Establece la llave foránea en todos los registros relacionados en NULL.</li>
<li><code>:restrict</code> Genera un error si se intenta eliminar un registro padre que tiene registros relacionados.</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyApp.Repo.Migrations.CreateAccounts</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Ecto.Migration</span>

  <span style="color:#66d9ef">def</span> change <span style="color:#66d9ef">do</span>
    create table(<span style="color:#e6db74">:accounts</span>) <span style="color:#66d9ef">do</span>
      add <span style="color:#e6db74">:balance</span>, <span style="color:#e6db74">:decimal</span>, <span style="color:#e6db74">default</span>: <span style="color:#ae81ff">0</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      add <span style="color:#e6db74">:active</span>, <span style="color:#e6db74">:boolean</span>, <span style="color:#e6db74">default</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      add <span style="color:#e6db74">:user_id</span>, references(<span style="color:#e6db74">:users</span>, <span style="color:#e6db74">on_delete</span>: <span style="color:#e6db74">:restrict</span>), <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>

      timestamps()
    <span style="color:#66d9ef">end</span>

    create index(<span style="color:#e6db74">:accounts</span>, [<span style="color:#e6db74">:user_id</span>])
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyApp.Repo.Migrations.CreateDeposits</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Ecto.Migration</span>

  <span style="color:#66d9ef">def</span> change <span style="color:#66d9ef">do</span>
    create table(<span style="color:#e6db74">:deposits</span>) <span style="color:#66d9ef">do</span>
      add <span style="color:#e6db74">:amount</span>, <span style="color:#e6db74">:decimal</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      add <span style="color:#e6db74">:account_id</span>, references(<span style="color:#e6db74">:accounts</span>, <span style="color:#e6db74">on_delete</span>: <span style="color:#e6db74">:restrict</span>), <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>

      timestamps()
    <span style="color:#66d9ef">end</span>

    create index(<span style="color:#e6db74">:deposits</span>, [<span style="color:#e6db74">:account_id</span>])
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyApp.Repo.Migrations.CreateWithdrawals</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Ecto.Migration</span>

  <span style="color:#66d9ef">def</span> change <span style="color:#66d9ef">do</span>
    create table(<span style="color:#e6db74">:withdrawals</span>) <span style="color:#66d9ef">do</span>
      add <span style="color:#e6db74">:amount</span>, <span style="color:#e6db74">:decimal</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      add <span style="color:#e6db74">:account_id</span>, references(<span style="color:#e6db74">:accounts</span>, <span style="color:#e6db74">on_delete</span>: <span style="color:#e6db74">:restrict</span>), <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>

      timestamps()
    <span style="color:#66d9ef">end</span>

    create index(<span style="color:#e6db74">:withdrawals</span>, [<span style="color:#e6db74">:account_id</span>])
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>La mejor opción dependerá de nuestro caso de uso. Por ahora evitaremos su eliminación usando la opción <code>on_delete: :restrict</code>.</p>
<h2 id="valores-inválidos">Valores inválidos</h2>
<p>Por ejemplo, cuando se manejan valores monetarios, como en la tabla donde registramos los depósitos y la tabla de retiros.  En el caso del campo <code>balance</code> de la tabla <code>accounts</code> un valor menor a 0 podríamos considerarlo inválido.</p>
<p>Además, en el caso de los depósitos y retiros, no tiene mucho sentido que el monto de una de estas operaciones sea 0 y mucho menos menor a 0.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyApp.Repo.Migrations.CreateDeposits</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Ecto.Migration</span>

  <span style="color:#66d9ef">def</span> change <span style="color:#66d9ef">do</span>
    create table(<span style="color:#e6db74">:deposits</span>) <span style="color:#66d9ef">do</span>
      add <span style="color:#e6db74">:amount</span>, <span style="color:#e6db74">:decimal</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      add <span style="color:#e6db74">:account_id</span>, references(<span style="color:#e6db74">:accounts</span>, <span style="color:#e6db74">on_delete</span>: <span style="color:#e6db74">:restrict</span>), <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>

      timestamps()
    <span style="color:#66d9ef">end</span>

    create index(<span style="color:#e6db74">:deposits</span>, [<span style="color:#e6db74">:account_id</span>])

	create constraint(<span style="color:#e6db74">&#34;deposits&#34;</span>, <span style="color:#e6db74">&#34;amount_must_be_positive&#34;</span>, <span style="color:#e6db74">check</span>: <span style="color:#e6db74">&#34;amount &gt; 0&#34;</span>, <span style="color:#e6db74">comment</span>: <span style="color:#e6db74">&#34;Deposit amount can&#39;t be 0 or negative&#34;</span>)
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyApp.Repo.Migrations.CreateWithdrawals</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Ecto.Migration</span>

  <span style="color:#66d9ef">def</span> change <span style="color:#66d9ef">do</span>
    create table(<span style="color:#e6db74">:withdrawals</span>) <span style="color:#66d9ef">do</span>
      add <span style="color:#e6db74">:amount</span>, <span style="color:#e6db74">:decimal</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      add <span style="color:#e6db74">:account_id</span>, references(<span style="color:#e6db74">:accounts</span>, <span style="color:#e6db74">on_delete</span>: <span style="color:#e6db74">:restrict</span>), <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>

      timestamps()
    <span style="color:#66d9ef">end</span>

    create index(<span style="color:#e6db74">:withdrawals</span>, [<span style="color:#e6db74">:account_id</span>])

	create constraint(<span style="color:#e6db74">&#34;withdrawals&#34;</span>, <span style="color:#e6db74">&#34;amount_must_be_positive&#34;</span>, <span style="color:#e6db74">check</span>: <span style="color:#e6db74">&#34;amount &gt; 0&#34;</span>, <span style="color:#e6db74">comment</span>: <span style="color:#e6db74">&#34;Withdrawal amount can&#39;t be 0 or negative&#34;</span>)
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><h2 id="valores-inválidos-entre-tablas">Valores inválidos entre tablas</h2>
<p>Cuando tenemos una acción de retiro, asumimos que la cantidad que se retira se resta del balance de la cuenta de la cual se efectúa el retiro.</p>
<p>Para evitar que se realice un retiro por un monto mayor al balance de la cuenta podemos agregar una verificación:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyApp.Repo.Migrations.CreateWithdrawals</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Ecto.Migration</span>

  <span style="color:#66d9ef">def</span> up <span style="color:#66d9ef">do</span>
    create table(<span style="color:#e6db74">:withdrawals</span>) <span style="color:#66d9ef">do</span>
      add <span style="color:#e6db74">:amount</span>, <span style="color:#e6db74">:decimal</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
      add <span style="color:#e6db74">:account_id</span>, references(<span style="color:#e6db74">:accounts</span>, <span style="color:#e6db74">on_delete</span>: <span style="color:#e6db74">:restrict</span>), <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>

      timestamps()
    <span style="color:#66d9ef">end</span>

    create index(<span style="color:#e6db74">:withdrawals</span>, [<span style="color:#e6db74">:account_id</span>])

	create constraint(<span style="color:#e6db74">&#34;withdrawals&#34;</span>, <span style="color:#e6db74">&#34;amount_must_be_positive&#34;</span>, <span style="color:#e6db74">check</span>: <span style="color:#e6db74">&#34;amount &gt; 0&#34;</span>, <span style="color:#e6db74">comment</span>: <span style="color:#e6db74">&#34;Withdrawal amount can&#39;t be 0 or negative&#34;</span>)

    execute <span style="color:#e6db74">&#34;&#34;&#34;
</span><span style="color:#e6db74">    CREATE OR REPLACE FUNCTION validate_withdrawal_amount()
</span><span style="color:#e6db74">      RETURNS TRIGGER AS $$
</span><span style="color:#e6db74">        BEGIN
</span><span style="color:#e6db74">          IF NEW.amount &gt; (SELECT balance FROM accounts WHERE id = NEW.account_id) THEN
</span><span style="color:#e6db74">          RAISE EXCEPTION &#39;Withdrawal amount is greater than account balance&#39;;
</span><span style="color:#e6db74">          END IF;
</span><span style="color:#e6db74">          RETURN NEW;
</span><span style="color:#e6db74">        END;
</span><span style="color:#e6db74">        $$ LANGUAGE plpgsql;
</span><span style="color:#e6db74">    &#34;&#34;&#34;</span>

    execute <span style="color:#e6db74">&#34;&#34;&#34;
</span><span style="color:#e6db74">    CREATE TRIGGER withdrawal_amount_validation
</span><span style="color:#e6db74">    BEFORE INSERT ON withdrawals
</span><span style="color:#e6db74">    FOR EACH ROW EXECUTE FUNCTION validate_withdrawal_amount();
</span><span style="color:#e6db74">    &#34;&#34;&#34;</span>
  <span style="color:#66d9ef">end</span>

  <span style="color:#66d9ef">def</span> down <span style="color:#66d9ef">do</span>
	  execute <span style="color:#e6db74">&#34;&#34;&#34;
</span><span style="color:#e6db74">    DROP TRIGGER withdrawal_amount_validation IF EXISTS ON withdrawals;
</span><span style="color:#e6db74">    &#34;&#34;&#34;</span>

    execute <span style="color:#e6db74">&#34;&#34;&#34;
</span><span style="color:#e6db74">    DROP FUNCTION validate_withdrawal_amount();
</span><span style="color:#e6db74">    &#34;&#34;&#34;</span>

    drop constraint(<span style="color:#e6db74">&#34;withdrawals&#34;</span>, <span style="color:#e6db74">&#34;amount_must_be_positive&#34;</span>)

    drop index(<span style="color:#e6db74">&#34;withdrawals&#34;</span>, [<span style="color:#e6db74">:account_id</span>])

    drop table(<span style="color:#e6db74">&#34;withdrawals&#34;</span>)
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>Dado que en estos ejemplos se está usando Postgres como base de datos, debemos crear una función que se encargue de realizar la verificación, además de un trigger que se encargue de llamar a dicha función cada vez que se realice un insert en la tabla <code>withdrawals</code>.</p>
<p>Esto debido a que Postgres no soporta <em>sub queries</em> en los <code>CONSTRAINT</code>.</p>
<h2 id="operaciones-parciales">Operaciones parciales</h2>
<p>En caso de que un usuario realice un retiro, hay 2 operaciones que deben realizarse:</p>
<ol>
<li>Crear el registro en la tabla <code>withdrawals</code> con la información del retiro</li>
<li>Actualizar el balance en la tabla <code>accounts</code> restando el monto del retiro al balance de la cuenta</li>
</ol>
<p>Queremos evitar que una de las operaciones se complete correctamente, pero la otra no. Quedando la base de datos con un estado incorrecto. Por ejemplo, tener un registro de retiro, pero que este monto no sea restado del balance de la cuenta.</p>
<p>Esto lo logramos con transacciones de la base de datos:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#a6e22e">MyRepo</span><span style="color:#f92672">.</span>transaction(<span style="color:#66d9ef">fn</span> <span style="color:#f92672">-&gt;</span>
  account <span style="color:#f92672">=</span> <span style="color:#a6e22e">Accounts</span><span style="color:#f92672">.</span>get!(account_id)
  <span style="color:#a6e22e">Withdrawals</span><span style="color:#f92672">.</span>create!(account, amount)
  <span style="color:#a6e22e">Accounts</span><span style="color:#f92672">.</span>update_balance!(account, account<span style="color:#f92672">.</span>balance <span style="color:#f92672">-</span> amount)
<span style="color:#66d9ef">end</span>)
</code></pre></div><p>De esta forma nos aseguramos que ambas operaciones se ejecuten correctamente o de lo contrario se lanzara una excepción, realizando un rollback y ninguno de los cambios será confirmado.</p>
<p>Así como estos escenarios hipotéticos, pueden surgir muchos más dependiendo de información que estemos almacenando en nuestra aplicación.</p>
<p>También es importante distinguir los casos donde una restricción es más una regla de negocio que puede cambiar o ajustarse de manera más o menos frecuente. Se debe evaluar si la base de datos es el lugar correcto para dicha restricción o, por el contrario, nos restaría flexibilidad en el futuro y la capa de dominio sería un mejor lugar para manejarla.</p>
<p>¿Qué hay de agregar estas validaciones en los modelos/dominio? Seguro, pero como menciona David Bryant Copeland en su libro <a href="https://sustainable-rails.com/">Sustainable Web Development with Ruby on Rails</a>: Las validaciones son geniales para la experiencia de usuario, pero estas no proporcionan integridad de los datos.1</p>
<h2 id="referencias">Referencias</h2>
<p><a href="https://doriankarter.com/avoiding-data-loss-understanding-the-ondelete-option-in-elixir-migrations/">Avoiding Data Loss: Understanding the <code>:on_delete</code> Option in Elixir Migrations</a></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Siempre asigna un valor por defecto a los campos booleanos</title>
      <link>https://sergiovp.dev/blog/siempre-asigna-un-valor-por-defecto-a-los-campos-booleanos/</link>
      <pubDate>Tue, 18 Apr 2023 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/siempre-asigna-un-valor-por-defecto-a-los-campos-booleanos/</guid>
      <description>Porque es importante que los campos booleanos en nuestra base de datos no permitan valores nulos?</description>
      <content:encoded><![CDATA[<p>Es relativamente común ver campos booleanos en una base de datos para representar algún estado y al mismo tiempo es igualmente común ver que por un descuido esta columna pueda y llegue a tener valores nulos.</p>
<p>Esto puede acarrear errores de semántica y de lógica. Para evitar este problema es una buena práctica definir los campos como <code>null: false</code> y además siempre dar un valor por defecto, por ejemplo <code>admin: false</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby">create_table <span style="color:#e6db74">:users</span> <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>t<span style="color:#f92672">|</span>
  t<span style="color:#f92672">.</span>boolean <span style="color:#e6db74">:admin</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">default</span>: <span style="color:#66d9ef">false</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>Si un campo es booleano, entonces su valor es <code>true</code> o <code>false</code>.</p>
<p>Pero, si tú, como yo en algún momento, pensaste en usar el valor <code>null</code> como un tercer estado, quizás lo que busques es un campo <code>enum</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby">create_table <span style="color:#e6db74">:users</span> <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>t<span style="color:#f92672">|</span>
  t<span style="color:#f92672">.</span>integer <span style="color:#e6db74">:role</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">default</span>: <span style="color:#ae81ff">1</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby"><span style="color:#66d9ef">class</span> <span style="color:#a6e22e">User</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ApplicationRecord</span>
  enum <span style="color:#e6db74">:role</span>, <span style="color:#f92672">[</span><span style="color:#e6db74">:user</span>, <span style="color:#e6db74">:admin</span><span style="color:#f92672">]</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>o incluso una <code>lookup table</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby">create_table <span style="color:#e6db74">:roles</span> <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>t<span style="color:#f92672">|</span>
  t<span style="color:#f92672">.</span>string <span style="color:#e6db74">:role</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby">create_table <span style="color:#e6db74">:users</span> <span style="color:#66d9ef">do</span> <span style="color:#f92672">|</span>t<span style="color:#f92672">|</span>
  t<span style="color:#f92672">.</span>references <span style="color:#e6db74">:role</span>, <span style="color:#e6db74">null</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">foreign_key</span>: <span style="color:#66d9ef">true</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>La mejor opción dependerá del contexto de tu aplicación.</p>
<p>Un excelente post sobre el tema es <a href="https://thoughtbot.com/blog/avoid-the-threestate-boolean-problem">Avoid the Three-state Boolean Problem</a></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Como crear un productor personalizado en Broadway</title>
      <link>https://sergiovp.dev/blog/como-crear-un-productor-personalizado-en-broadway/</link>
      <pubDate>Tue, 21 Feb 2023 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/como-crear-un-productor-personalizado-en-broadway/</guid>
      <description>Como crear un custom producer para consumir información de otra fuente en Elixir Broadway</description>
      <content:encoded><![CDATA[<p><a href="https://hexdocs.pm/broadway/Broadway.html">Broadway</a> es una herramienta construida sobre <a href="https://hexdocs.pm/gen_stage/1.0.0/GenStage.html">GenStage</a>, concurrente de varias etapas para crear pipelines de ingesta y procesamiento de datos.</p>
<p>Es decir, esta biblioteca nos permite consumir y procesar información de fuentes como un Message Broker (Kafka, RabbitMQ, etc.).</p>
<p>Por si fuera poco, viene con una serie de características integradas que nos permiten diseñar nuestros pipelines con una gran flexibilidad:</p>
<ul>
<li>Back-pressure</li>
<li>Concurrencia y procesamiento por lotes</li>
<li>Rate limiting</li>
<li>Varios producers out of the box</li>
<li>La posibilidad de crear custom producers</li>
<li>Entre otras.</li>
</ul>
<h2 id="creando-un-custom-producer">Creando un custom producer</h2>
<p>Si bien consumir información desde un <em>message broker</em> es, por decirlo de alguna manera, la opción por defecto de Broadway, esto no significa que esté limitado a estos como fuente de información.</p>
<p>Como ya se mencionó, nos permite crear y conectar nuestros propios <a href="https://hexdocs.pm/broadway/custom-producers.html">producers</a> y estos pueden consumir información de cualquier fuente que queramos. Una base de datos, por ejemplo.</p>
<p>Lo primero es crear un módulo que utilice el behaviour de Broadway:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyBroadway</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Broadway</span>

  <span style="color:#f92672">require</span> <span style="color:#a6e22e">Logger</span>

  <span style="color:#a6e22e">@chunk_size</span> <span style="color:#ae81ff">10</span>

  <span style="color:#66d9ef">def</span> start_link(_opts) <span style="color:#66d9ef">do</span>
    options <span style="color:#f92672">=</span> [
      <span style="color:#e6db74">name</span>: __MODULE__,
      <span style="color:#e6db74">producer</span>: [
        <span style="color:#e6db74">module</span>: {<span style="color:#a6e22e">DBDummyProducer</span>, []},
        <span style="color:#e6db74">transformer</span>: {__MODULE__, <span style="color:#e6db74">:transform</span>, []},
        <span style="color:#e6db74">concurrency</span>: <span style="color:#ae81ff">1</span>
      ],
      <span style="color:#e6db74">processors</span>: [
        <span style="color:#e6db74">default</span>: [
          <span style="color:#e6db74">max_demand</span>: <span style="color:#a6e22e">@chunk_size</span>,
          <span style="color:#e6db74">concurrency</span>: <span style="color:#ae81ff">1</span>
        ]
      ]
    ]

	<span style="color:#a6e22e">Broadway</span><span style="color:#f92672">.</span>start_link(__MODULE__, options)
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>En las opciones le estamos diciendo a la biblioteca que nuestro <em>producer</em> es el módulo <code>DBDummyProducer</code>, ya que vamos a &ldquo;realizar&rdquo; consultas a la base de datos, usamos la opción <code>max_demand</code> para limitar el número de registros que vamos a cargar en memoria. Vamos a utilizar este valor en <code>DBDummyProducer</code>.</p>
<p>Además, le estamos diciendo que vamos a utilizar como <em>transformer</em> la función <code>transform</code> en el mismo módulo <code>MyBroadway</code>. Ya que los datos llegaran como maps o structs y deben incluirse en la estructura <code>Broadway.Message.t()</code>.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">def</span> transform(event, _opts) <span style="color:#66d9ef">do</span>
    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>debug(<span style="color:#e6db74">&#34;[MyBroadway] transforming event: </span><span style="color:#e6db74">#{</span>inspect(event)<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)

    %<span style="color:#a6e22e">Message</span>{
      <span style="color:#e6db74">data</span>: event,
      <span style="color:#e6db74">acknowledger</span>: {__MODULE__, <span style="color:#e6db74">:trackings</span>, <span style="color:#e6db74">:ack_data</span>}
    }
<span style="color:#66d9ef">end</span>
</code></pre></div><p>Otra función que debemos implementar es <code>ack/3</code> . Esta función sirve para notificar que los mensajes recibidos se procesaron correctamente o fallaron.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">def</span> ack(<span style="color:#e6db74">:ack_id</span>, successful, failed) <span style="color:#66d9ef">do</span>
    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>debug(
      <span style="color:#e6db74">&#34;[MyBroadway] ack successful: </span><span style="color:#e6db74">#{</span><span style="color:#a6e22e">Enum</span><span style="color:#f92672">.</span>count(successful)<span style="color:#e6db74">}</span><span style="color:#e6db74">, failed: </span><span style="color:#e6db74">#{</span><span style="color:#a6e22e">Enum</span><span style="color:#f92672">.</span>count(failed)<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
    )

    <span style="color:#e6db74">:ok</span>
  <span style="color:#66d9ef">end</span>
</code></pre></div><p>Ahora creamos nuestro módulo <code>DBDummyProducer</code>, para efectos de simplificación vamos a generar datos en memoria, solo habría que sustituir esto por la llamada a nuestro contexto o módulo que genere la consulta.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">DBDummyProducer</span> <span style="color:#66d9ef">do</span>
  <span style="color:#a6e22e">@moduledoc</span> <span style="color:#e6db74">&#34;&#34;&#34;
</span><span style="color:#e6db74">  This module emulates DBProducer behavior producing in memory rows
</span><span style="color:#e6db74">  &#34;&#34;&#34;</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">GenStage</span>

  <span style="color:#f92672">require</span> <span style="color:#a6e22e">Logger</span>

  <span style="color:#a6e22e">@impl</span> <span style="color:#a6e22e">GenStage</span>
  <span style="color:#66d9ef">def</span> init(opts) <span style="color:#66d9ef">do</span>
    {<span style="color:#e6db74">:producer</span>, opts}
  <span style="color:#66d9ef">end</span>

  <span style="color:#66d9ef">def</span> start_link(opts) <span style="color:#66d9ef">do</span>
    <span style="color:#a6e22e">GenStage</span><span style="color:#f92672">.</span>start_link(__MODULE__, opts)
  <span style="color:#66d9ef">end</span>

  <span style="color:#a6e22e">@impl</span> <span style="color:#66d9ef">true</span>
  <span style="color:#a6e22e">@spec</span> handle_demand(integer, any) <span style="color:#f92672">::</span> {<span style="color:#e6db74">:noreply</span>, list, any}
  <span style="color:#66d9ef">def</span> handle_demand(demand, state) <span style="color:#66d9ef">do</span>
    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>debug(<span style="color:#66d9ef">fn</span> <span style="color:#f92672">-&gt;</span>
      <span style="color:#e6db74">&#34;[DBProducer] handling demand: </span><span style="color:#e6db74">#{</span>demand<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
    <span style="color:#66d9ef">end</span>)

    <span style="color:#75715e"># events = Users.list(limit: demand)</span>
    events <span style="color:#f92672">=</span>
      <span style="color:#a6e22e">Enum</span><span style="color:#f92672">.</span>map(<span style="color:#ae81ff">1</span><span style="color:#f92672">..</span>demand, <span style="color:#66d9ef">fn</span> _number <span style="color:#f92672">-&gt;</span>
        %{
          <span style="color:#e6db74">id</span>: <span style="color:#a6e22e">Enum</span><span style="color:#f92672">.</span>random(<span style="color:#ae81ff">1</span><span style="color:#f92672">..</span><span style="color:#ae81ff">9_999_999</span>),
        }
      <span style="color:#66d9ef">end</span>)

    {<span style="color:#e6db74">:noreply</span>, events, state}
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>

</code></pre></div><p>Ahora nuestro pipeline puede recibir datos desde nuestro <em>producer</em>. Seguramente necesitamos procesar esta información de alguna manera. Para esto tenemos dos <em>callback</em> <code>handle_message/3</code> para procesar mensajes individuales y <code>handle_batch/4</code> para procesar lotes.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">def</span> handle_message(_processor, message, _context) <span style="color:#66d9ef">do</span>
    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>debug(<span style="color:#66d9ef">fn</span> <span style="color:#f92672">-&gt;</span> <span style="color:#e6db74">&#34;[MyBroadway] Incoming Message: </span><span style="color:#e6db74">#{</span>inspect(message)<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span> <span style="color:#66d9ef">end</span>)
	<span style="color:#75715e"># perform business logic</span>
    message
<span style="color:#66d9ef">end</span>

<span style="color:#66d9ef">def</span> handle_batch(_batcher, messages, _batch_info, _context) <span style="color:#66d9ef">do</span>
	<span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>debug(<span style="color:#e6db74">&#34;[MyBroadway] in default batcher&#34;</span>)
	<span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>debug(<span style="color:#66d9ef">fn</span> <span style="color:#f92672">-&gt;</span> <span style="color:#e6db74">&#34;[MyBroadway] size: </span><span style="color:#e6db74">#{</span>length(messages)<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span> <span style="color:#66d9ef">end</span>)

	messages
<span style="color:#66d9ef">end</span>
</code></pre></div><p>Sí queremos hacer uso de <code>handle_batch/4</code> debemos hacer dos cosas:</p>
<ul>
<li>Agregar la llave <code>batchers</code> a nuestras <code>options</code></li>
<li>Al transformar el mensaje debemos especificar en cual <code>batcher</code> se va a procesar</li>
</ul>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">def</span> start_link(_opts) <span style="color:#66d9ef">do</span>
	 options <span style="color:#f92672">=</span> [
	  <span style="color:#75715e"># ... previous options</span>
	  <span style="color:#e6db74">batchers</span>: [<span style="color:#e6db74">default</span>: []]
	]
	<span style="color:#a6e22e">Broadway</span><span style="color:#f92672">.</span>start_link(__MODULE__, options)
	<span style="color:#66d9ef">end</span>
</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">def</span> transform(event, _state) <span style="color:#66d9ef">do</span>
    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>debug(<span style="color:#e6db74">&#34;[MyBroadway] transforming event: </span><span style="color:#e6db74">#{</span>inspect(event)<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)

    %<span style="color:#a6e22e">Broadway.Message</span>{
      <span style="color:#e6db74">data</span>: event,
      <span style="color:#e6db74">acknowledger</span>: {__MODULE__, <span style="color:#e6db74">:trackings</span>, <span style="color:#e6db74">:ack_data</span>}
    }
    <span style="color:#f92672">|&gt;</span> <span style="color:#a6e22e">Broadway.Message</span><span style="color:#f92672">.</span>put_batcher(<span style="color:#e6db74">:default</span>)
  <span style="color:#66d9ef">end</span>
</code></pre></div><p>Por último, solo resta agregar <code>MyBroadway</code> al supervisor, normalmente en el archivo <code>application.ex</code></p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir">children <span style="color:#f92672">=</span> [
  {<span style="color:#a6e22e">MyBroadway</span>, []}
]

<span style="color:#a6e22e">Supervisor</span><span style="color:#f92672">.</span>start_link(children, <span style="color:#e6db74">strategy</span>: <span style="color:#e6db74">:one_for_one</span>)
</code></pre></div><p>Si intentamos probar el comportamiento del código, no seremos capaces de apreciar los eventos que están sucediendo. Para configurar un poco este comportamiento podemos usar la configuración de <em>rate_limiting</em> en las opciones del <em>producer</em>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">def</span> start_link(_opts) <span style="color:#66d9ef">do</span>
	 options <span style="color:#f92672">=</span> [
	  <span style="color:#75715e"># ... previous options</span>
	  <span style="color:#e6db74">producer</span>: [
      <span style="color:#e6db74">module</span>: {<span style="color:#a6e22e">DBDummyProducer</span>, []},
      <span style="color:#e6db74">transformer</span>: {__MODULE__, <span style="color:#e6db74">:transform</span>, []},
      <span style="color:#e6db74">rate_limiting</span>: [
        <span style="color:#e6db74">interval</span>: <span style="color:#ae81ff">10_000</span>,
        <span style="color:#e6db74">allowed_messages</span>: <span style="color:#ae81ff">10</span>
      ],
      <span style="color:#e6db74">concurrency</span>: <span style="color:#ae81ff">1</span>
    ],
    <span style="color:#75715e"># .. next options</span>
	]
	<span style="color:#a6e22e">Broadway</span><span style="color:#f92672">.</span>start_link(__MODULE__, options)
<span style="color:#66d9ef">end</span>
</code></pre></div><p>Puedes ver el resultado final en el repositorio <a href="https://github.com/Plus17/broadway-custom-producer">broadway-custom-producer</a>. Pero nuestro código queda de la siguiente manera:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyBroadway</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Broadway</span>

  <span style="color:#f92672">require</span> <span style="color:#a6e22e">Logger</span>

  <span style="color:#a6e22e">@chunk_size</span> <span style="color:#ae81ff">10</span>

  <span style="color:#a6e22e">@doc</span> <span style="color:#e6db74">&#34;&#34;&#34;
</span><span style="color:#e6db74">  Uses rate_limiting options to be able to see log messages in the console
</span><span style="color:#e6db74">  &#34;&#34;&#34;</span>
  <span style="color:#66d9ef">def</span> start_link(_opts) <span style="color:#66d9ef">do</span>
    options <span style="color:#f92672">=</span> [
      <span style="color:#e6db74">name</span>: __MODULE__,
      <span style="color:#e6db74">producer</span>: [
        <span style="color:#e6db74">module</span>: {<span style="color:#a6e22e">DBDummyProducer</span>, []},
        <span style="color:#e6db74">transformer</span>: {__MODULE__, <span style="color:#e6db74">:transform</span>, []},
        <span style="color:#e6db74">rate_limiting</span>: [
          <span style="color:#e6db74">interval</span>: <span style="color:#ae81ff">10_000</span>,
          <span style="color:#e6db74">allowed_messages</span>: <span style="color:#ae81ff">10</span>
        ],
        <span style="color:#e6db74">concurrency</span>: <span style="color:#ae81ff">1</span>
      ],
      <span style="color:#e6db74">processors</span>: [
        <span style="color:#e6db74">default</span>: [
          <span style="color:#e6db74">max_demand</span>: <span style="color:#a6e22e">@chunk_size</span>,
          <span style="color:#e6db74">concurrency</span>: <span style="color:#ae81ff">1</span>
        ]
      ],
      <span style="color:#e6db74">batchers</span>: [<span style="color:#e6db74">default</span>: []]
    ]

    <span style="color:#a6e22e">Broadway</span><span style="color:#f92672">.</span>start_link(__MODULE__, options)
  <span style="color:#66d9ef">end</span>

  <span style="color:#66d9ef">def</span> transform(event, _state) <span style="color:#66d9ef">do</span>
    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>debug(<span style="color:#e6db74">&#34;[MyBroadway] transforming event: </span><span style="color:#e6db74">#{</span>inspect(event)<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)

    %<span style="color:#a6e22e">Broadway.Message</span>{
      <span style="color:#e6db74">data</span>: event,
      <span style="color:#e6db74">acknowledger</span>: {__MODULE__, <span style="color:#e6db74">:trackings</span>, <span style="color:#e6db74">:ack_data</span>}
    }
    <span style="color:#f92672">|&gt;</span> <span style="color:#a6e22e">Broadway.Message</span><span style="color:#f92672">.</span>put_batcher(<span style="color:#e6db74">:default</span>)
  <span style="color:#66d9ef">end</span>

  <span style="color:#66d9ef">def</span> ack(<span style="color:#e6db74">:trackings</span>, successful, failed) <span style="color:#66d9ef">do</span>
    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>debug(
      <span style="color:#e6db74">&#34;[MyBroadway] ack successful: </span><span style="color:#e6db74">#{</span>length(successful)<span style="color:#e6db74">}</span><span style="color:#e6db74">, failed: </span><span style="color:#e6db74">#{</span>length(failed)<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
    )

    <span style="color:#e6db74">:ok</span>
  <span style="color:#66d9ef">end</span>

  <span style="color:#66d9ef">def</span> handle_message(_processor, message, _context) <span style="color:#66d9ef">do</span>
    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>debug(<span style="color:#66d9ef">fn</span> <span style="color:#f92672">-&gt;</span> <span style="color:#e6db74">&#34;[MyBroadway] Incoming Message: </span><span style="color:#e6db74">#{</span>inspect(message)<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span> <span style="color:#66d9ef">end</span>)

    message
  <span style="color:#66d9ef">end</span>

  <span style="color:#66d9ef">def</span> handle_batch(_batcher, messages, _batch_info, _context) <span style="color:#66d9ef">do</span>
    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>debug(<span style="color:#e6db74">&#34;[MyBroadway] in default batcher&#34;</span>)
    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>debug(<span style="color:#66d9ef">fn</span> <span style="color:#f92672">-&gt;</span> <span style="color:#e6db74">&#34;[MyBroadway] size: </span><span style="color:#e6db74">#{</span>length(messages)<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span> <span style="color:#66d9ef">end</span>)

    messages
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>

</code></pre></div><div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">DBDummyProducer</span> <span style="color:#66d9ef">do</span>
  <span style="color:#a6e22e">@moduledoc</span> <span style="color:#e6db74">&#34;&#34;&#34;
</span><span style="color:#e6db74">  This module emulates database behavior by producing in-memory rows
</span><span style="color:#e6db74">  &#34;&#34;&#34;</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">GenStage</span>

  <span style="color:#f92672">require</span> <span style="color:#a6e22e">Logger</span>

  <span style="color:#a6e22e">@impl</span> <span style="color:#a6e22e">GenStage</span>
  <span style="color:#66d9ef">def</span> init(opts) <span style="color:#66d9ef">do</span>
    {<span style="color:#e6db74">:producer</span>, opts}
  <span style="color:#66d9ef">end</span>

  <span style="color:#66d9ef">def</span> start_link(opts) <span style="color:#66d9ef">do</span>
    <span style="color:#a6e22e">GenStage</span><span style="color:#f92672">.</span>start_link(__MODULE__, opts)
  <span style="color:#66d9ef">end</span>

  <span style="color:#a6e22e">@impl</span> <span style="color:#66d9ef">true</span>
  <span style="color:#66d9ef">def</span> handle_demand(demand, state) <span style="color:#66d9ef">do</span>
    <span style="color:#a6e22e">Logger</span><span style="color:#f92672">.</span>debug(<span style="color:#66d9ef">fn</span> <span style="color:#f92672">-&gt;</span>
      <span style="color:#e6db74">&#34;[DBDummyProducer] handling demand: </span><span style="color:#e6db74">#{</span>demand<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>
    <span style="color:#66d9ef">end</span>)

    <span style="color:#75715e"># events = Users.list_users(limit: demand)</span>
    events <span style="color:#f92672">=</span>
      <span style="color:#a6e22e">Enum</span><span style="color:#f92672">.</span>map(<span style="color:#ae81ff">1</span><span style="color:#f92672">..</span>demand, <span style="color:#66d9ef">fn</span> _number <span style="color:#f92672">-&gt;</span>
        %{
          <span style="color:#e6db74">id</span>: <span style="color:#a6e22e">Enum</span><span style="color:#f92672">.</span>random(<span style="color:#ae81ff">1</span><span style="color:#f92672">..</span><span style="color:#ae81ff">9_999_999_999</span>)
        }
      <span style="color:#66d9ef">end</span>)

    {<span style="color:#e6db74">:noreply</span>, events, state}
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>

</code></pre></div><p>Ahora, si ingresamos a la consola interactiva de nuestra aplicación, veremos algo como los siguientes mensajes que mostramos mediante el <code>Logger</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-shell" data-lang="shell">10:10:39.341 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>DBDummyProducer<span style="color:#f92672">]</span> handling demand: <span style="color:#ae81ff">10</span>

10:10:39.349 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> transforming event: %<span style="color:#f92672">{</span>id: 2730249896<span style="color:#f92672">}</span>

10:10:39.349 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> transforming event: %<span style="color:#f92672">{</span>id: 8277765695<span style="color:#f92672">}</span>

10:10:39.349 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> transforming event: %<span style="color:#f92672">{</span>id: 917212511<span style="color:#f92672">}</span>

10:10:39.349 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> transforming event: %<span style="color:#f92672">{</span>id: 2264493728<span style="color:#f92672">}</span>

10:10:39.349 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> transforming event: %<span style="color:#f92672">{</span>id: 4033891075<span style="color:#f92672">}</span>

10:10:39.349 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> transforming event: %<span style="color:#f92672">{</span>id: 2065156038<span style="color:#f92672">}</span>

10:10:39.349 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> transforming event: %<span style="color:#f92672">{</span>id: 4625753942<span style="color:#f92672">}</span>

10:10:39.349 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> transforming event: %<span style="color:#f92672">{</span>id: 2587137776<span style="color:#f92672">}</span>

10:10:39.349 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> transforming event: %<span style="color:#f92672">{</span>id: 6481119527<span style="color:#f92672">}</span>

10:10:39.349 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> transforming event: %<span style="color:#f92672">{</span>id: 6661902836<span style="color:#f92672">}</span>

10:10:39.353 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> Incoming Message: %Broadway.Message<span style="color:#f92672">{</span>data: %<span style="color:#f92672">{</span>id: 2730249896<span style="color:#f92672">}</span>, metadata: %<span style="color:#f92672">{}</span>, acknowledger: <span style="color:#f92672">{</span>MyBroadway, :trackings, :ack_data<span style="color:#f92672">}</span>, batcher: :default, batch_key: :default, batch_mode: :bulk, status: :ok<span style="color:#f92672">}</span>

10:10:39.353 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> Incoming Message: %Broadway.Message<span style="color:#f92672">{</span>data: %<span style="color:#f92672">{</span>id: 8277765695<span style="color:#f92672">}</span>, metadata: %<span style="color:#f92672">{}</span>, acknowledger: <span style="color:#f92672">{</span>MyBroadway, :trackings, :ack_data<span style="color:#f92672">}</span>, batcher: :default, batch_key: :default, batch_mode: :bulk, status: :ok<span style="color:#f92672">}</span>

10:10:39.353 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> Incoming Message: %Broadway.Message<span style="color:#f92672">{</span>data: %<span style="color:#f92672">{</span>id: 917212511<span style="color:#f92672">}</span>, metadata: %<span style="color:#f92672">{}</span>, acknowledger: <span style="color:#f92672">{</span>MyBroadway, :trackings, :ack_data<span style="color:#f92672">}</span>, batcher: :default, batch_key: :default, batch_mode: :bulk, status: :ok<span style="color:#f92672">}</span>

10:10:39.353 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> Incoming Message: %Broadway.Message<span style="color:#f92672">{</span>data: %<span style="color:#f92672">{</span>id: 2264493728<span style="color:#f92672">}</span>, metadata: %<span style="color:#f92672">{}</span>, acknowledger: <span style="color:#f92672">{</span>MyBroadway, :trackings, :ack_data<span style="color:#f92672">}</span>, batcher: :default, batch_key: :default, batch_mode: :bulk, status: :ok<span style="color:#f92672">}</span>

10:10:39.353 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> Incoming Message: %Broadway.Message<span style="color:#f92672">{</span>data: %<span style="color:#f92672">{</span>id: 4033891075<span style="color:#f92672">}</span>, metadata: %<span style="color:#f92672">{}</span>, acknowledger: <span style="color:#f92672">{</span>MyBroadway, :trackings, :ack_data<span style="color:#f92672">}</span>, batcher: :default, batch_key: :default, batch_mode: :bulk, status: :ok<span style="color:#f92672">}</span>

10:10:39.354 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> Incoming Message: %Broadway.Message<span style="color:#f92672">{</span>data: %<span style="color:#f92672">{</span>id: 2065156038<span style="color:#f92672">}</span>, metadata: %<span style="color:#f92672">{}</span>, acknowledger: <span style="color:#f92672">{</span>MyBroadway, :trackings, :ack_data<span style="color:#f92672">}</span>, batcher: :default, batch_key: :default, batch_mode: :bulk, status: :ok<span style="color:#f92672">}</span>

10:10:39.354 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> Incoming Message: %Broadway.Message<span style="color:#f92672">{</span>data: %<span style="color:#f92672">{</span>id: 4625753942<span style="color:#f92672">}</span>, metadata: %<span style="color:#f92672">{}</span>, acknowledger: <span style="color:#f92672">{</span>MyBroadway, :trackings, :ack_data<span style="color:#f92672">}</span>, batcher: :default, batch_key: :default, batch_mode: :bulk, status: :ok<span style="color:#f92672">}</span>

10:10:39.354 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> Incoming Message: %Broadway.Message<span style="color:#f92672">{</span>data: %<span style="color:#f92672">{</span>id: 2587137776<span style="color:#f92672">}</span>, metadata: %<span style="color:#f92672">{}</span>, acknowledger: <span style="color:#f92672">{</span>MyBroadway, :trackings, :ack_data<span style="color:#f92672">}</span>, batcher: :default, batch_key: :default, batch_mode: :bulk, status: :ok<span style="color:#f92672">}</span>

10:10:39.354 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> Incoming Message: %Broadway.Message<span style="color:#f92672">{</span>data: %<span style="color:#f92672">{</span>id: 6481119527<span style="color:#f92672">}</span>, metadata: %<span style="color:#f92672">{}</span>, acknowledger: <span style="color:#f92672">{</span>MyBroadway, :trackings, :ack_data<span style="color:#f92672">}</span>, batcher: :default, batch_key: :default, batch_mode: :bulk, status: :ok<span style="color:#f92672">}</span>

10:10:39.354 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> Incoming Message: %Broadway.Message<span style="color:#f92672">{</span>data: %<span style="color:#f92672">{</span>id: 6661902836<span style="color:#f92672">}</span>, metadata: %<span style="color:#f92672">{}</span>, acknowledger: <span style="color:#f92672">{</span>MyBroadway, :trackings, :ack_data<span style="color:#f92672">}</span>, batcher: :default, batch_key: :default, batch_mode: :bulk, status: :ok<span style="color:#f92672">}</span>

10:10:40.355 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> in default batcher

10:10:40.355 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> size: <span style="color:#ae81ff">10</span>

10:10:40.355 <span style="color:#f92672">[</span>debug<span style="color:#f92672">]</span> <span style="color:#f92672">[</span>MyBroadway<span style="color:#f92672">]</span> ack successful: 10, failed: <span style="color:#ae81ff">0</span>
</code></pre></div><p>Como pudimos ver, Broadway es una opción muy poderosa para consumir y procesar información, podemos diseñar nuestros pipelines incluyendo distintas fases en el proceso y contamos con múltiples opciones que nos permiten adecuar el comportamiento de acuerdo a nuestro caso de uso.</p>
<p>En este post apenas abarcamos lo básico, algunos recursos muy útiles para consultar:</p>
<ul>
<li>El libro <a href="https://pragprog.com/titles/sgdpelixir/concurrent-data-processing-in-elixir/">Concurrent Data Processing in Elixir</a></li>
<li>La <a href="https://hexdocs.pm/broadway/introduction.html">documentación oficial</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Useful Patterns to communicate with external services</title>
      <link>https://sergiovp.dev/blog/useful-patterns-to-communicate-with-external-services/</link>
      <pubDate>Tue, 03 Jan 2023 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/useful-patterns-to-communicate-with-external-services/</guid>
      <description>Some patterns to handle external requests</description>
      <content:encoded><![CDATA[<p>Communicating with external services like a third-party API REST is hard, not only because the contract is designed by someone else. Also, now you need to consider the Fallacies of Distributed Systems.</p>
<p>Calls to external services can fail for multiple reasons unrelated to business concerns. You can receive a timeout error, the network can fail, bandwith related errors, etc.</p>
<p>However, some patterns exist to build more reliable systems when dealing with network calls.</p>
<h3 id="retries">Retries</h3>
<p>It&rsquo;s basically about automatically retrying failed requests.</p>
<p>But we need to be careful with this pattern if the action isn&rsquo;t idempotent.</p>
<h3 id="circuit-breaker">Circuit breaker</h3>
<p>It&rsquo;s commonly found services or processes depend at some point on an external call, and when this external service fails causes a cascade of failures.</p>
<p>The <a href="https://microservices.io/patterns/reliability/circuit-breaker.html">circuit breaker</a> pattern monitors the failures, if the call fails over a certain number of times in a time frame, opens the circuit preventing new requests that you know will fail.</p>
<h3 id="background-jobs">Background jobs</h3>
<p>If we have expensive work that doesn&rsquo;t require completing all steps to send a response to the client this could be very useful.</p>
<p>Exists a lot of libraries provide us with power background jobs with capabilities like automatic retries, exponential back-off, etc.</p>
<p>But even with these libraries we need to concern about issues like having idempotent jobs, not serializing complex structures like a complete object and using an id instead or performance issues, etc.</p>
<p>Off course exists other patterns depending on the problem to handle o multiple ways to manage the same issue. But this is a start point to know some of them.</p>
<p>Lastly, here are some helpful resources:</p>
<ul>
<li><a href="https://multithreaded.stitchfix.com/blog/2017/05/09/patterns-of-service-oriented-architecture/">Patterns of Service-Oriented Architecture</a></li>
<li><a href="https://medium.com/becloudy/architecting-for-reliability-part-2-resiliency-and-availability-design-patterns-for-the-cloud-cf7aaaed0df2">Architecting for Reliability Part 2</a></li>
<li><a href="https://learn.microsoft.com/en-us/azure/architecture/patterns/">Architecture Patterns - Azure learn</a></li>
<li><a href="https://www.redhat.com/architect/topics/architecture-patterns">Architecture Patterns - Redhat articles</a></li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>How to manage business logic in rails</title>
      <link>https://sergiovp.dev/blog/how-to-manage-business-logic-in-rails/</link>
      <pubDate>Tue, 25 Oct 2022 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/how-to-manage-business-logic-in-rails/</guid>
      <description>A pattern to handle complexity with business logic in Ruby on Rails</description>
      <content:encoded><![CDATA[<p>One of the main problems, when an application grows, is how to keep organized the business logic.</p>
<p>Following the “rails way” can conduce to a common result called obese models &amp; obese controllers.</p>
<p>This means that your models and your controllers keep growing when you continuously add the business logic in the same place. Another inconvenience is that you are coupling your use cases or business logic to the infrastructure (the framework, the database, and the input interface).</p>
<p>Imagine a common feature like registering a user, easily you can have the following steps:</p>
<ol>
<li>Save the user in the database</li>
<li>Generate a token to handle email confirmation</li>
<li>Send an email to confirm the user&rsquo;s email</li>
</ol>
<p>You would usually see this logic in the model &amp; controller:</p>
<p>A common approach to handle this is to encapsulate the business logic in a specific class, this pattern has several names like class services, handlers, interactors, etc.</p>
<p>Another important thing to leverage this pattern is to take advantage of solid principles like single responsibility. Having multiple specific purpose classes that our service classes can use like query objects, mailers, jobs, POROs, etc.</p>
<p>I take this one from <a href="https://sustainable-rails.com/">Sustainable Web Development with Ruby on Rails</a> book:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby"><span style="color:#75715e"># app/services/user_creator.rb</span>

<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">UserCreator</span>
  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">create_user</span>(user)
    user<span style="color:#f92672">.</span>save

    <span style="color:#66d9ef">if</span> user<span style="color:#f92672">.</span>invalid?
      <span style="color:#66d9ef">return</span> <span style="color:#66d9ef">Result</span><span style="color:#f92672">.</span>new(<span style="color:#e6db74">created</span>: <span style="color:#66d9ef">false</span>, <span style="color:#e6db74">user</span>: user)
    <span style="color:#66d9ef">end</span>

    <span style="color:#66d9ef">UserEmailConfirmationJob</span><span style="color:#f92672">.</span>perform_async(user<span style="color:#f92672">.</span>id)

    <span style="color:#66d9ef">Result</span><span style="color:#f92672">.</span>new(<span style="color:#e6db74">created</span>: user<span style="color:#f92672">.</span>valid?, <span style="color:#e6db74">user</span>: user)
  <span style="color:#66d9ef">end</span>

  <span style="color:#66d9ef">class</span> <span style="color:#a6e22e">Result</span>
    <span style="color:#66d9ef">attr_reader</span> <span style="color:#e6db74">:user</span>

    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">initialize</span>(<span style="color:#e6db74">created</span>:, <span style="color:#e6db74">user</span>:)
      @created <span style="color:#f92672">=</span> created
      @user <span style="color:#f92672">=</span> user
    <span style="color:#66d9ef">end</span>

    <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">created?</span>
      @created
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>Usage:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-ruby" data-lang="ruby"><span style="color:#75715e"># app/controllers/users_controller.rb</span>

<span style="color:#66d9ef">class</span> <span style="color:#a6e22e">UsersController</span> <span style="color:#f92672">&lt;</span> <span style="color:#66d9ef">ApplicationController</span>
  <span style="color:#f92672">...</span>

  <span style="color:#66d9ef">def</span> <span style="color:#a6e22e">create</span>
    user_params <span style="color:#f92672">=</span> params<span style="color:#f92672">.</span>require(<span style="color:#e6db74">:user</span>)<span style="color:#f92672">.</span>permit(<span style="color:#e6db74">:email</span>, <span style="color:#e6db74">:password</span>)

    result <span style="color:#f92672">=</span> <span style="color:#66d9ef">UserCreator</span><span style="color:#f92672">.</span>new<span style="color:#f92672">.</span>create_user(<span style="color:#66d9ef">User</span><span style="color:#f92672">.</span>new(user_params))

    <span style="color:#66d9ef">if</span> result<span style="color:#f92672">.</span>created?
      redirect_to user_path(result<span style="color:#f92672">.</span>user)
    <span style="color:#66d9ef">else</span>
      @user <span style="color:#f92672">=</span> result<span style="color:#f92672">.</span>user
      render <span style="color:#e6db74">:new</span>, <span style="color:#e6db74">status</span>: <span style="color:#e6db74">:unprocessable_entity</span>
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>Some considerations:</p>
<ul>
<li>This class performs business logic and expects some specific kind of data to make an action. Type casting, input sanitization, etc. These things are infrastructure concerns, not the business.</li>
<li>Return rich result objects provide more useful details than a single boolean. This is very useful for tests and readability.</li>
<li>Explicit method names like <code>create_user</code> provide more context than a generic method like <code>call</code> or <code>execute</code>.</li>
</ul>
<p>Finally, you get some nice advantages: the possibility of testing your business logic independently of the input interface. If your SSR app change to an API REST (or you have both) this will be easy to handle.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Testing the Phoenix controllers without rebinding the conn</title>
      <link>https://sergiovp.dev/blog/testing-the-phoenix-controllers-without-rebinding-the-conn/</link>
      <pubDate>Wed, 11 May 2022 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/testing-the-phoenix-controllers-without-rebinding-the-conn/</guid>
      <description>An easy way to improve readability in phoenix controllers tests</description>
      <content:encoded><![CDATA[<p>Maybe, for readability purposes, you prefer to avoid <a href="https://hexdocs.pm/credo/Credo.Check.Refactor.VariableRebinding.html">variable rebinding</a>.</p>
<p>For example, take this generated test code from <code>mix phx.gen.auth</code> task:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir">test <span style="color:#e6db74">&#34;renders log in page&#34;</span>, %{<span style="color:#e6db74">conn</span>: conn} <span style="color:#66d9ef">do</span>
  conn <span style="color:#f92672">=</span> get(conn, <span style="color:#a6e22e">Routes</span><span style="color:#f92672">.</span>user_session_path(conn, <span style="color:#e6db74">:new</span>))
  response <span style="color:#f92672">=</span> html_response(conn, <span style="color:#ae81ff">200</span>)
  assert response <span style="color:#f92672">=~</span> <span style="color:#e6db74">&#34;Log in&#34;</span>
  assert response <span style="color:#f92672">=~</span> <span style="color:#e6db74">&#34;Register&lt;/a&gt;&#34;</span>
  assert response <span style="color:#f92672">=~</span> <span style="color:#e6db74">&#34;Forgot password?&lt;/a&gt;&#34;</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>It&rsquo;s completely ok. But, you can prefer something like this:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir">test <span style="color:#e6db74">&#34;renders log in page&#34;</span>, %{<span style="color:#e6db74">conn</span>: conn} <span style="color:#66d9ef">do</span>
  response <span style="color:#f92672">=</span>
    conn
    <span style="color:#f92672">|&gt;</span> get(<span style="color:#a6e22e">Routes</span><span style="color:#f92672">.</span>user_session_path(conn, <span style="color:#e6db74">:new</span>))
    <span style="color:#f92672">|&gt;</span> html_response(<span style="color:#ae81ff">200</span>)

  assert response <span style="color:#f92672">=~</span> <span style="color:#e6db74">&#34;Log in&#34;</span>
  assert response <span style="color:#f92672">=~</span> <span style="color:#e6db74">&#34;Register&lt;/a&gt;&#34;</span>
  assert response <span style="color:#f92672">=~</span> <span style="color:#e6db74">&#34;Forgot password?&lt;/a&gt;&#34;</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>In this way, you don&rsquo;t rebind the conn and take advantage of the pipe operator.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>How to build a flexible API client in Elixir</title>
      <link>https://sergiovp.dev/blog/how-to-build-a-flexible-api-client-in-elixir/</link>
      <pubDate>Wed, 19 Jan 2022 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/how-to-build-a-flexible-api-client-in-elixir/</guid>
      <description>A brief guide to build an extensible API client</description>
      <content:encoded><![CDATA[<p>Normally, to talk with an external API we will search some library to do the work for us. Sometimes, this search is infructuous for some of the next reasons:</p>
<ul>
<li>Exist one or more libraries but are unmaintained.</li>
<li>There is no library at all.</li>
<li>Existing libraries do not fit our requirements.</li>
</ul>
<p>At this point, you could take the following paths.</p>
<h2 id="1-build-a-service-module-in-your-app">1 Build a service module in your app</h2>
<p>Maybe you just need to consume some endpoints and already use some HTTP client like Tesla. In this case, you can use the usual path:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">GitHub</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">Tesla</span>

  plug <span style="color:#a6e22e">Tesla.Middleware.BaseUrl</span>, <span style="color:#e6db74">&#34;https://api.github.com&#34;</span>
  plug <span style="color:#a6e22e">Tesla.Middleware.Headers</span>, [{<span style="color:#e6db74">&#34;authorization&#34;</span>, <span style="color:#e6db74">&#34;token xyz&#34;</span>}]
  plug <span style="color:#a6e22e">Tesla.Middleware.JSON</span>

  <span style="color:#66d9ef">def</span> user_repos(login) <span style="color:#66d9ef">do</span>
    get(<span style="color:#e6db74">&#34;/users/&#34;</span> <span style="color:#f92672">&lt;&gt;</span> login <span style="color:#f92672">&lt;&gt;</span> <span style="color:#e6db74">&#34;/repos&#34;</span>)
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><h2 id="2-build-a-custom-api-client">2 Build a custom API client</h2>
<p>Sometimes you want/need to do it yourself in a library way :). In this case you can follow the pattern mentioned in the <a href="https://dashbit.co/blog/goth-redesign">Goth redesign</a>.</p>
<h3 id="use-an-http-client-contract">Use an HTTP client contract:</h3>
<p>Using behaviours you can define a contract for the HTTP client. With this contract you can implement the contract and use the HTTP client library that you want:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">API.HTTPClient</span> <span style="color:#66d9ef">do</span>

  <span style="color:#a6e22e">@type</span> method() <span style="color:#f92672">::</span> atom()

  <span style="color:#a6e22e">@type</span> url() <span style="color:#f92672">::</span> binary()

  <span style="color:#a6e22e">@type</span> status() <span style="color:#f92672">::</span> non_neg_integer()

  <span style="color:#a6e22e">@type</span> header() <span style="color:#f92672">::</span> {binary(), binary()}

  <span style="color:#a6e22e">@type</span> body() <span style="color:#f92672">::</span> binary()

  <span style="color:#a6e22e">@doc</span> <span style="color:#e6db74">&#34;&#34;&#34;
</span><span style="color:#e6db74">  Callback to make an HTTP request.
</span><span style="color:#e6db74">  &#34;&#34;&#34;</span>
  <span style="color:#a6e22e">@callback</span> request(method(), url(), [header()], body(), opts <span style="color:#f92672">::</span> keyword()) <span style="color:#f92672">::</span>
              {<span style="color:#e6db74">:ok</span>, %{<span style="color:#e6db74">status</span>: status, <span style="color:#e6db74">headers</span>: [header()], <span style="color:#e6db74">body</span>: body()}}
              <span style="color:#f92672">|</span> {<span style="color:#e6db74">:error</span>, <span style="color:#a6e22e">Exception</span><span style="color:#f92672">.</span>t()}

  <span style="color:#a6e22e">@doc</span> <span style="color:#66d9ef">false</span>
  <span style="color:#66d9ef">def</span> request(module, method, url, headers, body, opts) <span style="color:#66d9ef">do</span>
    module<span style="color:#f92672">.</span>request(method, url, headers, body, opts)
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><h2 id="implement-the-http-client-contract">Implement the HTTP Client contract</h2>
<p>Hackney is a popular HTTP client library, so this can be the default implementation.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">API.HTTPClient.Hackney</span> <span style="color:#66d9ef">do</span>
  <span style="color:#a6e22e">@behaviour</span> <span style="color:#a6e22e">API.HTTPClient</span>

  <span style="color:#a6e22e">@impl</span> <span style="color:#66d9ef">true</span>
  <span style="color:#a6e22e">@spec</span> request(atom(), <span style="color:#a6e22e">String</span><span style="color:#f92672">.</span>t(), list(), <span style="color:#a6e22e">String</span><span style="color:#f92672">.</span>t(), <span style="color:#a6e22e">Keyword</span><span style="color:#f92672">.</span>t()) <span style="color:#f92672">::</span>
          {<span style="color:#e6db74">:ok</span>, map()} <span style="color:#f92672">|</span> {<span style="color:#e6db74">:error</span>, any()}
  <span style="color:#66d9ef">def</span> request(method, url, headers, body, opts) <span style="color:#66d9ef">do</span>
    with {<span style="color:#e6db74">:ok</span>, status, headers, body_ref} <span style="color:#f92672">&lt;-</span> <span style="color:#e6db74">:hackney</span><span style="color:#f92672">.</span>request(method, url, headers, body, opts),
         {<span style="color:#e6db74">:ok</span>, body} <span style="color:#f92672">&lt;-</span> <span style="color:#e6db74">:hackney</span><span style="color:#f92672">.</span>body(body_ref) <span style="color:#66d9ef">do</span>
      {<span style="color:#e6db74">:ok</span>, %{<span style="color:#e6db74">status</span>: status, <span style="color:#e6db74">headers</span>: headers, <span style="color:#e6db74">body</span>: body}}
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>Then you can set this as default in the api client config:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir">config <span style="color:#e6db74">:api</span>, <span style="color:#e6db74">http_client</span>: <span style="color:#a6e22e">API.HTTPClient.Hackney</span>
</code></pre></div><h3 id="using-the-http-client">Using the HTTP client</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">API.Resource</span> <span style="color:#66d9ef">do</span>

  <span style="color:#66d9ef">def</span> get_resource() <span style="color:#66d9ef">do</span>
    url <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;https://</span><span style="color:#e6db74">#{</span><span style="color:#a6e22e">Application</span><span style="color:#f92672">.</span>fetch_env!(<span style="color:#e6db74">:api</span>, <span style="color:#e6db74">:base_url</span>)<span style="color:#e6db74">}</span><span style="color:#e6db74">/api/&lt;resource&gt;&#34;</span>

    headers <span style="color:#f92672">=</span> build_headers()

    result <span style="color:#f92672">=</span>
      <span style="color:#e6db74">:api</span>
      <span style="color:#f92672">|&gt;</span> <span style="color:#a6e22e">Application</span><span style="color:#f92672">.</span>fetch_env!(<span style="color:#e6db74">:http_client</span>)
      <span style="color:#f92672">|&gt;</span> <span style="color:#a6e22e">HTTPClient</span><span style="color:#f92672">.</span>request(<span style="color:#e6db74">:get</span>, url, headers, <span style="color:#e6db74">&#34;&#34;</span>, [])
      <span style="color:#f92672">|&gt;</span> handle_response()

    <span style="color:#66d9ef">case</span> result <span style="color:#66d9ef">do</span>
      {<span style="color:#e6db74">:ok</span>, response} <span style="color:#f92672">-&gt;</span> {<span style="color:#e6db74">:ok</span>, response}
      {<span style="color:#e6db74">:error</span>, error} <span style="color:#f92672">-&gt;</span> {<span style="color:#e6db74">:error</span>, error}
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>

  <span style="color:#66d9ef">defp</span> build_headers() <span style="color:#66d9ef">do</span>
    [
      {<span style="color:#e6db74">&#34;content-type&#34;</span>, <span style="color:#e6db74">&#34;application/json&#34;</span>}
    ]
  <span style="color:#66d9ef">end</span>

  <span style="color:#66d9ef">defp</span> handle_response({<span style="color:#e6db74">:ok</span>, %{<span style="color:#e6db74">status</span>: status, <span style="color:#e6db74">body</span>: body}}) <span style="color:#f92672">when</span> status <span style="color:#f92672">in</span> [<span style="color:#ae81ff">200</span>, <span style="color:#ae81ff">201</span>] <span style="color:#66d9ef">do</span>
    <span style="color:#66d9ef">case</span> <span style="color:#a6e22e">Jason</span><span style="color:#f92672">.</span>decode(body) <span style="color:#66d9ef">do</span>
      {<span style="color:#e6db74">:ok</span>, attrs} <span style="color:#f92672">-&gt;</span> {<span style="color:#e6db74">:ok</span>, attrs}
      {<span style="color:#e6db74">:error</span>, reason} <span style="color:#f92672">-&gt;</span> {<span style="color:#e6db74">:error</span>, reason}
    <span style="color:#66d9ef">end</span>
  <span style="color:#66d9ef">end</span>

  <span style="color:#66d9ef">defp</span> handle_response({<span style="color:#e6db74">:ok</span>, response}) <span style="color:#66d9ef">do</span>
    {<span style="color:#e6db74">:error</span>, <span style="color:#a6e22e">Jason</span><span style="color:#f92672">.</span>decode!(response<span style="color:#f92672">.</span>body)}
  <span style="color:#66d9ef">end</span>

  <span style="color:#66d9ef">defp</span> handle_response({<span style="color:#e6db74">:error</span>, exception}) <span style="color:#66d9ef">do</span>
    {<span style="color:#e6db74">:error</span>, exception}
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><h3 id="custom-implementation">Custom implementation</h3>
<p>If you don&rsquo;t use hackney and you use another client like Finch or Mint instead, you can implement the contract yourself:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyApp.Extensions.API.FinchClient</span> <span style="color:#66d9ef">do</span>
  <span style="color:#a6e22e">@behaviour</span> <span style="color:#a6e22e">API.HTTPClient</span>

  <span style="color:#a6e22e">@impl</span> <span style="color:#66d9ef">true</span>
  <span style="color:#66d9ef">def</span> request(method, url, headers, body, opts, initial_state) <span style="color:#66d9ef">do</span>
    opts <span style="color:#f92672">=</span> <span style="color:#a6e22e">Keyword</span><span style="color:#f92672">.</span>merge(initial_state<span style="color:#f92672">.</span>default_opts, opts)

    <span style="color:#a6e22e">Finch</span><span style="color:#f92672">.</span>build(method, url, headers, body)
    <span style="color:#f92672">|&gt;</span> <span style="color:#a6e22e">Finch</span><span style="color:#f92672">.</span>request(initial_state<span style="color:#f92672">.</span>name, opts)
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>Then you can use the new implementation in your app config:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir">config <span style="color:#e6db74">:api</span>,
  <span style="color:#e6db74">http_client</span>: <span style="color:#a6e22e">MyApp.Extensions.API.FinchClient</span>,
  <span style="color:#e6db74">base_url</span>: <span style="color:#e6db74">&#34;...&#34;</span>
</code></pre></div><h3 id="advantages">Advantages:</h3>
<p>You can make the default http client dependency optional in your API client: <code>{:hackney, &quot;~&gt; 1.7&quot;, optional: true}</code></p>
<p>Who use the API client can pick their HTTP client, exists a lot of options to choose. Read some at: <a href="https://blog.appsignal.com/2020/07/28/the-state-of-elixir-http-clients.html">The State of Elixir HTTP Clients</a></p>
<p>Avoid duplication of dependencies. If you&rsquo;re already using hackney great! But if you are already using another client, you are not forced to download and use Hackney additionally.</p>
<p>Who use the API client can use the http client contract to test inside their app using <a href="https://github.com/dashbitco/mox">Mox</a>. More about this in: <a href="http://blog.plataformatec.com.br/2015/10/mocks-and-explicit-contracts/">Mocks and explicit contracts</a></p>
<p>Share code between the organization projects. Preventing writing the same functionality multiple times</p>
<p>Finally, you can see another example in the Tesla library:</p>
<ul>
<li><a href="https://github.com/teamon/tesla/blob/c1e0f2d031eb87a207db33333b8d4afc58384e87/lib/tesla.ex#L127">Tesla.Adapter</a> (behaviour)</li>
<li><a href="https://github.com/teamon/tesla/blob/master/lib/tesla/adapter/hackney.ex">Tesla.Adapter.Hackney</a> (contract implementation)</li>
</ul>
]]></content:encoded>
    </item>
    
    <item>
      <title>Procesando archivos de forma asíncrona con Elixir</title>
      <link>https://sergiovp.dev/blog/procesando-archivos-de-forma-as%C3%ADncrona-con-elixir/</link>
      <pubDate>Tue, 14 Jul 2020 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/procesando-archivos-de-forma-as%C3%ADncrona-con-elixir/</guid>
      <description>Como cargar y procesar archivos en segundo plano con Elixir/Phoenix</description>
      <content:encoded><![CDATA[<h2 id="escenario">Escenario</h2>
<p>A veces, no todo es un escenario perfecto y puede que para alimentar de información tu aplicación  necesitas; no conectarte a una API, si no subir archivos CSV&rsquo;s.</p>
<p>Si es el caso, no quieres que quien sea responsable de esta tarea tenga que subir de 100 en 100 registros por que si sube un archivo con 101, el servidor tarda demasiado en responder, el navegador muestra un error de timeout y la carga se interrumpe.</p>
<p>Es decir, lo que buscamos es procesar archivos CSV&rsquo;s grandes, con miles de registros.</p>
<h2 id="proceso-general">Proceso general</h2>
<p>Podemos resumir el proceso en los siguientes pasos</p>
<ol>
<li>El usuario envía un archivo al servicio</li>
<li>El servicio recibe el archivo y llamas un código similar a este</li>
</ol>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#a6e22e">Task</span><span style="color:#f92672">.</span>start(<span style="color:#66d9ef">fn</span> <span style="color:#f92672">-&gt;</span> <span style="color:#a6e22e">Modulo</span><span style="color:#f92672">.</span>procesar_csv(plug_upload) <span style="color:#66d9ef">end</span>)
</code></pre></div><ol start="3">
<li>El servicio contesta el request con algo como: &ldquo;archivo recibido&rdquo;</li>
</ol>
<p>La propuesta tiene 2 inconvenientes:</p>
<ul>
<li>Si el Task falla estamos perdidos, no tenemos forma de recuperarnos</li>
<li>Phoenix utiliza el plug <a href="https://hexdocs.pm/plug/Plug.Upload.html">Plug.Upload</a>, un GenServer para manejar los archivos cargados.</li>
</ul>
<p>El problema es que este archivo únicamente vive durante el ciclo de vida de la petición, por lo que si usamos algo como <a href="https://hexdocs.pm/elixir/Stream.html">Streams</a>, el archivo terminara despareciendo sin haber concluido el proceso.</p>
<p>Debido a la limitación anterior el flujo se vería modificado de la siguiente manera:</p>
<ol>
<li>El usuario envía un archivo al servidor</li>
<li>El servicio recibe el archivo y lo copia a una carpeta del sistema</li>
<li>Se ejecuta el procesamiento del archivo de forma asíncrona con una tarea supervisada (utilizaremos <code>Task.Supervisor.async_nolink</code>)</li>
<li>El servicio responde la petición</li>
<li>Al terminar de procesar el archivo, este es borrado del servidor</li>
</ol>
<h3 id="configuración">Configuración</h3>
<p>Primero necesitamos agregar <code>Task.Supervisor</code> al supervisor de nuestra aplicación. Si usamos Phoenix lo encontraremos en el archivo <code>my_app/lib/app/application.ex</code>:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">def</span> start(_type, _args) <span style="color:#66d9ef">do</span>
    <span style="color:#75715e"># List all child processes to be supervised</span>
    children <span style="color:#f92672">=</span> [
      <span style="color:#75715e"># Start the Ecto repository</span>
      <span style="color:#a6e22e">App.Repo</span>,
      <span style="color:#75715e"># Start the PubSub system</span>
      {<span style="color:#a6e22e">Phoenix.PubSub</span>, <span style="color:#e6db74">name</span>: <span style="color:#a6e22e">App.PubSub</span>},
      <span style="color:#75715e"># Start the endpoint when the application starts</span>
      <span style="color:#a6e22e">AppWeb.Endpoint</span>,
      {<span style="color:#a6e22e">Task.Supervisor</span>, <span style="color:#e6db74">name</span>: <span style="color:#a6e22e">App.TaskSupervisor</span>},
    ]

    <span style="color:#75715e"># See https://hexdocs.pm/elixir/Supervisor.html</span>
    <span style="color:#75715e"># for other strategies and supported options</span>
    opts <span style="color:#f92672">=</span> [<span style="color:#e6db74">strategy</span>: <span style="color:#e6db74">:one_for_one</span>, <span style="color:#e6db74">name</span>: <span style="color:#a6e22e">App.Supervisor</span>]
    <span style="color:#a6e22e">Supervisor</span><span style="color:#f92672">.</span>start_link(children, opts)
  <span style="color:#66d9ef">end</span>

</code></pre></div><h3 id="guardar-el-archivo-en-el-servidor">Guardar el archivo en el servidor</h3>
<p>Como dijimos que queremos guardar el archivo en el servidor para poder procesarlo mas tarde sin mantener la conexión con el cliente abierta, creamos un pequeño modulo para esto:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyApp.Files</span> <span style="color:#66d9ef">do</span>
	<span style="color:#a6e22e">@spec</span> move_to_server(<span style="color:#a6e22e">Plug.Upload</span><span style="color:#f92672">.</span>t()) <span style="color:#f92672">::</span> {<span style="color:#e6db74">:ok</span>, <span style="color:#a6e22e">String</span><span style="color:#f92672">.</span>t())
	<span style="color:#66d9ef">def</span> move_to_server(%<span style="color:#a6e22e">Plug.Upload</span>{} <span style="color:#f92672">=</span> upload_params) <span style="color:#66d9ef">do</span>
		tmp_dir <span style="color:#f92672">=</span> <span style="color:#a6e22e">System</span><span style="color:#f92672">.</span>tmp_dir!() <span style="color:#75715e"># Nos da un directorio temporal de escritura</span>

    extension <span style="color:#f92672">=</span> <span style="color:#a6e22e">Path</span><span style="color:#f92672">.</span>extname(upload_params<span style="color:#f92672">.</span>filename)
    tmp_file <span style="color:#f92672">=</span> <span style="color:#a6e22e">Path</span><span style="color:#f92672">.</span>join(tmp_dir, <span style="color:#e6db74">&#34;</span><span style="color:#e6db74">#{</span><span style="color:#a6e22e">Ecto.UUID</span><span style="color:#f92672">.</span>generate()<span style="color:#e6db74">}#{</span>extension<span style="color:#e6db74">}</span><span style="color:#e6db74">&#34;</span>)

		<span style="color:#a6e22e">File</span><span style="color:#f92672">.</span>cp(upload_params<span style="color:#f92672">.</span>path, tmp_file)
	<span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><h3 id="server">Server</h3>
<p>Ahora crearemos un server, este se encargara de llamar a la tarea  y verificar su estatus</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-elixir" data-lang="elixir"><span style="color:#66d9ef">defmodule</span> <span style="color:#a6e22e">MyApp.FileServer</span> <span style="color:#66d9ef">do</span>
  <span style="color:#f92672">use</span> <span style="color:#a6e22e">GenServer</span>

	<span style="color:#f92672">alias</span> <span style="color:#a6e22e">MyApp.Files</span>

  <span style="color:#66d9ef">def</span> start_task <span style="color:#66d9ef">do</span>
    <span style="color:#a6e22e">GenServer</span><span style="color:#f92672">.</span>call(__MODULE__, <span style="color:#e6db74">:start_task</span>)
  <span style="color:#66d9ef">end</span>

  <span style="color:#75715e"># In this case the task is already running, so we just return :ok.</span>
  <span style="color:#66d9ef">def</span> handle_call(<span style="color:#e6db74">:start_task</span>, _from, %{<span style="color:#e6db74">ref</span>: ref} <span style="color:#f92672">=</span> state) <span style="color:#f92672">when</span> is_reference(ref) <span style="color:#66d9ef">do</span>
    {<span style="color:#e6db74">:reply</span>, <span style="color:#e6db74">:ok</span>, state}
  <span style="color:#66d9ef">end</span>

  <span style="color:#75715e"># The task is not running yet, so let&#39;s start it.</span>
  <span style="color:#66d9ef">def</span> handle_call({<span style="color:#e6db74">:process_file</span>, upload_params}, _from, %{<span style="color:#e6db74">ref</span>: <span style="color:#66d9ef">nil</span>} <span style="color:#f92672">=</span> state) <span style="color:#66d9ef">do</span>
		{<span style="color:#e6db74">:ok</span>, path_to_file} <span style="color:#f92672">=</span> <span style="color:#a6e22e">Files</span><span style="color:#f92672">.</span>move_to_server(upload_params)
    task <span style="color:#f92672">=</span>
      <span style="color:#a6e22e">Task.Supervisor</span><span style="color:#f92672">.</span>async_nolink(<span style="color:#a6e22e">MyApp.TaskSupervisor</span>, <span style="color:#66d9ef">fn</span> <span style="color:#f92672">-&gt;</span>
        <span style="color:#a6e22e">Modulo</span><span style="color:#f92672">.</span>procesar_csv(path_to_file)
      <span style="color:#66d9ef">end</span>)

    <span style="color:#75715e"># We return :ok and the server will continue running</span>
    {<span style="color:#e6db74">:reply</span>, <span style="color:#e6db74">:ok</span>, %{state <span style="color:#f92672">|</span> <span style="color:#e6db74">ref</span>: task<span style="color:#f92672">.</span>ref}}
  <span style="color:#66d9ef">end</span>

  <span style="color:#75715e"># The task completed successfully</span>
  <span style="color:#66d9ef">def</span> handle_info({ref, answer}, %{<span style="color:#e6db74">ref</span>: ref} <span style="color:#f92672">=</span> state) <span style="color:#66d9ef">do</span>
    <span style="color:#75715e"># We don&#39;t care about the DOWN message now, so let&#39;s demonitor and flush it</span>
    <span style="color:#a6e22e">Process</span><span style="color:#f92672">.</span>demonitor(ref, [<span style="color:#e6db74">:flush</span>])
    <span style="color:#75715e"># Do something with the result and then return</span>
    {<span style="color:#e6db74">:noreply</span>, %{state <span style="color:#f92672">|</span> <span style="color:#e6db74">ref</span>: <span style="color:#66d9ef">nil</span>}}
  <span style="color:#66d9ef">end</span>

  <span style="color:#75715e"># The task failed</span>
  <span style="color:#66d9ef">def</span> handle_info({<span style="color:#e6db74">:DOWN</span>, ref, <span style="color:#e6db74">:process</span>, _pid, _reason}, %{<span style="color:#e6db74">ref</span>: ref} <span style="color:#f92672">=</span> state) <span style="color:#66d9ef">do</span>
    <span style="color:#75715e"># Log and possibly restart the task...</span>
    {<span style="color:#e6db74">:noreply</span>, %{state <span style="color:#f92672">|</span> <span style="color:#e6db74">ref</span>: <span style="color:#66d9ef">nil</span>}}
  <span style="color:#66d9ef">end</span>
<span style="color:#66d9ef">end</span>
</code></pre></div><p>Este server, ejecuta una tarea, guarda en el estado la referencia de esta tarea. Si ya tiene una tarea en el estado y recibe una nueva llamada responde un <code>:ok</code> pero no toma la nueva tarea.</p>
<p>Este comportamiento es útil en mi caso, ya que los registros van a la base de datos, procesar un solo archivo a la vez evita tener problemas de recursos (por ejemplo).</p>
<p>Cuando la tarea termina o falla, se recibe el mensaje <code>:DOWN</code>, si la tarea se completo <code>reason</code> será <code>:normal</code>. En caso contrario podemos notificar o manejar el caso según lo necesitemos cuando sabemos que la tarea fallo.</p>
<p>En ambos casos, limpia el estado y ahora puede recibir un nuevo archivo para ser procesado.</p>
<p>En mi caso particular, notifico por email cuando la tarea finalizo y si hubo errores los recolecto en un CSV que se adjunta al email.</p>
<p>Links útiles:</p>
<p><a href="https://dockyard.com/blog/2016/05/02/phoenix-tips-and-tricks">https://dockyard.com/blog/2019/02/13/understanding-test-concurrency-in-elixir</a></p>
<p><a href="https://hexdocs.pm/elixir/Task.Supervisor.html#async_nolink/3-compatibility-with-otp-behaviours">https://hexdocs.pm/elixir/Task.Supervisor.html#async_nolink/3-compatibility-with-otp-behaviours</a></p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Validación de datos con PHP y filter_var()</title>
      <link>https://sergiovp.dev/blog/validaci%C3%B3n-de-datos-con-php-y-filter_var/</link>
      <pubDate>Wed, 17 Jun 2015 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/validaci%C3%B3n-de-datos-con-php-y-filter_var/</guid>
      <description>Como validar la entrada de datos utilizando filter_var() en PHP</description>
      <content:encoded><![CDATA[<p>Ahora que sabemos como sanear variables en PHP aprenderemos a validar datos comunes utilizando la función filter_var() y sus filtro predefinidos.</p>
<p>A diferencia de la entrada pasada utilizaremos los filtros de validación en lugar de los de saneamiento. Recordemos el formulario del ejemplo anterior:</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-php" data-lang="php"><span style="color:#f92672">&lt;!</span><span style="color:#a6e22e">DOCTYPE</span> <span style="color:#a6e22e">html</span><span style="color:#f92672">&gt;</span>
<span style="color:#f92672">&lt;</span><span style="color:#a6e22e">html</span><span style="color:#f92672">&gt;</span>
<span style="color:#f92672">&lt;</span><span style="color:#a6e22e">head</span><span style="color:#f92672">&gt;</span>
    <span style="color:#f92672">&lt;</span><span style="color:#a6e22e">meta</span> <span style="color:#a6e22e">charset</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;utf-8&#34;</span><span style="color:#f92672">&gt;</span>
    <span style="color:#f92672">&lt;</span><span style="color:#a6e22e">meta</span> <span style="color:#a6e22e">http</span><span style="color:#f92672">-</span><span style="color:#a6e22e">equiv</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;X-UA-Compatible&#34;</span> <span style="color:#a6e22e">content</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;IE=edge&#34;</span><span style="color:#f92672">&gt;</span>
    <span style="color:#f92672">&lt;</span><span style="color:#a6e22e">title</span><span style="color:#f92672">&gt;</span><span style="color:#a6e22e">Formulario</span> <span style="color:#a6e22e">sencillo</span><span style="color:#f92672">&lt;/</span><span style="color:#a6e22e">title</span><span style="color:#f92672">&gt;</span>
    <span style="color:#f92672">&lt;</span><span style="color:#a6e22e">link</span> <span style="color:#a6e22e">rel</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;stylesheet&#34;</span> <span style="color:#a6e22e">href</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;&#34;</span><span style="color:#f92672">&gt;</span>
<span style="color:#f92672">&lt;/</span><span style="color:#a6e22e">head</span><span style="color:#f92672">&gt;</span>
<span style="color:#f92672">&lt;</span><span style="color:#a6e22e">body</span><span style="color:#f92672">&gt;</span>
    <span style="color:#f92672">&lt;</span><span style="color:#a6e22e">form</span> <span style="color:#a6e22e">action</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;datos.php&#34;</span> <span style="color:#a6e22e">method</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;POST&#34;</span> <span style="color:#f92672">&gt;</span>

        <span style="color:#f92672">&lt;</span><span style="color:#a6e22e">label</span><span style="color:#f92672">&gt;</span> <span style="color:#a6e22e">Nombre</span> <span style="color:#f92672">&lt;/</span><span style="color:#a6e22e">label</span><span style="color:#f92672">&gt;</span>
        <span style="color:#f92672">&lt;</span><span style="color:#a6e22e">input</span> <span style="color:#a6e22e">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;text&#34;</span> <span style="color:#a6e22e">name</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;nombre&#34;</span> <span style="color:#a6e22e">required</span><span style="color:#f92672">&gt;</span>

        <span style="color:#f92672">&lt;</span><span style="color:#a6e22e">label</span><span style="color:#f92672">&gt;</span> <span style="color:#a6e22e">Telefono</span> <span style="color:#f92672">&lt;/</span><span style="color:#a6e22e">label</span><span style="color:#f92672">&gt;</span>
        <span style="color:#f92672">&lt;</span><span style="color:#a6e22e">input</span> <span style="color:#a6e22e">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;text&#34;</span> <span style="color:#a6e22e">name</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;telefono&#34;</span> <span style="color:#a6e22e">required</span><span style="color:#f92672">&gt;</span>

        <span style="color:#f92672">&lt;</span><span style="color:#a6e22e">label</span><span style="color:#f92672">&gt;</span> <span style="color:#a6e22e">Email</span> <span style="color:#f92672">&lt;/</span><span style="color:#a6e22e">label</span><span style="color:#f92672">&gt;</span>
        <span style="color:#f92672">&lt;</span><span style="color:#a6e22e">input</span> <span style="color:#a6e22e">type</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;text&#34;</span> <span style="color:#a6e22e">name</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;email&#34;</span> <span style="color:#a6e22e">required</span><span style="color:#f92672">&gt;</span>

        <span style="color:#f92672">&lt;</span><span style="color:#a6e22e">label</span><span style="color:#f92672">&gt;</span> <span style="color:#a6e22e">Mensaje</span> <span style="color:#f92672">&lt;/</span><span style="color:#a6e22e">label</span><span style="color:#f92672">&gt;</span>
         <span style="color:#f92672">&lt;</span><span style="color:#a6e22e">textarea</span> <span style="color:#a6e22e">rows</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;4&#34;</span> <span style="color:#a6e22e">cols</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;0&#34;</span> <span style="color:#a6e22e">name</span><span style="color:#f92672">=</span><span style="color:#e6db74">&#34;mensaje&#34;</span> <span style="color:#a6e22e">required</span><span style="color:#f92672">&gt;</span>

        <span style="color:#f92672">&lt;/</span><span style="color:#a6e22e">textarea</span><span style="color:#f92672">&gt;</span>

    <span style="color:#f92672">&lt;/</span><span style="color:#a6e22e">form</span><span style="color:#f92672">&gt;</span>
<span style="color:#f92672">&lt;/</span><span style="color:#a6e22e">body</span><span style="color:#f92672">&gt;</span>
<span style="color:#f92672">&lt;/</span><span style="color:#a6e22e">html</span><span style="color:#f92672">&gt;</span>
</code></pre></div><p>Después de utilizar filter_var() para sanear los datos podemos utilizarla para validarlos como sigue:</p>
<p>datos.php</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-php" data-lang="php"><span style="color:#f92672">&lt;?</span><span style="color:#a6e22e">php</span>

$nombre <span style="color:#f92672">=</span> $_POST[<span style="color:#e6db74">&#39;nombre&#39;</span>];
$telefono <span style="color:#f92672">=</span> $_POST[<span style="color:#e6db74">&#39;telefono&#39;</span>];
$email <span style="color:#f92672">=</span> $_POST[<span style="color:#e6db74">&#39;email&#39;</span>];
$mensaje <span style="color:#f92672">=</span> $_POST[<span style="color:#e6db74">&#39;mensaje&#39;</span>];

<span style="color:#a6e22e">filter_var</span>($telefono,<span style="color:#a6e22e">FILTER_VALIDATE_INT</span>);

<span style="color:#66d9ef">if</span> ( <span style="color:#a6e22e">filter_var</span>($email,<span style="color:#a6e22e">FILTER_VALIDATE_EMAIL</span>) )
{
    <span style="color:#66d9ef">echo</span> <span style="color:#e6db74">&#34;El email es valido!&#34;</span>;

} <span style="color:#66d9ef">else</span> {

    <span style="color:#66d9ef">echo</span> <span style="color:#e6db74">&#34;El email es invalido&#34;</span>;

}
</code></pre></div><p>La principal limitación es la cantidad limitada de filtros para validar. En nuestro ejemplo solo podemos validar el email y un numero entero de esta forma.</p>
<p>Sin embargo no nos quedaremos atrapados ya que la misma función nos permite validar utilizando expresiones regulares.</p>
<p>En la siguiente entrada veremos esta opción y una serie de regex para validar datos comunes.</p>
<p>Adicionalmente puedes ver los otros filtros disponibles (bastante útiles) en:
filters_validate</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Proteger el SSH con Latch</title>
      <link>https://sergiovp.dev/blog/proteger-el-ssh-con-latch/</link>
      <pubDate>Thu, 23 Apr 2015 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/proteger-el-ssh-con-latch/</guid>
      <description>Proteger el SSH con Latch</description>
      <content:encoded><![CDATA[<p>En este manual les mostrare como instalar y configurar la herramienta Latch de ElevenPaths para mejorar la seguridad de nuestro servidor GNU/Linux, protegeremos el acceso vía <strong>SSH</strong>.</p>
<p>Basicamente Latch se define como un &ldquo;cerrojo&rdquo; para nuestras <strong>identidades virtuales</strong>, puedes incluirlo en muchisimos sitios mediante plugins listos para usar (Wordpress, owncloud, joomla, prestashop, etc), en sitios donde ya esta implementada la funcion como Tuenti o mediante el SDK que ponen a disposición de los desarrolladores en multiples lenguajes.</p>
<p>Con esta herramienta podemos agregar un <strong>segundo factor de autenticación</strong> o <strong>2FA</strong>, basicamente el servicio protegido por esta herramienta solo estara disponible si tenemos el cerrojo abierto, en caso contrario sera imposible acceder a el.</p>
<p>Tambien puede usarse como otras aplicaciónes tipo <em>Google Authenticator</em>, con passwords de un solo uso, aúnque a mi parecer la primera opción es bastante buena y la segunda solo esta disponible en la versión pro.</p>
<p>Por otra parte si bien hay otros tutoriales para realizar esta misma tarea (como <a href="http://www.dexa-dev.com/securizando-un-vps-con-latch-i/">este</a> o <a href="http://www.hackplayers.com/2014/01/securizando-openssh-mode-paranoico-2.html">este otro</a>  , por lo que he visto se basan en un plugin anterior que era exclusivo para <strong>SSH</strong>, despues este se elimino y su funcionalidad se añadio a la de otros plugins en uno especifico para UNIX, por lo que con el animo de aportar me animo a realizar esta entrada.</p>
<p>Bien, lo primero es que <em>ElevenPaths</em> nos proporciona el latch-plugin-unix, que incorpora varias funcionalidades para sistemas UNIX, podemos proteger cuentas de usuario, SSH, FTP, etc, aunque solo lo he utilizado para la segunda opción.</p>
<p>El manual se divide en las siguientes partes para su mejor lectura:</p>
<ol>
<li>Crear una cuenta de desarrollador y una app</li>
<li>Instalar las librerias necesarias</li>
<li>Instalación y configuración del plugin</li>
<li>Configurar el plugin en SSH</li>
<li>Parear cuenta</li>
<li>Eliminar cuenta y plugin</li>
</ol>
<h3 id="1crear-una-cuenta-de-desarrollador">1.Crear una cuenta de desarrollador:</h3>
<ol>
<li>Necesitamos registrarnos en la <a href="https://latch.elevenpaths.com">web oficial</a> como desarrolladores: Esto nos dara acceso a las distintas herramientas necesarias:
<ul>
<li>Ingresar en &ldquo;Área de desarroladores&rdquo;</li>
<li>Clic en el botón &ldquo;Reistrarse como desarrollador&rdquo;</li>
</ul>
</li>
<li>Crear una nueva aplicación: Una vez registrados, accedemos al area para desarrolladores, creamos una nueva app, le damos un nombre
<img loading="lazy" src="https://res.cloudinary.com/escribocodigo/image/upload/c_scale,h_262/v1428901247/crear_app_latch_lsprmo.png" alt="Crear app latch"  />
</li>
<li>Por ultimo necesitamos guardar dos datos importantes, el <em>ID de aplicación</em> y la <em>secret_key</em>, los datos que se muestran en la imagen.
<img loading="lazy" src="https://res.cloudinary.com/escribocodigo/image/upload/c_scale,h_255/v1428901249/Datos_de_la_app_unvvbb.png" alt="Datos necesarios para latch"  />
</li>
</ol>
<ul>
<li>Aqui tenemos otra opción:
<ul>
<li>Podemos crear &ldquo;operaciones&rdquo;, para poder controlar acciones por separado, como comente con este plugin podemos proteger tanto el servicio SSH, como las cuentas de usuario, el comando sudo, etc, creando acciones podemos manejarlas de manera separada.
<ul>
<li>Sin embargo estas no son obligatorias, en caso de querer hacer una para el ssh en especifico seria de la siguiente manera:</li>
</ul>
</li>
<li>Creamos una operacion llamada &ldquo;sshd-login&rdquo; con OTP y LOR desabilitados. Al añadir la operación se creara un id que aparece junto al nombre entre [], tambien lo necesitaremos.</li>
</ul>
</li>
</ul>
<ol start="3">
<li>Instalar la app de latch para Android, IOS o Windows Phone dependiendo de donde la utilizaremos, desde ahí controlaremos los &ldquo;cerrojos&rdquo;.
<img loading="lazy" src="https://res.cloudinary.com/escribocodigo/image/upload/c_scale,h_302/v1428901250/latch_android_jyd5in.jpg" alt="Latch para Android"  />
</li>
<li>Iniciar Sesión en la aplicación movil utilizando los datos con los que nos registramos en la web de la herramienta.</li>
</ol>
<h3 id="2-librerias-necesarias">2. Librerias necesarias:</h3>
<p>Ahora ingresamos a nuestro servidor, antes de comenzar todo el proceso es necesario satisfacer algunas dependencias del plugin, librerias necesarias para su funcionamiento, para ello utilizaremos los siguientes comandos:</p>
<p><code>sudo apt-get install gcc make</code></p>
<p><code>sudo apt-get install libpam0g-dev libcurl4-openssl-dev libssl-dev</code></p>
<h3 id="3-instalación-y-configuración-del-plugin">3. Instalación y configuración del plugin:</h3>
<ol>
<li>
<p>Necesitaremos descargar el plugin desde <a href="https://github.com/ElevenPaths/latch-plugin-unix">https://github.com/ElevenPaths/latch-plugin-unix</a>.</p>
<ul>
<li>Es necesario tener instalado git en el servidor, en caso de no tenerlo ejecutar: <code>sudo apt-get install git</code></li>
</ul>
</li>
<li>
<p>Ahora clonamos el repositorio:
<code>git clone https://github.com/ElevenPaths/latch-plugin-unix.git</code></p>
</li>
<li>
<p>Ingresamos a la carpeta donde se descargo el proyecto:
<code>cd latch-plugin-unix</code></p>
</li>
<li>
<p>Ejecutamos el siguiente comando para instalar:
<code>./configure prefix=/usr sysconfdir=/etc &amp;&amp; make &amp;&amp; sudo make install</code></p>
</li>
<li>
<p>Editamos el archivo de configuración donde agregaremos la información que obtuvimos al crear la app en la web de Latch, el  &ldquo;Application ID&rdquo; y &ldquo;SecretKey&rdquo; con el comando:</p>
</li>
</ol>
<p><code>sudo nano /etc/latch/latch.conf</code></p>
<p>Tendremos el siguiente contenido en la terminal:</p>
<pre tabindex="0"><code class="language-language-bash" data-lang="language-bash"># Identify your Application
# Application ID value
#
app_id = REPLACE_APP_ID_HERE

# Secret key value
#
secret_key = REPLACE_SECRET_KEY_HERE
</code></pre><p>Solo hace falta reemplazar los valores correspondientes.</p>
<p>Opcional:</p>
<ul>
<li>Si se creo la operacion <em>sshd-login</em> en la sección #Operations, buscamos la linea:</li>
<li>&lsquo;sshd-login = REPLACE_OPERATION_ID_HERE&rsquo;</li>
<li>Reemplazamos el texto &ldquo;REPLACE_OPERATION_ID_HERE&rdquo; por el id que obtuvimos al crear la operación.</li>
</ul>
<p>Una vez editados los campos, &ldquo;Ctrl + X&rdquo; para cerrar, &ldquo;Y&rdquo; para confirmar los cambios y un ultimo &ldquo;Enter&rdquo; para guardar el archivo.</p>
<ol start="6">
<li>Ahora es necesario mover el archivo <code>pam_latch.so</code> con el comando:
<code>sudo mv /usr/lib/pam_latch.so /lib*/*/security</code></li>
</ol>
<h3 id="4-configurar-el-plugin-en-el-ssh">4. Configurar el plugin en el SSH:</h3>
<ol>
<li>
<p>Editamos el siguiente archivo:
<code>sudo nano /etc/pam.d/sshd</code></p>
</li>
<li>
<p>Debajo de <code>@include common-auth</code>, agregamos la siguiente linea:
<code>auth	   required	pam_latch.so config=/etc/latch/latch.conf accounts=/etc/latch/latch.accounts operation=sshd-login otp=no</code>.</p>
<ul>
<li>Las opciones <code>operation=sshd-login otp=no</code>, solo se agregaran en caso de haber configurado la acción <em>sshd-login</em>, en caso contrario las omitimos.</li>
</ul>
</li>
<li>
<p>Editamos el archivo sshd_config mediante:
<code>sudo nano /etc/ssh/sshd_config </code></p>
<ul>
<li>
<p>Buscamos la linea <code>ChallengeResponseAuthentication</code>, la cual debe quedar así:
<code>ChallengeResponseAuthentication yes</code></p>
</li>
<li>
<p>Buscamos <code>PasswordAuthentication</code> y debe quedar:
<code>PasswordAuthentication no</code></p>
</li>
<li>
<p>Nos aseguramos que al final del archivo este la linea:
<code>UsePAM yes</code></p>
</li>
<li>
<p>Si alguna linea esta comentada con <code>#</code> al inicio, se le quita y se cambia el valor al que se menciona.</p>
</li>
</ul>
</li>
<li>
<p>Ahora reiniciamos el servidor SSH:</p>
</li>
</ol>
<p><code>sudo service ssh restart</code></p>
<h3 id="5-el-ultimo-paso-es-parear-la-cuenta">5. El ultimo paso es parear la cuenta:</h3>
<p>Para esto son necesarios los siguientes dos pasos, ya con la aplicación instalada en nuestro movil:</p>
<ol>
<li>En el móvil:
<ul>
<li>Abrir la aplicación</li>
<li>Usamos la opción agregar un nuevo servicio y luego generar nuevo codigo.
<img loading="lazy" src="https://res.cloudinary.com/escribocodigo/image/upload/c_scale,h_331/v1428901249/generar_codigo_wtr078.jpg" alt="Agregar un nuevo servicio"  />
</li>
</ul>
</li>
<li>En la terminal con la sesión de servidor ejecutamos el comando:</li>
</ol>
<p><code>latch -p CÓDIGO</code></p>
<ul>
<li>CÓDIGO es el que nos proporciona la app móvil.</li>
</ul>
<p>Nos devolvera el mensaje:</p>
<p><code>Account successfully paired to the user USUARIO</code></p>
<p>Además deberia saltar una alerta en el movil, informando sobre la acción, elegimos la opcion &ldquo;setup now&rdquo;, elegimos si quieremos utilizar alguna de las opciones y listo.</p>
<p>####Ahora solo resta probar si todo funciona correctamente:
Si el servicio esta desbloqueado y accedemos recibiremos una alerta como la siguiente:</p>
<p><img loading="lazy" src="https://res.cloudinary.com/escribocodigo/image/upload/c_scale,h_360/v1428901249/cerrojo_abierto_ulzekd.jpg" alt="Acceso con latch abierto"  />
</p>
<p>En cambio si esta bloqueado y damos los datos correctos de acceso sera como la siguiente:</p>
<p><img loading="lazy" src="https://res.cloudinary.com/escribocodigo/image/upload/c_scale,h_360/v1428901249/cerrojo_cerrado_q0hc5p.jpg" alt="Acceso con latch cerrado"  />
</p>
<p>Y veremos que no nos permite acceder.</p>
<p>Recordemos que latch no avisa de intentos de acceso, solo cuando se produce un login exitoso, es decir con los datos correctos.</p>
<h3 id="6-dejar-de-usar-latch">6. Dejar de usar latch:</h3>
<p>Podemos desemparejar una cuenta con el comando:
<code>latch -u</code></p>
<p>Esta acción arrojara el mensaje:
<code>Account belonging to the user USUARIO successfully unpaired from latch</code></p>
<ul>
<li>Para desinstalar el plugin se deben revertir los cambios realizados en los ficheros, acceder a la carpeta del plugin y ejecutar el siguiente comando:
<code>./configure prefix=/usr sysconfdir=/etc &amp;&amp; make &amp;&amp; sudo make uninstall</code></li>
</ul>
<p>Para información adicional:
No pueden perderse la serie de entradas en el blog de El Maligno: <a href="http://www.elladodelmal.com/2013/12/como-proteger-las-identidades-digitales.html">Latch: Cómo proteger las identidades digitales</a>
No dejen de revisar el GitHub donde encontraran muchisimo codigo he información: <a href="https://github.com/ElevenPaths">https://github.com/ElevenPaths</a>
En diversos blogs hay tutoriales para utilizar Latch en diversos sitios, en el mismo blog oficial o en elladodelmal hay muchos post con información muy valiosa sobre esta herramienta.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>5 aplicaciones para mantener la privacidad en las comunicaciones</title>
      <link>https://sergiovp.dev/blog/5-aplicaciones-para-mantener-la-privacidad-en-las-comunicaciones/</link>
      <pubDate>Thu, 16 Apr 2015 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/5-aplicaciones-para-mantener-la-privacidad-en-las-comunicaciones/</guid>
      <description>5 aplicaciones para mantener la privacidad en las comunicaciones</description>
      <content:encoded><![CDATA[<p>La <strong>privacidad</strong> es un tema delicado que se debe tratar con pinzas, sin embargo mas allá de la opinión de cada uno considero que esta es necesaria, de manera personal o corporativa por ejemplo. Aunque hay momentos en el que el tema explota y todos hablan de el, luego va bajando y salen algun texto por aquí y por allá</p>
<p>Aún así es algo de lo que considero no debería dejar de hablarse, siempre sera un tema que para bien o para mal se retomara, ya sea porque se han hecho avances en esta materia o porque hubo un nuevo intento de algún país para tener acceso libre e ilimitado a toda nuestra información personal.</p>
<p>Por cierto, los animo a leer el siguiente articulo, simplemente excelente: <a href="http://www.xataka.com/otros/por-que-la-privacidad-es-necesaria-desmontando-el-no-tengo-nada-que-ocultar">Por qué la privacidad es necesaria: desmontando el &ldquo;no tengo nada que ocultar&rdquo;</a>.</p>
<p>Así que aquí muestro algunas recomendaciones para ayudar a <strong>mejorar la privacidad</strong> (que no anonimato) o <strong>seguridad</strong> de nuestras <strong>comunicaciones</strong> por distintas vías:</p>
<h2 id="1-mensajería-privada">1. Mensajería privada</h2>
<p>En la mensajería móvil tenemos un líder indiscutible que es <em>WhatsApp</em> y su rival <em>Telegram</em>, el segundo es el que más se dice respetuoso de nuestra privacidad y preocupados por la seguridad, pero desde el inicio surgieron varias dudas al respecto, principalmente por el hecho de utilizar un protocolo propio (basado en otro) en lugar de uno abierto y probado por su seguridad, por eso la recomendación es:</p>
<ul>
<li><a href="https://whispersystems.org/#privacy">TextSecure</a> (Android y Signal para IOS)</li>
</ul>
<p>En un principio esta app proporcionaba incluso cifrado para SMS, pero le han dicho <a href="https://whispersystems.org/blog/goodbye-encrypted-sms/">adiós</a>, aún así nos proporciona cifrado en los mensajes enviados mediante datos móviles o WiFi, por lo que nuestra privacidad se vera mejorada, además es de código.</p>
<p>Otro aspecto es que whatsapp trabajo junto a whispersystems (desarrolladores de Textsecure entre otras apps) para proveer mejoras a la seguridad de su propia aplicación, al menos para las conversaciones 1 a 1.</p>
<ul>
<li><a href="https://pidgin.im">Pidgin + OTR</a> (Windows, Linux)</li>
</ul>
<p>A pidgin, cuantos no te conocen :), aunque este tipo de comunicaciones tipo messenger hace mucho que se &ldquo;extinguieron&rdquo;, siempre podemos utilizar software como pidgin que es multiprotocolo y utilizar nuestras cuentas de correo, XMPP o incluso facebook para comunicarnos con nuestros contactos y si a esto le agregamos el plugin <strong>Off-the-Record Messaging (OTR)</strong> tendremos nuestros mensajes cifrados.</p>
<h2 id="2-llamadas">2. Llamadas</h2>
<p>Aunque para <strong>cifrar las llamadas</strong> comunes que realizamos utilizando nuestra operadora existe hardware especializado, este es caro y poco accesible, para este tema nuestra mejor opción es hacer uso de datos móviles o una conexión WiFi y utilizar alguna aplicación estilo <strong>Line o Viber</strong> pero con énfasis en la seguridad:</p>
<ul>
<li>RedPhone (Linux, Singal en IOS)</li>
</ul>
<p>De la misma gente que <em>TextSecure</em>, nos proporciona llamadas cifradas, ambas aplicaciones utilizan nuestro <strong>numero telefónico</strong>, además si llamamos a un contacto de forma normal y este tiene <strong>RedPhone</strong> instalado nos dará la opción de realizar la llamada utilizándolo. para mas información recomiendo el siguiente articulo (muy completo) <a href="http://www.hackplayers.com/2013/06/como-llamar-por-telefono-conservando-la-privacidad.html">Cómo llamar por teléfono conservando la privacidad con Redphone </a></p>
<h3 id="alternativas-a-skype">Alternativas a Skype</h3>
<p>Skype es una herramienta muy útil, aunque no muy recomendada por aquellos que se preocupan de la privacidad, aquí no hay muchas opciones pero en lo personal he probado <strong>jitsi</strong>:</p>
<ul>
<li><a href="https://jitsi.org/">Jitsi</a> (Windows, Linux, Mac)</li>
</ul>
<p>Es un cliente multiprotocolo opensource (al igual que las anteriores), nos permite mantener chat y llamadas de voz o vídeo cifradas, bastante bueno en general. Lo mas próximo además de esta alternativa quizá sea <a href="http://blog.desdelinux.net/tox-la-alternativa-open-source-skype/">Tox</a>, aunque aún esta en desarrollo.</p>
<h2 id="3-correo-electrónico">3. Correo electrónico</h2>
<p>El <strong>email</strong> continua siendo un imprescindible en las comunicaciones, muchas veces se envía información sensible como datos bancarios, documentación personal entre otros sin ningún tipo de protección, pero podemos fácilmente mejorar esto.</p>
<ul>
<li>PGP</li>
</ul>
<p>Aquí nuestra mejor alternativa es utilizar <strong>GnuPG</strong>, una implementación libre de <strong>GPG</strong> que nos permite cifrar de forma asimétrica cualquier mensaje que sera enviado mediante un medio potencialmente inseguro como el correo electrónico.</p>
<p>Al decir asimétrico se refiere a que se utiliza una <strong>clave publica</strong> para cifrar, esta se le proporciona a quien quiera enviarnos un mensaje, y para descifrar la información se utiliza una <strong>clave privada</strong>, la cual no necesitamos compartir. Esto elimina el problema de tener que compartir la contraseña para descifrar un archivo por un medio tan o más inseguro como por el que pasaríamos la información :p.</p>
<p>Suena complejo y de cierta forma lo es, pero la gente de <strong>FayerWayer</strong> no explican como usarlo sin dolor en: <a href="https://www.fayerwayer.com/2015/03/que-es-y-como-usar-pgp-en-tu-vida-diaria/">Qué es y cómo usar PGP en tu vida diaria</a>.</p>
<p>###Bonus: utilizar VPN
Si queremos mejorar aún mas el tema de la privacidad y seguridad de nuestros datos siempre podemos optar por utilizar una <strong>VPN</strong>, estas generan un &ldquo;tunel&rdquo; por donde nuestra información viaja cifrada de nuestro equipo al destino.</p>
<p>Este tipo de soluciones es especialmente recomendable si utilizamos redes inalámbricas abiertas, en el aeropuerto o ese evento geek donde todos están conectados a la misma red, ya que nunca falta el que usa un sniffer para escuchar las comunicaciones que pasan, en estos casos también son tremendamente útiles para mejorar nuestra seguridad.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Control de flujo con if y else en C</title>
      <link>https://sergiovp.dev/blog/control-de-flujo-con-if-y-else-en-c/</link>
      <pubDate>Tue, 14 Apr 2015 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/control-de-flujo-con-if-y-else-en-c/</guid>
      <description>Control de flujo con if y else en C</description>
      <content:encoded><![CDATA[<p>Ahora que ya sabemos lo básico de <a href="http://sergiovp.dev/arreglos-o-arrays-en-c/">arrays</a>, es la hora de conocer un poco sobre el <strong>control de flujo</strong> para nuestros programas. Esto se refiere a decidir como se desarrollara el flujo de ejecución de acuerdo a ciertas circunstancias, validaciones, etc.</p>
<p>Pongamos un ejemplo:
Tenemos un pequeño codigo que solicita al usuario ingresar un numero e imprimir si este es par o impar, aquí es donde utilizaríamos el control de flujo, para realizar una acción u otra de acuerdo a una validación.</p>
<p>Estas <strong>&ldquo;validaciones&rdquo;</strong> se realizan mediante dos sentencias, <code>if</code> y <code>else</code>, que básicamente vendría a ser, si, si no.</p>
<p>Para el ejemplo seria:
Si el numero es par, imprime &ldquo;es par&rdquo;,
si no (por eliminación es impar), imprime &ldquo;es impar&rdquo;</p>
<p>Ahora <code>if</code> es una función que puede devolver dos resultados, <em><code>true</code></em> o <em><code>false</code></em>, de acuerdo al resultado de lo que preguntemos, y a continuación ejecutara las instrucciones que le indiquemos, en caso contrario ejecutara lo que le indiquemos en else, este ultimo, aunque no es obligatorio incluirlo, si es recomendable utilizarlo.</p>
<p>Veamos como queda en código:</p>
<pre tabindex="0"><code class="language-language-c" data-lang="language-c">#include &lt;stdio.h&gt;

int main()
{
	int numero;

	printf(&quot;Ingrese un número \n&quot;);
	scanf(&quot;%d&quot;,&amp;numero);

	if(numero%2 == 0) // si el resultado es true ejecutara lo siguiente
	{
		printf(&quot;El numero %i es par \n&quot;,numero);
	}
	else // si no, ejecutara esto
	{
		printf(&quot;El numero %i es impar \n&quot;,numero);
	}

	return 0;
}
</code></pre><p>Como puedes observar, ahora estamos utilizando varias de las cosas que explicamos en las primeras entradas ademas del tema actual:</p>
<ul>
<li>Declaración de variables.</li>
<li>Entrada y salida de datos.</li>
<li>Operadores aritméticos y de comparación.</li>
<li>Control de flujo.</li>
</ul>
<p>Ahora la explicación:</p>
<ol>
<li>Imprimimos un mensaje y leemos lo que el <a href="http://sergiovp.dev/entrada-salida-datos-c/">usuario ingresa mediante el teclado</a>, esto ya lo vimos, si no los has visto puedes visitar el post :)</li>
<li>El if (esto es lo interesante):</li>
</ol>
<ul>
<li>Estamos realizando una operación y verificando su resultado, decimos que si el modulo de lo que contiene numero es 0, es decir si <code>numero%2 == 0</code> es verdadero, imprima el mensaje que indica que el numero es par.
<ul>
<li>A modo de repaso, recordemos que el operador modulo(%), devuelve el residuo de una división, por lo que si el resultado del modulo (o el residuo) de cualquier numero entre 2 es 0, significa que este es par, así es como validamos.</li>
</ul>
</li>
<li>En caso de ser falso imprimirá el mensaje indicando que el numero es impar.</li>
</ul>
<p>Solo para mostrar otra forma en que podriamos interpretar el ejercicio veremos el siguiente código (espero no se se preste a confusiones, en caso de ser así omitirlo es permisible por ahora):</p>
<pre tabindex="0"><code class="language-language-c" data-lang="language-c">#include &lt;stdio.h&gt;

int main()
{
	int numero;

	printf(&quot;Ingrese un número \n&quot;);
	scanf(&quot;%d&quot;,&amp;numero);

	if(!numero%2 == 0) // si el resultado es true ejecutara lo siguiente
	{
		printf(&quot;El numero %i es impar \n&quot;,numero);
	}
	else // si no, ejecutara esto
	{
		printf(&quot;El numero %i es par \n&quot;,numero);
	}

	return 0;
}
</code></pre><p>¿Cual es la diferencia?, hemos invertido la lógica utilizada, ahora decimos:
Si <code>numero%2 == 0</code>, no es cierto (Es lo que hace el agregar el signo <code>!</code> al inicio), el resultado sera true, por lo que el numero es impar, en caso contrario es par.</p>
<p>¿Confuso?, sin duda lo es al inicio, algunas personas prefieren hacerlo de esta forma, pero no hace falta romperse la cabeza por ahora con este ejemplo, conforme desarrollemos nuestra lógica de programación entenderemos mejor estas variaciones, el objetivo es mostrar que no existe una única forma de hacer las cosas :).</p>
<p>Al final concluimos que estas sentencias son sumamente útiles, no por nada siempre se incluyen en temarios básicos de programación, pero con el tiempo y la practica aprenderemos usos mas avanzados de esta sentencia, pero en esta entrada lo dejaremos aquí.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Arreglos o arrays en C</title>
      <link>https://sergiovp.dev/blog/arreglos-o-arrays-en-c/</link>
      <pubDate>Thu, 09 Apr 2015 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/arreglos-o-arrays-en-c/</guid>
      <description>Arreglos o arrays en C</description>
      <content:encoded><![CDATA[<p>Ahora aprenderemos a utilizar arreglos (arrays, vectores), por lo cual debemos conocer algunos lineamientos para poder aprovecharlos:</p>
<ul>
<li>Como cualquier variable debe declararse su tipo</li>
<li>Solo puede albergar valores del tipo con el que fue declarado</li>
<li>Debe indicarse la longitud del array al ser declarado</li>
<li>Cada elemento guardado tendrá una posición en el array, posición 0, 1 o 2 etc</li>
<li>La primera posición de un array es la 0, no la 1</li>
</ul>
<p>Ahora veamos un ejemplo, crearemos un arreglo de tipo char, con una longitud de 9 espacios que contendrá el texto: &ldquo;HolaMundo&rdquo;.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#75715e">#include</span> <span style="color:#75715e">&lt;stdio.h&gt;</span><span style="color:#75715e">
</span><span style="color:#75715e"></span>

<span style="color:#66d9ef">int</span> <span style="color:#a6e22e">main</span>()
{
	<span style="color:#66d9ef">char</span> cadena[<span style="color:#ae81ff">9</span>] <span style="color:#f92672">=</span> <span style="color:#e6db74">&#34;HolaMundo&#34;</span>;

	printf(<span style="color:#e6db74">&#34;La primera letra es %c&#34;</span>,cadena[<span style="color:#ae81ff">0</span>]);
	printf(<span style="color:#e6db74">&#34;El texto es %s&#34;</span>,cadena);

	<span style="color:#66d9ef">return</span> <span style="color:#ae81ff">0</span>;
}
</code></pre></div><p>Como comentamos, se declaro un array de caracteres con una longitud de nueve elementos (La longitud va entre corchetes) y se le asigno el texto &ldquo;HolaMundo&rdquo; (Que al ser texto va entre comillas dobles).</p>
<p>En la siguiente linea se imprime la primera letra de la cadena, que seria &ldquo;H&rdquo;, se imprime la variable de la misma forma que se explicó en entradas anteriores, en la la posición 0 (se indica entre corchetes al igual que cuando se declara).</p>
<p>En la penúltima linea se imprime la cadena entera, para no tener que imprimir cada posición por separado se utiliza %s, para indicar que ahí va una variable de tipo cadena (o string).</p>
<p>Este tipo de estructuras de datos podemos aplicarlas a distintos tipos de datos, como int o double, colecciones de datos muy útiles sin duda.</p>
<p>Como nota, de igual forma que declaramos arreglos unidimensionales , podríamos declararlos bidimensionales (una matriz) con la siguiente notación:
tipo de dato + nombre del array + [longitud de las filas][longitud de las columnas].</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4"><code class="language-c" data-lang="c"><span style="color:#66d9ef">int</span> matriz[<span style="color:#ae81ff">3</span>][<span style="color:#ae81ff">3</span>];
</code></pre></div><p>Con lo anterior  tendríamos una matriz de 3x3: 3 filas y 3 columnas.</p>
<p>Ahora, una tarea muy común es la de recorrer un arreglo, en el caso de estos ejemplos que son muy simples recorrerlos en tarea fácil ya que conocemos de antemano su longitud, solo haría falta utilizar alguna estructura como un for (Que veremos mas adelante) para recorrer todas las posiciones:</p>
<pre tabindex="0"><code class="language-language-c" data-lang="language-c">#include &lt;stdio.h&gt;


int main()
{
	char numeros[4] = { 1, 2, 3, 4 };
	int i=0;
	for (i ; i&lt;4; i++)
	{
		printf(&quot;El numero es %d&quot;,numeros[i]);
	}

	return 0;
}
</code></pre><p>La salida de lo anterior seria:
1
2
3
4</p>
<p>No es necesario entender todo el código anterior, ya explicaremos la sentencia for, pero es útil tratar de analizar dicho código por uno mismo :).</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Primer ejercicio en C</title>
      <link>https://sergiovp.dev/blog/primer-ejercicio-en-c/</link>
      <pubDate>Tue, 07 Apr 2015 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/primer-ejercicio-en-c/</guid>
      <description>Primer ejercicio en C</description>
      <content:encoded><![CDATA[<p>Este es el tutorial numero 6 para programar en C, para este punto hemos visto 4 temas fundamentales:</p>
<ul>
<li>Tipos de datos</li>
<li>Operadores</li>
<li>Variables y sintaxis basica</li>
<li>Entrada y salida de datos</li>
</ul>
<p>Sin embargo, esto nos nos sirve si no sabemos aplicarlo a la resolución de un problema, la logica de programación es algo que no se suele tomar en cuenta en una serie de post como esta, sin embargo en escribocodigo haremos el esfuerzo de utilizar algunos problemas sencillos para poder desarrollar lo que presumiblemente hemos aprendido.</p>
<p>Y es que, al final de todo si solo estudiamos algo &ldquo;teoricamente&rdquo; el conocimiento esta incompleto, falta la otra mitad, la practica para poder comprender completamente eso que estamos estudiando.</p>
<p>Por lo que es hora de poner en practica un poco lo aprendido, en esta entrada hay un ejercicio sugerido de nivel basico, la idea es que traten de resolverlo sin buscar en google o ver el codigo de la respuesta (que se colocara mas abajo), a menos que de verdad sientan que no pueden resolverlo, intenten resolverlo por su cuenta. Tanto para este ejercicio como para los siguientes.</p>
<p>El problema:</p>
<blockquote>
<p>Guillermo tiene N pesos.
Luis tiene la mitad de lo que posee Guillermo.
Juan tiene la mitad de lo que poseen Luis y Guillermo juntos.
Realiza un programa que capture mediante teclado la cantidad que tiene Guillermo, además que calcule e imprima la cantidad de dinero que tiene cada uno y el que tienen entre los tres.</p>
</blockquote>
<p>Por supuesto, si conocen mas problemas de este tipo que seria bueno resolver en este punto no duden en comentarlos.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Como configurar un dominio en Ghost y DigitalOcean</title>
      <link>https://sergiovp.dev/blog/como-configurar-un-dominio-en-ghost-y-digitalocean/</link>
      <pubDate>Thu, 02 Apr 2015 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/como-configurar-un-dominio-en-ghost-y-digitalocean/</guid>
      <description>Como configurar un dominio en Ghost y DigitalOcean</description>
      <content:encoded><![CDATA[<p>Como saben este blog usa <strong>Ghost</strong> como plataforma para publicar, la cual funciona sobre <strong>Node.js</strong> y ya que tuve que investgar como hacer el proceso para este blog porque no compartirlo.</p>
<p>La idea es que al finalizar esta entrada tengamos todo configurado para poder acceder a Ghost mediante nuestro propio .com, .net, etc y de acuerdo a nuestra preferencias, para este caso utilizando la forma sin www, de modo que al ingresar a <em><a href="http://www.midomino.com">www.midomino.com</a></em> redireccione a <em>midominio.com</em>.</p>
<p>El objetivo es aprender a configurar un dominio con esta plataforma y un droplet de <strong>DigitalOcean</strong>, en lo personal tuve varios inconvenientes en este tema (cuestión de la inexperiencia jaja), no es tema de esta entrada explicar como instalar la plataforma ya que gracias a las herramientas que se nos proporcionan es muy facil, pero los pasos se resumen en los siguientes:</p>
<p>Iniciar sesión en DO y clic en &ldquo;Create Droplet&rdquo;:</p>
<ol>
<li>Seleccionar el tamaño y costo</li>
<li>Elegir una ubicación o región</li>
<li>En la opción select image serán dos pasos:
<ul>
<li>En la pestaña &ldquo;Linux Distributions&rdquo; elegimos la distribución linux a instalar, recomiendo Ubuntu o Debian</li>
<li>En la pestaña &ldquo;Aplications&rdquo; seleccionamos Ghost</li>
</ul>
</li>
</ol>
<p>Listo, creamos el droplet y se nos enviaran por correo los datos de acceso.</p>
<p>NOTA: Algunas opciones en &ldquo;Settings&rdquo; como &ldquo;enable backups&rdquo; conllevan un costo asociado, hay que elegir adecuadamente.</p>
<p>Ahora ya con Ghost instalado, veremos que podremos acceder mediante la ip que nos proporciona el servidor, pero nosotros queremos ingresar mediante el dominio que compramos. Sera necesario trabajar en 4 lugares:</p>
<ul>
<li>En la <strong>configuración servidor (Nginx)</strong></li>
<li>En la <strong>configuración de Ghost</strong></li>
<li>Donde tengamos el dominio registrado</li>
<li>En DigitalOcean.</li>
</ul>
<p>Como aviso, antes de realizar los pasos se recomienda agregar ambas formas de nuestra  dirección (con y sin www) a Google Web Master Tools y elegir el de nuestra preferencia en la configuración para efectos de SEO, más aún si se realiza una migración y ambos dominios estaban activos.</p>
<p>###En el servidor:</p>
<p>Debemos ingresar a nuestro VPS con los datos enviados a nuestro correo al momento de crear el Droplet, una vez ahí ingresamos el siguiente comando:</p>
<pre tabindex="0"><code class="language-language-bash" data-lang="language-bash">sudo nano /etc/nginx/sites-available/ghost
</code></pre><p>NOTA: Agrego &lsquo;sudo&rsquo; ya que en este caso el usuario root esta deshabilitado, en caso dcontrario no hace falta</p>
<p>Ahora nos movemos en el contenido del archivo con las flechas del teclado y veremos que en <strong>server_name</strong> pone <em>my-ghost-blog.com</em>, a nosotros nos debe quedar de la siguiente manera:</p>
<pre tabindex="0"><code>  server_name midominio.com www.midominio.com;

    if ($host = 'www.midominio.com' ) {
            rewrite  ^/(.*)$  http://midominio.com/$1  permanent;
    }
</code></pre><p>Solo hace falta reemplazar las direcciones con las nuestras.</p>
<p>Ojo con usar &ldquo;Ctrl + c&rdquo; o &ldquo;Ctrl + v&rdquo;, estamos en el editor nano y aqui no funcionan :).</p>
<p>Una vez editado pulsamos  <em>Ctrl + x</em>, nos pregunta si queremos guardar los cambios escribimos <em>y</em> , por último <em>enter</em> para confirmar la edición.</p>
<p>###En la configuración de Ghost</p>
<p>Ahora utilizamos el comando:</p>
<pre tabindex="0"><code class="language-language-bash" data-lang="language-bash">sudo nano /var/www/ghost/config.js
</code></pre><p>Repetimos la operación anterior cambiando &lsquo;my-ghost-blog.com&rsquo; por el que tengamos y los mismos pasos para guardar.</p>
<p>Ahora toca reiniciar los servicios:</p>
<p>Para reiniciar ghost:
<code>service ghost status</code></p>
<p>Para reiniciar NginX:
<code>/etc/init.d/nginx restart</code></p>
<p>###En el registrador de dominios:</p>
<p>Aquí necesitamos cambiar los <strong>DNS</strong> que gestionan nuestro dominio, puede varias dependiendo del registrador, pero en NameCheap es así:</p>
<ol>
<li>Vamos a la opción &ldquo;Manage Domains&rdquo;</li>
<li>Elegimos el dominio que configuraremos</li>
<li>En &ldquo;NAME SERVER INFORMATION&rdquo; usaremos la opción: &ldquo;Specify Custom DNS Servers ( Your own DNS Servers )&rdquo;</li>
<li>Llenamos los datos con las DNS de DO:
<ul>
<li>ns1.digitalocean.com</li>
<li>ns2.digitalocean.com</li>
<li>ns3.digitalocean.com</li>
</ul>
</li>
<li>Save Changes y listo</li>
</ol>
<p><img loading="lazy" src="https://res.cloudinary.com/escribocodigo/image/upload/v1428005051/Namecheap_xb9rei.png" alt="Cambiar DNS en NameCheap"  />
</p>
<p>###En DO:</p>
<ol>
<li>Después de loguearnos vamos a la opción DNS</li>
<li>Elegimos: &ldquo;Add Domain&rdquo;</li>
<li>En Name ponemos el dominio</li>
<li>En IP Address iría la ip que nos corresponde, tenemos un menú desplegable donde elegimos el droplet que creamos y este dato se llena automáticamente.</li>
</ol>
<p><img loading="lazy" src="https://res.cloudinary.com/escribocodigo/image/upload/c_scale,h_366/v1428005051/Agregar_dominio_do_euh9by.png" alt="Apuntar dominio en DigitalOcean"  />
</p>
<p>Ahora veremos que tenemos un registro de tipo A creado.</p>
<p>Nosotros agregaremos un nuevo registro de tipo CNAME:</p>
<ol>
<li>Clic en &ldquo;Add Record&rdquo; y elegir CNAME</li>
<li>En Name ponemos: www</li>
<li>En Hostname va: @</li>
<li>Clic en Create.</li>
</ol>
<p>Listo, ahora llevara un tiempo para que los DNS se propaguen y podamos acceder mediante el dominio, después de eso notaremos que si utilizamos la dirección con www nos redirige (de esto se encarga la configuración de Nginx),  por lo que todo quedo como esperabamos :).</p>
<p>Sin duda parece engorroso el proceso (pero es lo que hay  <del>por ser hipsters y utilizar una plataforma no mainstream :p</del>), sin embargo es parecido al que haríamos en otras plataformas, aunque WordPress ofrece mas facilidades para elegir la preferencia del dominio por ejemplo.</p>
<p>Ahora que todo esta listo (por fin!) todos podemos comenzar a disfrutar tu blog o sitio web, si tienen dudas no olviden dejar un comentario.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Entrada y salida de datos en  C</title>
      <link>https://sergiovp.dev/blog/entrada-y-salida-de-datos-en-c/</link>
      <pubDate>Tue, 31 Mar 2015 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/entrada-y-salida-de-datos-en-c/</guid>
      <description>Entrada y salida de datos en  C</description>
      <content:encoded><![CDATA[<p>En esta entrada veremos como solicitar datos al usuario y como guardarlos cuando este los introduzca.</p>
<p>Básicamente necesitaremos la librería <code>stdio.h</code>, incluirla nos permitirá utilizar la función <code>printf();</code> que sirve para mostrar texto en pantalla y <code>scanf();</code> que nos permite leer datos introducidos mediante el teclado.</p>
<p>Antes debemos recordar que al ser un lenguaje estricto con los tipos de datos, deberemos tratar cada dato de acuerdo a su tipo, para realizar una entrada simple de datos tendríamos el siguiente código:</p>
<pre tabindex="0"><code class="language-language-c" data-lang="language-c">#include &lt;stdio.h&gt;

int main()
{
	int numero;
	printf(&quot;Introduce un numero \n&quot;);
	scanf(&quot;%d&quot;,&amp;numero);
	printf(&quot;El numero es %d&quot;,numero);

	return 0;
}
</code></pre><p>Como vemos, primero declaramos una <strong>variable</strong> de tipo Entero(<em>int</em>) llamada numero, despues imprimimos el texto &ldquo;Introduce un numero&rdquo; seguida de <code>\n</code> (esto nos indica un salto de linea).</p>
<p>En la siguiente linea leemos el dato, scanf tiene la siguiente estructura: <code>scanf(&quot;%x&quot;,&amp;variable)</code>, donde <code>x</code> corresponde a una letra de acuerdo al tipo de dato que se debe recibir, d para int, c para char, etc. Siempre entre comillas dobles, una coma, el signo &ldquo;&amp;&rdquo; (ampersand) junto al nombre de la variable.</p>
<p>Después volvemos a usar la función printf, pero ahora imprime también una variable, para esto se sigue un criterio similar, con <code>%d</code>, para indicar el tipo de variable a imprimir (Esto dentro de las comillas dobles), despues de la coma el nombre de la variable, esta vez sin el sigo `&amp;``.</p>
<p>Si imprimieramos mas variables en una sola linea solo basta agregar un <code>%d</code> por cada variable que se desea imprimir y donde se mostrara, de igual forma después de la coma irán los nombre de las variables (separadas por comas) en el orden en el cual aparecerán, recuerda que % debe ir seguido de la letra correspondiente al tipo de variable.</p>
<p>Terminaremos con el siguiente ejemplo:</p>
<pre tabindex="0"><code class="language-language-c" data-lang="language-c">#include &lt;stdio.h&gt;

int main()
{
	int numero, numero2;
	printf(&quot;Introduce un numero \n&quot;);
	scanf(&quot;%d&quot;,&amp;numero);
	printf(&quot;Introduce un segundo numero \n&quot;);
	scanf(&quot;%d&quot;,&amp;numero2);
	printf(&quot;Los numeros son %d y %d \n&quot;,numero, numero2);

	return 0;
}
</code></pre><p>En el próximo post veremos uno o dos ejercicios simples para practicar lo visto hasta ahora y desarrollar un poco la soltura de la sintaxis. Si tienes dudas no dudes en comentarlo.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Plugins y consejos útiles para Sublime Text</title>
      <link>https://sergiovp.dev/blog/plugins-y-consejos-%C3%BAtiles-para-sublime-text/</link>
      <pubDate>Fri, 27 Mar 2015 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/plugins-y-consejos-%C3%BAtiles-para-sublime-text/</guid>
      <description>Plugins y consejos útiles para Sublime Text</description>
      <content:encoded><![CDATA[<p>Seguramente mas de alguno que este interesado en los temas que trata este blog conoce <strong>Sublime Text</strong>, es por eso que en esta entrada quiero compartir los plugins y configuraciones que en lo personal utilizo en este excelente editor de código.</p>
<p>###Plugins recomendados para Sublime Text:</p>
<p>Uso varios, para diferentes funciones, pero están esencialmente destinados para desarrollo web, estos son:</p>
<p><strong>Bracket Highlighter:</strong>
La función de este plugin es &ldquo;sencilla&rdquo; pero extremadamente útil, algo que ST debería incluir por defecto, que es resaltar el inicio y cierre de las llaves { }.</p>
<p><strong>Color Highlighter:</strong>
Cuando realizas diseño web se suelen utilizar colores en hexadecimal, rgb, etc, Color Highlighter nos permite saber del color que se trata tan solo dando clic en el código hexadecimal, este se iluminara de acuerdo al color del que se trate.</p>
<p><strong>Color Picker:</strong>
Nos permite utilizar la paleta de colores del sistema utilizando la combinación de teclas &lsquo;Ctrl + shift + c&rsquo;, con ella podremos seleccionar colores en hexadecimal desde ST</p>
<p><strong>HTML5:</strong>
Para agregar soporte para esta versión de HTML a ST.</p>
<p><strong>EMMET:</strong>
Un plugins con un potencial enorme, antes llamado <em>zend-coding</em>, nos permite ahorrar tiempo y trabajo cuando escribimos HTML, ya que al escribir una linea de código y pulsar tabulador podemos obtener grandes resultados, podemos pasar de esto:</p>
<p><code>nav#navegacion&gt;ul&gt;li.navegacion_elemento*5&gt;a</code></p>
<p>a esto:</p>
<pre tabindex="0"><code class="language-language-html" data-lang="language-html">&lt;nav id=&quot;navegacion&quot;&gt;
	&lt;ul&gt;
		&lt;li class=&quot;navegacion_elemento&quot;&gt;&lt;a href=&quot;&quot;&gt;&lt;/a&gt;&lt;/li&gt;
		&lt;li class=&quot;navegacion_elemento&quot;&gt;&lt;a href=&quot;&quot;&gt;&lt;/a&gt;&lt;/li&gt;
		&lt;li class=&quot;navegacion_elemento&quot;&gt;&lt;a href=&quot;&quot;&gt;&lt;/a&gt;&lt;/li&gt;
		&lt;li class=&quot;navegacion_elemento&quot;&gt;&lt;a href=&quot;&quot;&gt;&lt;/a&gt;&lt;/li&gt;
		&lt;li class=&quot;navegacion_elemento&quot;&gt;&lt;a href=&quot;&quot;&gt;&lt;/a&gt;&lt;/li&gt;
	&lt;/ul&gt;
&lt;/nav&gt;
</code></pre><p>Al principio puede intimidar un poco, pero se volverá imprescindible.</p>
<p><strong>AutoFileName:</strong>
Editores como Brackets tienen una genial característica que es mostrar los archivos que tenemos conforme escribimos rutas en el código, por ejemplo si utilizamos un &lsquo;require&rsquo; en PHP, conforme escribamos la ruta para incluir nuestro archivo se irán mostrando las carpetas y archivos que estén en el mismo nivel, esto nos ahorra mucho tiempo al incluir archivos y con este plugin añadimos esto a nuestro editor.</p>
<p>###Configuraciones útiles:</p>
<p>ST es un editor sumamente personalizable, podemos modificar múltiples aspectos, para esto vamos a <em>Preferences&gt;Settings - User</em>, ahí podemos agregar las siguientes opciones:</p>
<p><em>Cambiar la fuente:</em>
Para ello añadimos la siguiente linea:</p>
<p><code>&quot;font_face&quot;: &quot;SourceCodePro-Medium&quot;,</code></p>
<p>Como verán yo estoy utilizando la fuente SourceCodePro-Medium, pero pueden utilizar cualquier fuente que este instalada en su sistema, esto resulta provechosos ya que existen fuentes mas recomendables para el código que las &ldquo;comunes&rdquo;, con mono espaciado y otras características.</p>
<p><em>Resaltar la linea actual:</em></p>
<p>Solo hace falta añadir la siguiente linea:</p>
<p><code>&quot;highlight_line&quot;: true,</code></p>
<p><em>Resaltar las pestañas con cambios:</em></p>
<p>Por defecto al modificar el contenido de un archivo ST señala la pestaña del mismo de una forma muy sutil, para mejorar esto agregamos:</p>
<p><code>&quot;highlight_modified_tabs&quot;: true,</code></p>
<p><em>Guardar los cambios al cambiar de ventana:</em></p>
<p>Después de utilizar esta opción me resulta incomodo tener que presionar Ctrl + s cada vez que quiero guardar un cambio, mas si esto significa que iras a ver dicho cambio en el navegador, agregando la opción:</p>
<p><code>&quot;save_on_focus_lost&quot;: true,</code></p>
<p>ST guardara los cambios realizados cada vez que pierda el foco, es decir cada ves que cambiemos de ventana.</p>
<p><em>Resaltar las carpetas de los archivos en el sidebar:</em></p>
<p>Si tienes muchos proyectos en el sidebar de ST, posiblemente se vuelva confuso el árbol de archivos, para resaltar las carpetas de los archivos agregamos la siguiente linea:</p>
<p><code>&quot;bold_folder_labels&quot;: true,</code></p>
<p>Y eso es todo, con estos plugins y configuraciones tenemos un Sublime Text listo para la acción. Seguro pareciera que mi perfil es frontend, pero no!, jeje es backend.</p>
<p>Como comentario final quisiera hacer énfasis en una idea, que es sacarle el máximo provecho a nuestro editor, mas allá de si utilizas uno u otro, muchas veces desperdiciamos características geniales de cada editor, nos quedamos con cosas como el auto completado y nos olvidamos de características como los cursores múltiples o que en sublime text podemos ordenar alfabéticamente los atributos css seleccionándolos y presionado F9, esos shortcuts que ahorran valiosos segundos que terminan significando un incremento en nuestra productividad :).</p>
<p>En fin, es privilegiar nuestras necesidades y comodidad sobre la &ldquo;moda&rdquo; de utilizar un editor u otro, date un tiempo de explorar las diversas características que te ofrecen y elige el que mejor te acomode.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Variables y sintaxis básica en C</title>
      <link>https://sergiovp.dev/blog/variables-y-sintaxis-b%C3%A1sica-en-c/</link>
      <pubDate>Thu, 26 Mar 2015 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/variables-y-sintaxis-b%C3%A1sica-en-c/</guid>
      <description>Variables y sintaxis básica en C</description>
      <content:encoded><![CDATA[<p>Las <strong>variables</strong> son espacios en memoria a los que podemos asignar valores de distintos tipos, se declaran de la siguiente forma  	<code>[tipo] [variable]</code>, por ejemplo:</p>
<pre tabindex="0"><code class="language-language-c" data-lang="language-c">int numero=0;
string nombre=&quot;MiBlog&quot;; // Las cadenas de caracteres deben ir entre comillas dobles
char caracter[5]=&quot;HOLA&quot;;
</code></pre><p>Lo que estamos declarando es una variable de tipo entero (int), es decir números sin decimales como vimos el la primer parte <a href="https://sergiovp.dev/tipos-de-datos-en/">Tipos de datos en C</a>, recordemos que en C debemos indicar explícitamente el tipo de variable a utilizar.</p>
<p>En el caso del tipo char debemos definir una longitud máxima entre corchetes, es decir el máximo de caracteres que contendrá la variable.</p>
<p>Otro aspecto importante es conocer el <strong>ámbito</strong> de las variables, ya que estas pueden ser globales o locales, las variables globales puedes ser accedidas desde cualquier parte de nuestro programa, en cambio las variables locales solo pueden ser usadas en donde se declararon, por ejemplo una función:</p>
<pre tabindex="0"><code class="language-language-c" data-lang="language-c">int global; // esta es una variable global
int main(){

	int local; // esta es una variable local
    //y solo pude ser usada en mifuncion
	return 0;

}
</code></pre><p>Es tiempo de hablar de la sintaxis básica, de la cual ya vimos un poco en las lineas anteriores, C es estricto con su sintaxis, analicemos el ejemplo del primer:</p>
<pre tabindex="0"><code class="language-language-c" data-lang="language-c">#include &lt;stdio.h&gt;

int main(){

	printf(&quot;Hola Mundo!!&quot;);
	return 0;

}
</code></pre><p><code>#include</code> : se usa para &ldquo;incluir&rdquo; librerías (archivos que contienen funciones), en el ejemplo se incluye la librería standar stdio.h que contienen funciones básicas como printf y scanf.</p>
<p><code>int main()</code> : aquí definimos una función, las funciones son una serie de instrucciones agrupadas, int hace referencia a que se espera que la función devuelva un resultado de tipo entero, main sirve para definir la función principal, por lo que sera la primera en ejecutarse, además, todas las funciones llevan paréntesis ().</p>
<p>Entre los paréntesis podríamos definir argumentos para la función, que básicamente serian variables necesarias para que esta realice su tarea, un ejemplo seria: int funcion(float pi).</p>
<p>Además, para delimitar las instrucciones de una función estas deben estar dentro de los signos { y }, estos delimitan el inicio y el final de la función.</p>
<p><code>printf(&quot;Hola Mundo!!&quot;);</code>, es una función estándar, esta tiene varios elementos:
printf Esta es una palabra reservada, es decir que es utilizada por el lenguaje y no puede ser usada para otros propósitos como definir variables.</p>
<p><code>(&quot;&quot;);</code> Como vemos si queremos imprimir texto este debe ir entre comillas dobles, y todas las sentencias deben terminar con punto y coma (;).</p>
<p>Esto es todo por esta entrada, a partir de aquí, otros conceptos sobre la sintaxis u otros aspectos se irán explicando en base a ejemplos (Vaya, por fin!!) que utilicemos en los siguientes temas.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Operadores en C</title>
      <link>https://sergiovp.dev/blog/operadores-en-c/</link>
      <pubDate>Wed, 25 Mar 2015 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/operadores-en-c/</guid>
      <description>Operadores en C</description>
      <content:encoded><![CDATA[<p>Es el turno de aprender operadores, sirven para hacer un montón de cosas en muchos lenguajes, principalmente tenemos 4 tipos de operadores:</p>
<p>###1. De asignación</p>
<p>Con este &ldquo;asignamos&rdquo; valores, por ejemplo si queremos asignar a la variable &lsquo;numero&rsquo; un valor entero de 10, se hace de la siguiente forma:</p>
<p><code>int a = 10;</code></p>
<p>O podemos asignar el valor de otras variables, la variable &lsquo;f&rsquo; tendra el valor de otra variable &lsquo;i&rsquo; (recordar que C es fuertemente tipado, por lo que deben ser del mismo tipo):</p>
<p><code>int i=0;</code>
<code>int f=i;</code></p>
<p>###2. Aritmeticos</p>
<p>No requieren de mayor explicación, permiten realizar operaciones aritméticas (suma, resta, multiplicación, división y modulo<sup id="fnref:1"><a href="#fn:1" class="footnote-ref" role="doc-noteref">1</a></sup> ), en orden:</p>
<ul>
<li><code>+, ejemplo: 5+5</code></li>
<li><code>-, ejemplo: 3-2</code></li>
<li><code>*, ejemplo: 6*2 (no se usa &quot;x&quot; para multiplicar)</code></li>
<li><code>/, ejemplo: 10/2</code></li>
<li><code>%, ejemplo: 26%2</code></li>
</ul>
<p>###3. De comparación</p>
<p>Nos permiten comparar dos expresiones, si la comparación es verdadera se devuelve un valor booleano &lsquo;true&rsquo;, en caso contrario sera &lsquo;false&rsquo;:</p>
<ul>
<li><code>== igual que</code></li>
<li><code>!= diferente de</code></li>
<li><code>&gt; mayor que</code></li>
<li><code>&lt; menor que</code></li>
<li><code>&gt;= mayor o igual que</code></li>
<li><code>&lt;= menor o igual que</code></li>
</ul>
<p>###4. Lógicos</p>
<p>Relacionan expresiones lógicas y devuelven &lsquo;true&rsquo; o &lsquo;false&rsquo; de acuerdo al resultado.</p>
<ul>
<li>
<p><code>&amp;&amp; (AND), ejemplo: 7&gt;5 &amp;&amp; 8&gt;3</code>
(7 mayor que 5 y 8 mayor que 3, el resultado seria true ya que ambas son ciertas).</p>
</li>
<li>
<p><code>|| (OR), ejemplo: 10==5 || 20!= 6</code>
(10 igual que 5 o 20 diferente de 6, el resultado es true ya que 20 es diferente de 6 aunque 10 no sea igual a 5).</p>
</li>
</ul>
<p>Como vemos, si usamos <code>&amp;&amp;</code> devolverá true solo si todas las expresiones son ciertas, en cambio <code>||</code> devolverá true si al menos una de las expresiones es cierta. Veremos su utilidad en algunos ejemplos mas adelante.</p>
<p>Espero este quedando claro el tema, ya que estos operadores se utilizan de manera constante cuando programamos, de cualquier forma estos conceptos básicos lo repasaremos de forma practica mas adelante.</p>
<section class="footnotes" role="doc-endnotes">
<hr>
<ol>
<li id="fn:1" role="doc-endnote">
<p>El modulo(%) nos permite saber el residuo de una división, por ejemplo, si quisiéramos saber si un numero es divisible entre dos, utilizamos 10%2. si el resultado (o residuo), es 0, el numero es divisible entre dos (o par).&#160;<a href="#fnref:1" class="footnote-backref" role="doc-backlink">&#x21a9;&#xfe0e;</a></p>
</li>
</ol>
</section>
]]></content:encoded>
    </item>
    
    <item>
      <title>Tipos de datos en C</title>
      <link>https://sergiovp.dev/blog/tipos-de-datos-en-c/</link>
      <pubDate>Tue, 24 Mar 2015 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/tipos-de-datos-en-c/</guid>
      <description>Tipos de datos en C</description>
      <content:encoded><![CDATA[<p>Tutorial de C: Tipos de datos</p>
<p>Ya, ya se, dirán: &ldquo;¿Mas párrafos de teoría?, puff hay que escribir código&rdquo;, ciertamente puede ser aburrido, pero las bases son importantes, conforme avancemos estos conceptos se irán obviando, pero es lo básico que hay que saber y sin las bases sera mas difícil avanzar, así que empecemos.</p>
<p>Es importante conocer que en C, como en la mayoría de lenguajes de programación hay diversos tipos de datos (en unos mas en otros menos), los básicos son:</p>
<ul>
<li>int - Numero entero, ejemplo: 10.</li>
<li>float - Numero real, ejemplo: 3.1416.</li>
<li>char - Caracter, ejemplo: &ldquo;a&rdquo;.</li>
</ul>
<p>La forma de declararlos es la siguiente:</p>
<pre tabindex="0"><code class="language-language-c" data-lang="language-c">
int main(){

	int numero = 5;
	float numero_flotante = 3.1415;
	char caracter[5]=&quot;Hola&quot;; // aquí especificamos la longitud, que sera hasta 5 caracteres


} // todas las funciones en C se delimitan mediante { y }
</code></pre><p>Existen otros, como: double, long int, etc, etc. De estos no es necesario  preocuparse por ahora.</p>
<p>Hay que decir que C es un lenguaje &ldquo;fuertemente tipado&rdquo;, es decir que cuando declaremos variables debemos especificar explícitamente su tipo y se deben utilizar solo como se espera que sean usadas, no es como en PHP donde podemos utilizar las variables a diestra y siniestra.</p>
<p>Existen casos donde el compilador se encarga de realizar conversiones, por ejemplo si decimos que utilizaremos una variable con un numero real pero en realidad la que usamos es de tipo entero, no habría problema, se realizaría de forma implícita, pero si son incompatibles, como float (numero real) y char (caracter), se producirá un error.</p>
<p>Por supuesto podríamos hacer conversiones de un tipo a otro, el llamado casting.</p>
]]></content:encoded>
    </item>
    
    <item>
      <title>Programación Básica en C</title>
      <link>https://sergiovp.dev/blog/programaci%C3%B3n-b%C3%A1sica-en-c/</link>
      <pubDate>Mon, 23 Mar 2015 00:00:00 +0000</pubDate>
      
      <guid>https://sergiovp.dev/blog/programaci%C3%B3n-b%C3%A1sica-en-c/</guid>
      <description>Programación Básica en C</description>
      <content:encoded><![CDATA[<p>Esta es la primera entrada en forma del blog, sera el primero de una serie de post que conformaran un mmmm digamos, curso básico para <strong>aprender a programar en C</strong>.</p>
<p>Esta entrada servirá a modo de introducción y a su vez de índice para tener en un solo lugar todos los post.</p>
<p>Indice:</p>
<ol>
<li>Introducción (este post)</li>
<li><a href="https://sergiovp.dev/tipos-datos-c/">Tipos de datos</a></li>
<li><a href="https://sergiovp.dev/operadores-en-c/">Operadores</a></li>
<li><a href="https://sergiovp.dev/variables-sintaxis-basica-c/">Variables y sintaxis básica</a></li>
<li><a href="https://sergiovp.dev/entrada-salida-datos-c/">Entrada y salida de datos</a></li>
<li><a href="https://sergiovp.dev/primer-ejercicio-en-c/">Primer ejercicio</a></li>
<li><a href="https://sergiovp.dev/arreglos-o-arrays-en-c/">Array o arreglos</a></li>
<li><a href="https://sergiovp.dev/control-de-flujo-if-y-else-c/">Control de Flujo: if y else</a></li>
</ol>
<p>###¿Porque un curso? y ¿Porque acerca de C?
Afuera hay muchos recursos para aprender a programar, no solo en C, si no en muchísimos lenguajes de programación, la intención no es hacer mas de lo mismo, el principal objetivo es, (aunque quizá no sea el mas adecuado) desempolvar mis propios conocimientos en C, ya que es el lenguaje con el que aprendí a programar y que mejor manera de hacerlo que ayudar a que otros aprendan.</p>
<p>####Entonces, ¿Que es C?</p>
<p>Un lenguaje de programación compilado, de mediano nivel(o de alto nivel si nos ponemos estrictos), con el podemos interactuar directamente con el sistema, incluso incluir lenguaje ensamblador, además ha influenciado a diversos lenguajes como java, PHP, entre otros. Para mas información: <a href="https://es.wikipedia.org/wiki/C_%28lenguaje_de_programaci%C3%B3n%29">C (lenguaje de programación)</a></p>
<p>¿Que necesitamos para programar en C?
Un compilador y un editor de código esencialmente, existen IDE (Entornos de desarrollo integrados) que suelen incluir estas herramientas entre muchas otras para ayudar al desarrollo.</p>
<p>En Windows he utilizado:</p>
<ul>
<li><a href="http://orwelldevcpp.blogspot.mx/">Orwell Dev-C++</a></li>
<li><a href="http://www.codeblocks.org">Code::Blocks</a></li>
</ul>
<p>Nada mas instalar alguno estaríamos listos para comenzar, por supuesto existen otros especializados o que podemos utilizar para este fin como Codelite, Netbeans o Eclipse.</p>
<p>En linux es un poco mas largo el proceso, un excelente IDE para este propósito en Geany, sin embargo este no cuenta con un compilador instalado, deberos instalarlo aparte y configurar geany para que lo use o compilar desde la terminal</p>
<p>Para esto instalamos geany, en ubuntu y derivadas esta en los repositorios, bastara con:</p>
<pre tabindex="0"><code class="language-language-bash" data-lang="language-bash">sudo aptitude install geany
</code></pre><p>Para instalar el compilador, en este caso gcc (GNU Compiler Collection)</p>
<pre tabindex="0"><code class="language-language-bash" data-lang="language-bash">sudo aptitude install gcc
</code></pre><p>o en su defecto build-essential en lugar de gcc, que instalara otras herramientas.</p>
<p>Ya deberíamos estar listos, podríamos escribir un &ldquo;Hola mundo&rdquo; para no quedarnos con las ganas de escribir algo :), en otro post explicaremos la sintaxis:</p>
<pre tabindex="0"><code class="language-language-c" data-lang="language-c">#include &lt;stdio.h&gt;
//La librería estándar stdio.h es necesaria para utilizar la función printf
int main(){

	printf(&quot;Hola Mundo!! \n&quot;); //Imprime en pantalla el texto Hola Mundo
	return 0; /*la función main espera el retorno de un entero (int), por lo que retornamos 0 para cumplir */

} // todas las funciones en C se delimitan mediante { y }
</code></pre><p>Por ultimo abrimos Geany y vamos a la opción: Editar&gt;Preferencias&gt;Herramientas y en el campo Terminal colocamos lo siguiente:</p>
<pre tabindex="0"><code>x-terminal-emulator -e &quot;/bin/sh %c&quot;
</code></pre><p>De esta forma podremos compilar directamente desde nustro IDE, en otro caso podemos hacerlo desde la terminal, suponiendo que el codigo esta en el archivo &lsquo;codigo.c&rsquo; en la terminal y utilizando gcc seria:</p>
<pre tabindex="0"><code class="language-language-bash" data-lang="language-bash">gcc -o codigo codigo.c
</code></pre><p>Esto genera un ejecutable llamado codigo, para ejecutarlo escribimos el comando:</p>
<pre tabindex="0"><code>./codigo
</code></pre><p><img loading="lazy" src="https://res.cloudinary.com/escribocodigo/image/upload/c_scale,h_373/v1427440628/jecucion_C_yk8uy1.png" alt="Compilar y ejecutar codigo C en Linux"  />
</p>
<p>Hasta aquí el post, espero les sea útil, no duden en comentar su opiniones, dudas o sugerencias.</p>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
