- ```
-
-## Vue
-
-* Seguir guía de estilos de Vue a la hora de ponerle nombre a los componentes.
-
- * Tener todos los componentes en la misma carpeta. Fuente ([detailed explanation en guía de estilo](https://vuejs.org/style-guide/rules-strongly-recommended.html#tightly-coupled-component-names))
-
- * Si es que se hace inmanejable la cantidad de componentes, lo más probable es que la aplicación en si sea lo suficientemente grande como para dividir en engines, en cuyo caso los componentes deberían ir en sus carpetas de engine respectivas.
-
- * Si un componente es global y básico (inputs, botones) usar prefijo `base`. Ejemplos: `base-input`, `base-modal`. [Fuente](https://vuejs.org/style-guide/rules-strongly-recommended.html#tightly-coupled-component-names)
-
- * Si es un componente que se usa una sola vez (headers, footers, etc) usar prefijo `The`. Ejemplos: `the-header`, `the-contact-form` [Fuente](https://vuejs.org/style-guide/rules-strongly-recommended.html#tightly-coupled-component-names)
-
- * Componentes que solo se van a usar en otro componente, tienen de prefijo el nombre de ese componente. Ejemplo: `the-header-nav-bar`. [Fuente](https://vuejs.org/style-guide/rules-strongly-recommended.html#tightly-coupled-component-names)
-
- * Usar `kebab-case` en vez de `PascalCase` ya que usamos el DOM template en las vistas de Rails. [Fuente](https://vuejs.org/style-guide/rules-strongly-recommended.html#component-name-casing-in-templates)
-
-* Usar `href` en vez de `@click` cuando la única acción que se quiere realizar es cambiar de página.
-
-* Usar variables multilinea en vez desactivar regla de eslint
-
-* Preferir librerías que no muten sus valores, por ejemplo date-fns en vez de moment.
-
-* Preferir sintaxis que ayude al tree-shaking, para bajar el tamaño del bundle.
-
- ```javascript
- // ❌
- import * as dateFns from 'date-fns'
- // o, en el caso especifico de lodash
- import { get } from 'lodash'
-
- // ✅
- import get from 'lodash/get'
- // o
- import { get } from 'lodash-es'
-
- import { format, parse } from 'date-fns'
- ```
-
-* Las promesas deberían estar con catch y mostrar feedback al usuario.
-
- ```javascript
- // ❌
-
- const items = await itemsApi.getAll();
- doSomething(items)
-
- // ✅
- try {
- const items = await itemsApi.getAll();
- doSomething(items);
- } catch (e) {
- showTryAgainMessage.value = true;
- }
- ```
-
-* Estructuras repetidas deberían ser extraidas a componentes o ser usadas con v-for (sobre todo si se están usando métodos en vez de valores computed)
-
-* Preferir computed por sobre métodos (cuando son valores para el template)
-
-* Si hay CSS custom en `
-```
-
-`App.spec.js`
-
-```javascript
-import { shallowMount } from '@vue/test-utils';
-import App from './app.vue';
-
-describe('app.vue', () => {
- it('displays message on load', () => {
- const wrapper = shallowMount(App);
- expect(wrapper.find('p').text()).toEqual('Hello Platanus!');
- });
-});
-```
-
-```plain text
- PASS app/javascript/app.spec.js
- app.vue
- ✓ displays message on load (25ms)
-```
-
-
diff --git a/stack/mobile.md b/stack/mobile.md
deleted file mode 100644
index 66d8422..0000000
--- a/stack/mobile.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Mobile
-
-## React Native
-
-Ocupamos [React Native](https://reactnative.dev/) para desarrollar proyectos móbiles. Para agilizar aún más las cosas ocupamos Expo, que nos entrega herramientas de desarrollo y deploy sobre React Native.
-
-[Expo](mobile/expo.md)
-
-[React Navigation](mobile/react_navigation.md)
-
-[Redux](mobile/redux.md)
-
-[Styling](mobile/styling.md)
-
-[Recursos](mobile/recursos.md)
diff --git a/stack/mobile/expo.md b/stack/mobile/expo.md
deleted file mode 100644
index 9c1beda..0000000
--- a/stack/mobile/expo.md
+++ /dev/null
@@ -1,17 +0,0 @@
-# Expo
-
-[Expo](https://expo.io/) es un ecosistema de herramientas que facilitan el uso de React Native. Apoyan el desarrollo, build e incluso publicación a las stores. El proceso de build se refiere a generar la aplicación nativa en iOS (`.ipa`) o Android (`.apk` o `.aab`).
-
-Las herramientas que ofrecen son:
-
-* [Expo Client](https://expo.io/tools#client): aplicación para iOS y Android en la que podrás correr tu proyecto mientras desarrollas. Esto no requiere hacer un build. También permite compartir proyectos entre miembros del equipo antes de subirlos al store.
-
-* [Expo CLI](https://expo.io/tools#cli): command line interface que permite montar un servidor para correr el proyecto en Expo Client localmente, hacer build y publicar los proyectos
-
-* [Expo Snack](https://expo.io/tools#snack): herramienta online para correr Expo en el browser, permite probar y compartir pequeñas aplicaciones o ejemplos
-
-* [Expo SDK](https://expo.io/tools#sdk): SDK que provee acceso a las APIS nativas como la cámara, ubicación, acelerómetro, notificaciones, y muchos más.
-
-Actualmente los SDKs tienen [actualización trimestral](https://dev.to/expo/expo-sdk-37-is-now-available-69g#time-based-releases). Es importante mantenerse al día y [actualizar el proyecto](https://docs.expo.io/workflow/upgrading-expo-sdk-walkthrough/) con cada nuevo SDK. Primero porque estas actualizaciones suelen traer muchas novedades y arreglo de errores. Segundo porque la actualización paulatina es mucho más fácil que actualizar varias versiones al mismo tiempo y permite manejar los breaking changes de cada uno sin que se acumulen. Tercero porque con cada release se depreca el SDK más antiguo. Si tu proyecto ocupa un SDK deprecado las aplicaciones publicadas seguirán funcionando, pero no podrás seguir ocupando Expo Client o volver a hacer build.
-
-
diff --git a/stack/mobile/react_navigation.md b/stack/mobile/react_navigation.md
deleted file mode 100644
index f0cd171..0000000
--- a/stack/mobile/react_navigation.md
+++ /dev/null
@@ -1,25 +0,0 @@
-# React Navigation
-
-Una de las funcionalidades clave de cualquier aplicación mobile es poder navegar entre distintas pantallas, para esto ocupamos [React Navigation](https://reactnavigation.org/). La navegación es un poco más compleja que en una aplicación web porque las aplicaciones móviles suelen ocupar navegadores nesteados, por ejemplo ocupar un navegador drawer para las secciones principales y dentro de cada una de dichas secciones un navegador de stack. Los navegadores que implementa React Navigation son tres:
-
-* [Stack Navigator](https://reactnavigation.org/docs/hello-react-navigation): Con este navegador cada vez que navegas a una nueva página, esta se agrega al historial como una [lista LIFO](https://www.geeksforgeeks.org/lifo-last-in-first-out-approach-in-programming/). Al ir atrás entonces la página actual sale del historial y se muestra la página desde la que se navegó.
-
-* [Drawer Navigator](https://reactnavigation.org/docs/drawer-based-navigation): Este es el típico navegador de menú de hamburguesa, que está escondido y al mostrarse puedes elegir entre distintas secciones.
-
-* [Tab Navigator](https://reactnavigation.org/docs/tab-based-navigation): Este navegador es el que muestra las secciones abajo y se puede cambiar entre estas.
-
-Existe una cuarta forma útil de cambiar entre pantallas que es mediante el JSX de un componente. Por ejemplo con un operador ternario (en el `return` de un componente):
-
-```plain text
-{
- condition ? (
-
- ) : (
-
- )
-}
-```
-
-Esto se ocupa para cambiar de pantallas mediante código, es útil por ejemplo [para cambiar entre pantalla logeada y de login](https://reactnavigation.org/docs/auth-flow)
-
-
diff --git a/stack/mobile/recursos.md b/stack/mobile/recursos.md
deleted file mode 100644
index a044ee0..0000000
--- a/stack/mobile/recursos.md
+++ /dev/null
@@ -1,11 +0,0 @@
-# Recursos
-
-Algunos recursos útiles que te pueden ayudar:
-
-* [React](https://reactjs.org/): librería de Javascript en la que se basa React Native
-
-* [React Native Directory](https://reactnative.directory/): repositorio de recursos para React Native
-
-* [Expo Blog](https://blog.expo.io/)
-
-* [React Native Blog](https://reactnative.dev/blog/)
diff --git a/stack/mobile/redux.md b/stack/mobile/redux.md
deleted file mode 100644
index 33c9b2b..0000000
--- a/stack/mobile/redux.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Redux
-
-En React y React Native se suele utilizar el concepto de store. Este es un estado global de la aplicación en el que se mantienen datos que se ocupan en varios lugares y por lo tanto no pertenecen al estado de un solo componente. Para esto ocupamos [Redux](https://redux.js.org/), posiblemente la herramienta más conocida y utilizada para esto.
-
-Hay algunas herramientas complementarias que probablemente necesites:
-
-* [Redux toolkit](https://redux-toolkit.js.org/): "redux con las baterias incluidas", este paquete incluye varias funciones para agilizar el desarrollo con Redux. Incluye funcionalidades que cumplen tareas muy comunes como configurar el store, crear una acción para cada reducer, o separar tu store en convenientes [slices](https://redux-toolkit.js.org/usage/usage-guide#creating-slices-of-state).
-
-* [Redux saga](https://redux-saga.js.org/): redux no tiene la capacidad de manejar acciones asíncronas en sus reducers. Esto es sin embargo una funcionalidad muy usada, por ejemplo para obtener datos del servidor y luego guardarlos en el store. Para suplir esta falencia nace Redux Saga. Esta librería ocupa [generadores](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function*) y una de sus grandes cruzadas es ser testeable, que en general no es fácil con funciones asíncronas. Es altamente probable que lo necesites.
diff --git a/stack/mobile/styling.md b/stack/mobile/styling.md
deleted file mode 100644
index eecdb9d..0000000
--- a/stack/mobile/styling.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# Styling
-
-Para poder darle estilo a tus componentes y que tu aplicación se vea reluciente, existen distintas opciones:
-
-* [Stylesheets](https://reactnative.dev/docs/stylesheet): este es el método propuesto por React Native. Los estilos se escriben en un objeto y se pasan como prop a cada componente.
-
-* [Styled Components](https://styled-components.com/docs/basics#react-native): librería muy utilizada en React y que puede ser utilizada también en React Native. Los estilos se escriben en un multi-line string lo que lo hace un poco más parecido a CSS.
diff --git a/stack/nuestro_mvc_extendido.md b/stack/nuestro_mvc_extendido.md
deleted file mode 100644
index 2b92210..0000000
--- a/stack/nuestro_mvc_extendido.md
+++ /dev/null
@@ -1,49 +0,0 @@
-# Nuestro MVC extendido
-
-Rails es la base de nuestro stack, y como quizás ya sabes tiene un modelo MVC:
-
-* `Models` que definen la estructura de la data y se comunican con la base de datos
-
-* `Views` que definen la UI - lo que ve el usuario
-
-* `Controllers` que sacan data de los modelos y ponen a disposición de la vista correspondiente la información que necesiten
-
-En Platanus hemos adaptado un poco este modelo MVC, agregando distintas capas y herramientas que nos ayudan a tener responsabilidades más separadas, cosa de mantener nuestros modelos y controllers lo más *skinny *posible.
-
-Puedes encontrar explicaciones más detalladas de estas tecnologías en Stack , la idea de esta sección es explicar un poco como interactúan estas distintas partes:
-
-* Power API: herramienta para generar **controladores de API**. Estos, a diferencia de los controladores de app normales, no tienen una vista `.html.erb` asociada, si no que retornan un json. El front (o una app mobile por ejemplo) puede hacer request a estos endpoints de API y obtener este json sin necesidad de recargar la página.
-
-* Active Admin: herramienta que nos da una manera fácil y rápida de definir interfaces de administración. Básicamente con una sintáxis simple crea controllers y vistas que le permiten a un admin hacer el CRUD tradicional sobre un modelo en particular.
-
-* Pundit: introduce el concepto de **Policy, **clases que funcionan como una capa de **autorización **en los controllers y en Active Admin. Chequean si el usuario actual tiene permitido acceder a una acción en particular.
-
-* https://github.com/heartcombo/devise: funciona como una capa de **autenticación**, osea permite iniciar sesión y requerir que esta esté activa en ciertas acciones. Se usa en controllers y Active Admin
-
-* https://github.com/rails-api/active_model_serializers: introducen el concepto de **Serializers**, clases que toman una instancia de un modelo y define qué atributos/métodos de éste se incluyen en un json.
-
- Se usan implícitamente en los controlers de API al llamar a `respond_with`. También los usamos explícitamente en vistas `.html.erb` cuando se necesita pasar un objeto como prop a un componente Vue, con el helper `serialize_resource` que nos facilita Power API.
-
-* https://github.com/drapergem/draper: introduce el concepto de **Decorators**, clases donde va la lógica de presentación asociada a un modelo, que puede ser usada en varias vistas. En otras palabras, métodos que podrían ser de instancia de un modelo en particular, pero que serían únicamente para ser usados en vistas, entonces lo separamos de la lógica propiamente tal en el modelo.
-
-* Active Job y Services: los jobs son el principal lugar donde definimos la **lógica de negocios** (además de tareas que se deban encolar o requieran recurrencia), dado que el MVC tradicional no tiene un lugar obvio para ésta. Es común verlos en controllers con un `perform_now` encapsulando lógica que va más allá del CRUD simple y el manejo de la response/request que hace el controller. También es común verlo en Observers con un `perform_later`, gatillando algún side-effect debido al update o create de un record.
-
- Los Services, aunque menos comunes, también se usan para lógica de negocio. La diferencia es que exponen varios métodos, y la idea es que estos estén fuertemente relacionados y hagan uso de los mismos parámetros.
-
-* Observers: clases asociadas a un modelo que gatilla “algo” frente a cambios en un record. Tiene los mismos [callbacks que un modelo](https://guides.rubyonrails.org/active_record_callbacks.html), pero la idea es que en el observer vaya todo cambio que **sea un side-effect externo a la instancia.** En otras palabras, todo cambio que deba ir en un callback que toque al objeto mismo debe ir en el modelo, y todo efecto externo (mandar una notificación a slack por ejemplo), en el observer.
-
-* [DRAFT] Clients: clases dónde se definen las interacciones con alguna **página web externa**, ya sea llamadas a una **API o scrapping**. Es común verlo en Job que haga algo con la respuesta, o directo en un controller u observer.
-
-* Data Migrate: gema que separa las migraciones de datos de las de schema. Rails tiene el concepto de migraciones, pero estas definen la manera de cambiar la estructura de la base de datos. Hay veces que no solo se quiere cambiar la estructura (columnas y tablas), sino que también la data misma (las filas). Para eso, usamos migraciones de data por separado.
-
-* Vue: framework de JS que usamos para armar la UI. Usamos `.html.erb` todavía para algunas cosas, pero la mayoría de nuestro front lo estamos construyendo en Vue. La interacción más común que tenemos es que **Rails maneja el ruteo**, pero las vistas se construyen con Vue. Esto quiere decir que tenemos controladores no-API con sus acciones, y esas acciones tienen un `.html.erb` asociado, y dentro de esa vista es común que solo haya un componente Vue que recibe props desde Rails.
-
-* [Tailwind](https://tailwindcss.com/): framework CSS que usamos para estilizar nuestras vistas. Prácticamente no usamos CSS puro o SCSS, en casi todos los casos usamos Tailwind. Se usa en vistas `.html.erb` o en componentes Vue
-
-En este diagrama se muestran más o menos las interacciones descritas:
-
-
-
-https://app.diagrams.net/#G16C9xcj5y6dPcNtThtR819z8DFbTTasOe
-
-
diff --git a/stack/resolviendo_problemas_debugging.md b/stack/resolviendo_problemas_debugging.md
deleted file mode 100644
index f594ccb..0000000
--- a/stack/resolviendo_problemas_debugging.md
+++ /dev/null
@@ -1,123 +0,0 @@
-# Resolviendo problemas (debugging)
-
-A veces un `puts` o `console.log` no es la mejor manera de debuggear o ver qué está pasando en una parte del código. En esta sección veremos algunas herramientas que te pueden servir cuando estés desarrollando o buscando el origen de un bug escondido.
-
-# Back - Rails
-
-## Consola
-
-Probablemente ya conoces el comando `rails s` para iniciar el server y ver en esa terminal el output mientras se usa la aplicación. Otro comando de Rails que puede ser útil cuando se quiere probar algo es `rails c`. Este comando abre una consola de rails en la que puedes correr cualquier comando de Ruby/Rails, y también llamar a clases (modelos, jobs, etc.) de tu aplicación.
-
-> 💡 Ojo que si tienes una consola abierta y entremedio haces cambios en el código, esos cambios no se verán reflejados en la consola automáticamente. Debes correr manualmente `reload!`.
-Al igual que con el servidor, si tus cambios incluyen cambios en una migración o cambios a los initializers, deberás cortar y volver a abrir la consola de Rails, ahí no basta el `reload!`
-
-## Pry
-
-En la consola puedo llamar a métodos/clases de mi código, pero, qué pasa si quiero saber el valor de una variable o un método en un punto específico del código?
-
-Una primera aproximación como mencioné antes podría ser el `puts`, pero hay una forma mejor: `binding.pry` , cortesía de la gema https://github.com/pry/pry. Se pone entremedio del código que queremos tener más detalles, igual como se haría con el puts. Luego, cuando la ejecución del código llega a esa línea, se pausa, y en la terminal aparece una consola. Igual que la consola con `rails c` , puedes ejecutar lo que quieras, pero ahora **dentro del contexto en que se puso el binding.pry. **O sea, puedes llamar a métodos privados, variables de instancia o locales, etc:
-
-[Ver video](assets/resolviendo-problemas-debugging-1.qt)
-
-Como se ve al final del video, si quieres salir de la consola del `binding.pry` y volver a la ejecución normal del programa, basta con poner `c`. En verdad es un alias de `continue`, definido en los `.pryrc` de nuestros proyectos. Puedes encontrar más detalles de este y otros comandos en https://github.com/deivid-rodriguez/pry-byebug, en particular `next` (o `n`) y `step` (o `s`) pueden servir harto cuando se necesita hacer una ejecución más controlada del código.
-
-## Problemas en producción/staging
-
-De repente hay problemas en las apps ya deployeadas que no son fáciles de replicar en local. En esos casos, necesitamos sacar más información desde la app que ya está arriba. Aquí lamentablemente no podremos usar `binding.pry`, pero hay un par de cosas que te pueden servir:
-
-### Sentry
-
-Nuestros proyectos están configurados para funcionar con [Sentry](https://docs.sentry.io/platforms/ruby/), que centraliza los errores que se lanzan. También está por lo general configurado para que mande notificaciones a un canal de Slack. Es un buen primer lugar para entender qué está pasando.
-
-### Logs en Heroku
-
-Puedes ver los logs de la aplicación, junto a los de sus workers, con:
-
-```plain text
-heroku logs --tail -a pl-nombre-proyecto-staging
-```
-
-El `--tail` hace que te sigan llegando los logs que se van generando, en un stream.
-
-Con `-a` le dices a que app conectarse. El nombre de nuestras apps siguen por lo general el formato anterior, terminando en `staging` o `production` según corresponda.
-
-Si quieres filtrar por logs del server o de un worker de sidekiq, puedes filtrar por nombre del dyno:
-
-```plain text
-
-heroku logs --tail --dyno worker.1 -a nombre-de-la-app
-```
-
-Puedes ver el nombre de los dynos con `heroku ps -a nombre-de-la-app`.
-
-### Consola rails en heroku
-
-Puedes correr también la consola de rails en el contexto de una app de heroku:
-
-```plain text
-heroku run bundle exec rails c -a nombre-de-la-app
-```
-
-> 🚨 Ojo que las cosas que corras aquí van a afectar tu ambiente de staging/production, no es un ambiente sandbox inofensivo. Si creas records por ejemplo, eso se verá reflejado en la base de datos de la app
-
-### Monkeypatching en la consola
-
-Ruby permite hacer fácilmente *monkeypatching* de una clase, en otras palabras, redefinir partes de esa clase “desde afuera” de dónde se define originalmente. Podemos usar eso para probar cosas en la consola de staging, poniendo puts para debuggear por ejemplo (la gema `pry` no está en ambientes de producción por si acaso). Una forma fácil de hacer eso es definir la clase nuevamente en consola, y redefinir solo los métodos que quiero modificar. El resto de la definición de la clase que no toqué (otros métodos, constantes, etc.) se mantendrán intactos.
-
-Veamos un ejemplo. Digamos que tenemos la siguiente clase
-
-```ruby
-class MyJob < ApplicationJob
- def perform
- do_something
- do_something_else
- end
-
- private
-
- def do_something
- # do_something implementation
- end
-
- def do_something_else
- # do_something_else implementation
- end
-end
-```
-
-Digamos que hay un error y sospecho que está en el método `do_something_else`. Puedo pegar en consola algo así:
-
-```ruby
-class MyJob < ApplicationJob
- private
-
- def do_something_else
- puts 'Some message to help me debug'
- # do_something_else implementation
- end
-end
-```
-
-Luego puedo correr el Job en consola y se verá el puts o los cambios que haya hecho. Notar que no se necesitó redefinir el resto de la clase, pero si tengo que mantener la implementación de `do_something_else`.
-
-> 💡 Los cambios que se hagan de esta manera solo se mantendrán **dentro de esa sesión de la consola de rails.** O sea que esos cambios no se verán reflejados en la aplicación cuando la usen los usuarios. Los cambios viven y mueren con ese `heroku run bundle exec rails c -a nombre-de-la-app`
-
-# Front
-
-## Debugger
-
-Así como en Ruby está el `puts`, probablemente te suena que en javascript está el `console.log`. Y así como te mostramos `pry` como alternativa, en javascript también tenemos un símil: `debugger`. Lo mismo: se pone entremedio del código que queremos tener más detalles y luego, cuando la ejecución del código llega a esa línea, se pausa. El navegador aparece pausado también, y abre la pestaña Sources, mostrando la línea en que se paró la ejecución. Ahí puedes ir a la pestaña Console y ejecutar lo que sea necesario:
-
-[Ver video](assets/resolviendo-problemas-debugging-2.qt)
-
-## Extensión Vue.js devtools
-
-https://devtools.vuejs.org/guide/installation.html
-
-Con esta extensión se agrega una nueva pestaña al devtools del navegador. En ella se puede ver el árbol de componentes presente en la vista actual, y detalles de cada componente (props, computed, etc.).
-
-
-
-También puedes ver los eventos que han sido emitidos con sus parámetros en la tab Timeline:
-
-
diff --git a/stack/ruby_rails.md b/stack/ruby_rails.md
deleted file mode 100644
index d1ad9c7..0000000
--- a/stack/ruby_rails.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# Ruby/Rails
-
-[Rails](setup/configuracion_de_proyectos/rails.md)
-
-[Power Types](ruby_rails/power_types.md)
-
-[Potassium](ruby_rails/potassium.md)
-
-[Power API](ruby_rails/power_api.md)
-
-[Active Admin](ruby_rails/active_admin.md)
-
-[Pundit](ruby_rails/pundit.md)
-
-[Shrine](ruby_rails/shrine.md)
-
-[Pry](ruby_rails/pry.md)
-
-[Strong Migrations](ruby_rails/strong_migrations.md)
-
-[Data Migrate](ruby_rails/data_migrate.md)
-
-[Active Job](ruby_rails/active_job.md)
-
-[Gems](ruby_rails/gems.md)
-
-[Engines - Modularización en Rails](ruby_rails/engines_modularizacion_en_rails.md)
-
-[Sentry](ruby_rails/sentry.md)
-
-[State Machine AASM](ruby_rails/state_machine_aasm.md)
-
-
diff --git a/stack/ruby_rails/active_admin.md b/stack/ruby_rails/active_admin.md
deleted file mode 100644
index 8e16258..0000000
--- a/stack/ruby_rails/active_admin.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# Active Admin
-
-[General](active_admin/general.md)
-
-[2FA - Active Admin](active_admin/2fa_active_admin.md)
-
-[Active Admin Addons](active_admin/active_admin_addons.md)
diff --git a/stack/ruby_rails/active_admin/active_admin_addons.md b/stack/ruby_rails/active_admin/active_admin_addons.md
deleted file mode 100644
index 2447a61..0000000
--- a/stack/ruby_rails/active_admin/active_admin_addons.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# Active Admin Addons
-
-Es una gema que desarrollamos en Platanus y sirve para resolver algunas cuestiones que Active Admin no resuelve. Por ej:
-
-* Selectores ajax.
-
-* Manejo de imágenes.
-
-* Manejo de booleanos.
-
-* En fin, todo lo que [aquí](https://github.com/platanus/activeadmin_addons#what-you-get) aparece.
-
-## ¿Por qué la usamos?
-
-Para no tener que resolver a mano problemas comunes que sabemos tener en Active Admin.
-
-## ¿Cómo la usamos?
-
-### Instalación
-
-Viene con [Potassium](https://github.com/platanus/potassium) pero se puede instalar manualmente siguiendo la instrucciones [aquí](https://github.com/platanus/activeadmin_addons#installation).
-
-### Uso básico
-
-Por suerte le hemos puesto mucho cariño al README de la gema por lo que mejor consultar [ahí](https://github.com/platanus/activeadmin_addons#what-you-get).
-
-### Recursos útiles
-
-* [Repositorio en Github](https://github.com/platanus/activeadmin_addons)
-
-* [Presentación Platanus](https://www.youtube.com/watch?v=k0ewc_5DDHA) sobre addons
diff --git a/stack/ruby_rails/active_admin/assets/general-1.png b/stack/ruby_rails/active_admin/assets/general-1.png
deleted file mode 100644
index 9f07dfa..0000000
Binary files a/stack/ruby_rails/active_admin/assets/general-1.png and /dev/null differ
diff --git a/stack/ruby_rails/active_admin/assets/general-10.png b/stack/ruby_rails/active_admin/assets/general-10.png
deleted file mode 100644
index 74f5c02..0000000
Binary files a/stack/ruby_rails/active_admin/assets/general-10.png and /dev/null differ
diff --git a/stack/ruby_rails/active_admin/assets/general-11.png b/stack/ruby_rails/active_admin/assets/general-11.png
deleted file mode 100644
index 3484bcf..0000000
Binary files a/stack/ruby_rails/active_admin/assets/general-11.png and /dev/null differ
diff --git a/stack/ruby_rails/active_admin/assets/general-12.png b/stack/ruby_rails/active_admin/assets/general-12.png
deleted file mode 100644
index ca22738..0000000
Binary files a/stack/ruby_rails/active_admin/assets/general-12.png and /dev/null differ
diff --git a/stack/ruby_rails/active_admin/assets/general-13.png b/stack/ruby_rails/active_admin/assets/general-13.png
deleted file mode 100644
index 788c528..0000000
Binary files a/stack/ruby_rails/active_admin/assets/general-13.png and /dev/null differ
diff --git a/stack/ruby_rails/active_admin/assets/general-14.png b/stack/ruby_rails/active_admin/assets/general-14.png
deleted file mode 100644
index 9ac7d7d..0000000
Binary files a/stack/ruby_rails/active_admin/assets/general-14.png and /dev/null differ
diff --git a/stack/ruby_rails/active_admin/assets/general-15.png b/stack/ruby_rails/active_admin/assets/general-15.png
deleted file mode 100644
index b486f90..0000000
Binary files a/stack/ruby_rails/active_admin/assets/general-15.png and /dev/null differ
diff --git a/stack/ruby_rails/active_admin/assets/general-2.png b/stack/ruby_rails/active_admin/assets/general-2.png
deleted file mode 100644
index 9dd9eed..0000000
Binary files a/stack/ruby_rails/active_admin/assets/general-2.png and /dev/null differ
diff --git a/stack/ruby_rails/active_admin/assets/general-3.png b/stack/ruby_rails/active_admin/assets/general-3.png
deleted file mode 100644
index 59fbd5a..0000000
Binary files a/stack/ruby_rails/active_admin/assets/general-3.png and /dev/null differ
diff --git a/stack/ruby_rails/active_admin/assets/general-4.png b/stack/ruby_rails/active_admin/assets/general-4.png
deleted file mode 100644
index b83f9e7..0000000
Binary files a/stack/ruby_rails/active_admin/assets/general-4.png and /dev/null differ
diff --git a/stack/ruby_rails/active_admin/assets/general-5.png b/stack/ruby_rails/active_admin/assets/general-5.png
deleted file mode 100644
index 190aac9..0000000
Binary files a/stack/ruby_rails/active_admin/assets/general-5.png and /dev/null differ
diff --git a/stack/ruby_rails/active_admin/assets/general-6.png b/stack/ruby_rails/active_admin/assets/general-6.png
deleted file mode 100644
index 818a166..0000000
Binary files a/stack/ruby_rails/active_admin/assets/general-6.png and /dev/null differ
diff --git a/stack/ruby_rails/active_admin/assets/general-7.png b/stack/ruby_rails/active_admin/assets/general-7.png
deleted file mode 100644
index 3e2235c..0000000
Binary files a/stack/ruby_rails/active_admin/assets/general-7.png and /dev/null differ
diff --git a/stack/ruby_rails/active_admin/assets/general-8.png b/stack/ruby_rails/active_admin/assets/general-8.png
deleted file mode 100644
index cbd3cf5..0000000
Binary files a/stack/ruby_rails/active_admin/assets/general-8.png and /dev/null differ
diff --git a/stack/ruby_rails/active_admin/assets/general-9.png b/stack/ruby_rails/active_admin/assets/general-9.png
deleted file mode 100644
index 38d9402..0000000
Binary files a/stack/ruby_rails/active_admin/assets/general-9.png and /dev/null differ
diff --git a/stack/ruby_rails/active_admin/general.md b/stack/ruby_rails/active_admin/general.md
deleted file mode 100644
index d287af7..0000000
--- a/stack/ruby_rails/active_admin/general.md
+++ /dev/null
@@ -1,606 +0,0 @@
-# General
-
-Es una gema que utilizamos como back office.
-
-## ¿Por qué la usamos?
-
-La utilizamos porque resuelve rápidamente los típicos CRUD de admin. Con ActiveAdmin en unos minutos puedes tener para un recurso (modelo ActiveRecord): el menu para acceder, las vistas para crear/editar, el listado paginado con filtros y varias cosas más.
-
-## ¿Cómo la usamos?
-
-### Instalación
-
-La gema viene instalada si el proyecto se generó usando [Potassium](https://github.com/platanus/potassium). Si no es así, igual puedes agregarlo luego ejecutando `potassium install admin`.
-
-### Uso básico
-
-Supongamos que tenemos el modelo `Blog` con los atributos `title`, `body` y `user` (owner del Blog). Si agregamos bajo `/app/admin/blogs.rb` el siguiente código:
-
-```ruby
-ActiveAdmin.register Blog do
-end
-```
-
-Al acceder a `http://localhost:3000/admin/blogs` veremos el listado de blogs:
-
-
-
-y si hacemos clic en el link "Editar" de alguno de los blogs veremos el formulario:
-
-
-
-Listo! Eso es todo lo que necesitas hacer para tener algo funcionando. De todos modos, en un proyecto Platanus comúnmente verás algo como esto:
-
-```ruby
-ActiveAdmin.register Blog do
- permit_params :title, :body, :user_id
-
- filter :title
-
- index do
- selectable_column
- id_column
- column :title
- column :user
- actions
- end
-
- show do
- attributes_table do
- row :title
- row :body
- row :user
- end
- end
-
- form do |f|
- f.semantic_errors
-
- f.inputs do
- f.input :title
- f.input :body
- f.input :user
- end
-
- f.actions
- end
-end
-```
-
-para tener más control de lo que se quiere mostrar. Por ejemplo, así se ve el index con la configuración custom:
-
-
-
-### I18n
-
-Active Admin toma la configuración de locales de Rails para traducir los nombres de las columnas.
-Por ejemplo, para traducir los atributos de `Blog` deberías tener la siguiente configuración:
-
-`/config/locales/es-CL.yml`
-
-```yaml
-es-CL:
- activerecord:
- attributes:
- blog:
- title: Título
- body: Texto
- user: Dueño
-```
-
-De esta manera al entrar por ejemplo la vista de un `Blog` verás los atributos traducidos:
-
-
-
-Ten en cuenta que también funciona con métodos (getters) custom. Por ejemplo, podrías tener:
-
-`/app/models/blog.rb`
-
-```ruby
-class Blog < ApplicationRecord
- def my_method
- "Hola!"
- end
-end
-```
-
-`/config/locales/es-CL.yml`
-
-```yaml
-es-CL:
- activerecord:
- attributes:
- blog:
- my_method: Mi método
-```
-
-`/app/admin/blogs.rb`
-
-```ruby
-ActiveAdmin.register Blog do
- index do
- selectable_column
- id_column
- column :my_method
- actions
- end
-
- show do
- attributes_table do
- row :my_method
- end
- end
-end
-```
-
-y funcionará.
-
-### Menú
-
-**Agrupar recursos**
-
-Para agrupar varios items dentro de un mismo menú, debes:
-
-1. Definir el menú en el initializer de active admin:
-
- `/config/initializers/active_admin.rb`
-
- ```ruby
- ActiveAdmin.setup do |config|
- config.namespace :admin do |admin|
- admin.build_menu do |menu|
- menu.add id: :some_menu_id, label: "Grupo"
- end
- end
- end
- ```
-
-1. Definir en el recurso su menú padre:
-
- `/app/admin/blogs.rb`
-
- ```plain text
- ActiveAdmin.register Blog do
- menu parent: :some_menu_id
- end
- ```
-
-
-
-**Ocultar menú**
-
-Se hace de la siguiente manera:
-
-`/app/admin/blogs.rb`
-
-```ruby
-ActiveAdmin.register Blog do
- menu false
-end
-```
-
-A simple vista parece no tener mucha utilidad pero lo importante aquí es que aunque no exista el menú, igual existen los endpoints. Algo que puede ser conveniente si queremos usar alguna ruta del admin como una API.
-
-
-
-**Menú condicional**
-
-Puede ser útil mostrar u ocultar un menú dependiendo de una condición. El caso más típico es el de roles. Por ejemplo:
-
-`/app/admin/blogs.rb`
-
-```ruby
-ActiveAdmin.register Blog do
- menu if: -> { current_admin_user.super_admin? }
-end
-```
-
-### Permisos (Pundit)
-
-En Platanus usamos Active Admin con el [adapter de Pundit](https://activeadmin.info/13-authorization-adapter.html#using-the-pundit-adapter) para autorizar recursos.
-Si al registrar un nuevo recurso en AA, no tienes creado el policy de ese recurso, observarás un error así:
-
-
-
-Si esto ocurre, agrega el policy correspondiente y define los permisos para cada una de las acciones del CRUD:
-
-```ruby
-class BlogPolicy < ApplicationPolicy
- def index?
- true
- end
-
- def show?
- true
- end
-
- def create?
- true
- end
-
- def new?
- create?
- end
-
- def update?
- true
- end
-
- def edit?
- update?
- end
-
- def destroy?
- true
- end
-end
-```
-
-> Si todavía no estás en la instancia del proyecto en la cual tienes que preocuparte por permisos deja todas las acciones en true.
-
-Es importante mencionar que si una acción no tiene permisos, esta desaparecerá del menú y links, etc. Por ejemplo:
-
-```ruby
-class BlogPolicy < ApplicationPolicy
- def show?
- false
- end
-end
-```
-
-Al tratar de acceder a `http://localhost:3000/admin/blogs/303`
-
-
-
-Se puede ver además como el link a "Ver" desapareció:
-
-
-
-### Action Items
-
-Los `action_item`s son botones que se pueden agregar a las vistas del recurso. Por ejemplo, el siguiente código mostrará un botón (link), solo en la vista `index`, para ir al listado de administradores.
-
-```ruby
-action_item :go_to_admins, only: [:index] do
- link_to("Ver Administradores", admin_admin_users_path)
-end
-```
-
-
-
-> Ten en cuenta que puedes decidir en qué vistas aparecerá el botón usando la opción only.
-
-### Member Action
-
-Las `member_action`s son acciones extra que se pueden agregar al controller del recurso. Por ejemplo, el siguiente código:
-
-```ruby
-member_action :send_mail, method: :post do
- # Ejecuta algún código. Por ejemplo enviar el blog por mail a n usuarios.
-
- redirect_to admin_blogs_path
-end
-```
-
-sumará el endpoint `/admin/blogs/:id/send_mail` a los endpoints del CRUD.
-
-
-
-Ten en cuenta que se puede utilizar `action_item`s para ejecutar estas nuevas acciones. Por ejemplo, el siguiente código agregará un botón en la vista del blog (`show`) desde el que se llamará a la acción `send_mail`.
-
-```ruby
-action_item :send_mail, only: [:show] do
- link_to("Enviar a Admin Users", send_mail_admin_blog_path(resource), method: :post)
-end
-```
-
-Una alternativa a los `action_item`s es agregar la acción al listado del index así:
-
-```ruby
-index do
- # ...
- actions do |blog|
- link_to("Enviar", send_mail_admin_blog_path(blog), method: :post)
- end
-end
-```
-
-
-
-### Collection Action
-
-Es lo mismo que una `member_action` pero sin apuntar a un recurso en particular sino a la colección. Por ejemplo, la siguiente `member_action`:
-
-```ruby
-member_action :send_mail, method: :post do
- # ...
-end
-```
-
-creará el siguiente endpoint: `POST /admin/blogs/:id/send_mail`. En cambio, esta `collection_action`:
-
-```ruby
-collection_action :send_mails, method: :post do
- # ...
-end
-```
-
-generará este: `POST /admin/blogs/send_mails`
-
-La idea entonces con esto es que las `member_action`s se usen junto a `action_item`s en `show`, `new` y `update` y las `collection_action` con `action_item`s en el `index`.
-
-> Ten en cuenta que dentro de una member_action podrás acceder al recurso actual (en nuestro ejemplo un Blog de id x) usando el método resource. En cambio, en una collection_action, podrás acceder al listado de recursos (lista de Blogs en el ejemplo), que está mostrando el index en ese momento, usando el método collection.
-
-### Display Name
-
-Si prestaste atención a la imagen del formulario de la sección "Uso básico", seguro notaste que el selector de usuarios no muestra correctamente el nombre de los mismos:
-
-
-
-esto se debe a que Active Admin espera que los recursos tengan definido el método: `:display_name` para que puedan ser representados como "String". Si el recurso no lo implementa, simplemente llamará a `to_s` mostrando como resultado lo que vemos en el selector. Entonces, para solucionar esto, podemos hacer lo siguiente:
-
-```ruby
-class User < ApplicationRecord
- def display_name
- email
- end
-end
-```
-
-
-
-> Ten en cuenta que display_name (o cualquiera de las otras opciones) se utilizará en varios lugares: en el título de un recurso, en los links de las rows/columns y, como vimos, en los selectores.
-
-### Vistas custom
-
-Hay veces que necesitamos agregar nuevos endpoints con HTML a medida. Para hacer esto hacer lo siguiente:
-
-1. Agregar la `member_action`:
-
- ```plain text
- member_action :preview, method: :get do
- @blog = resource
- end
- ```
-
-1. Agregar el `action_item`:
-
- ```plain text
- action_item :preview, only: [:show] do
- link_to("Previsualizar", preview_admin_blog_path(resource))
- end
- ```
-
-1. Agregar la vista custom:
-
- Agregamos el HTML para nuestra nueva `member_action` en `/app/views/admin/blogs/preview.html.erb`
-
- ```html
-
<%= @blog.title %>
-
<%= @blog.body %>
- ```
-
-
-
- Ten en cuenta que puedes agregar el archivo con extensión `.arb` en vez de `.erb` y usar la gema [Arbre](https://activeadmin.github.io/arbre/) que es el DSL que Active Admin utiliza para dibujar sus vistas. El siguiente código:
-
- `/app/views/admin/blogs/preview.html.arb`
-
- ```ruby
- h1 { resource.title }
- para { resource.body }
- ```
-
- sería equivalente a lo de `/app/views/admin/blogs/preview.html.erb` en Arbre.
-
-### Link de download para archivos
-
-> Para manejo de archivos se supone el uso de Shrine
-
-Suponiendo que el blog tiene un archivo `image`:
-
-1. Agregar la `member_action`:
-
- ```ruby
- member_action :download, method: :get do
- blog = Blog.find(params[:id])
- send_file blog.image.download
- end
- ```
-
-1. Agregar el `action_item`:
-
- ```ruby
- action_item :download, only: :show, if: -> { blog.image.present? } do
- link_to 'Download', download_admin_blog_path(blog)
- end
- ```
-
-1. Agregar link a las `actions` en el index
-
- ```ruby
- index do
- column :id
- .
- .
- .
- actions defaults: true do |blog|
- link_to 'Download Image', download_admin_blog_path(blog)
- end
- end
- ```
-
-### JavaScript en una vista
-
-Si necesitas agregar alguna pequeña inteligencia en una vista de Active Admin revisa nuestra guía sobre [AlpineJS](https://www.notion.so/js/alpine/README.md).
-
-### Active Admin Addons
-
-Es una [gema](https://github.com/platanus/activeadmin_addons) construida en Platanus sobre Active Admin y la utilizamos para facilitar algunas features comunes. Por ejemplo: [inputs con select2](https://github.com/platanus/activeadmin_addons/blob/master/docs/select2_default.md), [selector de booleanos en index/show](https://github.com/platanus/activeadmin_addons/blob/master/docs/toggle_bool.md), [selector de colores](https://github.com/platanus/activeadmin_addons/blob/master/docs/color-picker.md), etc.
-
-### Inputs custom
-
-Muchas veces en un formulario de Active Admin necesitamos un input custom. Por ejemplo: selectores de moneda, de colores, con búsqueda ajax, etc. Por esto, Active Admin ofrece una forma sencilla de agregar estos controles. Para mostrarte cómo funciona te mostraré en pasos como agregar un input que escribe con `console.info` lo que se escribió en input en el evento focus out.
-
-1. Agregar el archivo con mi input en `/app/inputs/logger_input.rb`.
-
- > Ten en cuenta que el nombre del archivo debe tener la forma: [nombre_control]_input y lo mismo para el nombre de la clase, pero en CamelCase.
-
- ```plain text
- class LoggerInput < ActiveAdminAddons::InputBase
- def render_custom_input
- concat(label_html)
- concat(builder.text_field(method, input_html_options))
- end
- end
- ```
-
-1. Agregar el js con la lógica del focus out en `/app/assets/javascripts/admin/inputs/logger_input.js`
-
- ```plain text
- $(document).ready(function(){
- $('.logger-input').each(function(i, el) {
- $(el).on( "focusout", function(event) {
- console.info(event.currentTarget.value);
- });
- });
- });
- ```
-
-1. Incluir el js en `/app/assets/javascripts/active_admin.js`:
-
- ```plain text
- //= require active_admin/base
- //= require admin/inputs/logger_input
- ```
-
-1. Usar en el form:
-
- ```plain text
- ActiveAdmin.register Blog do
- form do |f|
- f.inputs do
- f.input :title, as: :logger
- end
-
- f.actions
- end
- end
- ```
-
-### Filtros custom
-
-Para manejar la búsqueda de los filtros ActiveAdmin por debajo usa [Ransack](https://github.com/activerecord-hackery/ransack). Esta gema usa [distintos sufijos](https://github.com/activerecord-hackery/ransack#search-matchers) para indicar distintos tipos de búsqueda. Además, nos permite hacer búsquedas con respecto a [atributos de asociaciones](https://github.com/activerecord-hackery/ransack#associations).
-
-Por ejemplo, si queremos buscar blogs por nombre de usuario:
-
-```ruby
-filter :user_name_cont
-```
-
-Aquí `_cont` indica que se entregarán los resultados que contengan el valor dado. Podríamos haber usado `_eq` si quisieramos un match perfecto, por ejemplo.
-
-### Utilizando Vue
-
-Muchas veces lo que se puede hacer con AA es un poco riguroso en cuanto a las necesidades del cliente, por lo que pueden haber situaciones en las cuales necesitemos incorporar Vue.js en AA. A continuación se explica una guía paso a paso de cómo agregar este en AA.
-
-Para esto tenemos 2 opciones:
-
-1. Hacer una vista custom como se mencionó anteriormente y ahí usar Vue.
-
-1. Usar directamente un componente en la vista de admin o en un vista html.arb.
-
-### Primer caso.
-
-1. Se crea el componente vue que queremos utilizar, por ejemplo `massive-edit.vue`.
-
-1. Luego debemos registrar el componente globalmente. Acá necesitamos usar un archivo diferente al `application.js` que utilizamos normalmente, para este caso usaremos un archivo llamado `admin_application.js` que se debe encontrar en la misma carpeta que el `application.js`, si no se encuentra debe crearlo, la convención para registrar los componentes que se usan en admin es de `snake_case`, siguiendo con el ejemplo anterior el archivo se vería así:
-
- ```javascript
- import Vue from 'vue/dist/vue.esm';
- import MassiveEdit from '../views/products/massive-edit.vue';
-
- Vue.component('massive_edit', MassiveEdit);
-
- document.addEventListener('DOMContentLoaded', () => {
- if (document.getElementById('wrapper') !== null) {
- return new Vue({
- el: '#wrapper',
- });
- }
-
- return null;
- });
- ```
-
-1. Si queremos utilizar filtros, i18n o tailwind en los archivos Vue que utilizaremos en AA, debemos importarlos en este archivo también, tal como lo hacemos normalmente. Cabe destacar que si tenemos algo importado en `application.js` no va a funcionar en los componentes que se utilicen en AA, si no que debemos importarlos nuevamente en `admin_application.js`, acá un ejemplo del archivo completo:
-
- ```javascript
- import { camelizeKeys } from 'humps'; // filtro camelize
- import Vue from 'vue/dist/vue.esm';
- import MassiveEdit from '../views/products/massive-edit.vue';
- import i18n from '../plugins/i18n'; // i18n
- import '../css/application.css'; // tailwind
-
- Vue.component('massive_edit', MassiveEdit);
- Vue.filter('camelizeKeys', camelizeKeys);
-
- document.addEventListener('DOMContentLoaded', () => {
- if (document.getElementById('wrapper') !== null) {
- return new Vue({
- el: '#wrapper',
- i18n,
- });
- }
-
- return null;
- });
- ```
-
-1. Luego podemos ir a cualquier vista dentro de `app/views/admin` y usar el componente como lo hacemos normalmente:
-
- ```html
-
- ```
-
-### Segundo caso.
-
-1. Hacer los mismos pasos anteriores.
-
-1. Instalar la clase `vue_component` desde nuestra gema potassium. Esto agrega un par de inicializadores a nuestra app. Se debe escribir `potassium install` y escoger `vue_component`.
-
-1. Luego en el archivo `initializers/active_admin.rb` debemos importar `vue_componment` y hacer build del componente registrado anteriormente:
-
- ```plain text
- require "vue_component.rb"
-
- AUTO_BUILD_ELEMENTS = %i{
- massive_edit
- }
-
- component_creator(AUTO_BUILD_ELEMENTS)
- ```
-
- La función component_creator recibe los nombres de los componentes y los convierte a html que puede procesar AA mediante la gema [Arbre](https://github.com/activeadmin/arbre). Lo anterior se debe poner fuera del `ActiveAdmin.setup`.
-
-1. Utilizar el componente en la vista, puede ser de las siguientes formas:
-
- a. html.arb:
-
- ```ruby
- massive_edit(prop1: prop1, prop2: prop2)
- ```
-
- b. admin
-
- ```ruby
- index do
- massive_edit(prop1: prop1, prop2: prop2)
- end
- ```
-
-## Recursos útiles
-
-* [Active Admin Docs](https://activeadmin.info/documentation.html)
-
-* [Active Admin Addons Docs](https://github.com/platanus/activeadmin_addons)
diff --git a/stack/ruby_rails/active_job.md b/stack/ruby_rails/active_job.md
deleted file mode 100644
index 493568f..0000000
--- a/stack/ruby_rails/active_job.md
+++ /dev/null
@@ -1,320 +0,0 @@
-# Active Job
-
-[Es un framework que viene con Rails](https://guides.rubyonrails.org/active_job_basics.html) y nos permite definir tareas (jobs) para ejecutar en un "queuing backend".
-
-> 💡 Puedes ver la [presentación sobre Jobs](https://www.youtube.com/watch?v=P-Vqh5z5418) que hicimos en Platanus o continuar leyendo sobre el tema aquí en la guía.
-
-### ¿Para qué los usamos?
-
-Usamos jobs como Rails sugiere:
-
-> These jobs can be everything from regularly scheduled clean-ups, to billing charges, to mailings. Anything that can be chopped up into small units of work and run in parallel, really.
-
-pero en Platanus además los usamos para resolver la **lógica de negocios** de nuestra aplicación. Es decir, no solo para tareas triviales.
-El motivo de esto es evitar antipatrones como "fat models" o "fat controllers" que ocurren cuando nos vemos obligados a elegir un modelo o un controller para poner business logic.
-Para dejar esto más claro, veamos un ejemplo:
-
-Supongamos que tenemos los modelos:
-
-```ruby
-class User < ApplicationRecord
- has_many :bank_movements
-end
-
-class BankMovement
- belongs_to :user
-end
-```
-
-y queremos agregar lógica para generar un reporte de movimientos bancarios de un usuario.
-
-¿Dónde podrían esta lógica?
-
-Una opción sería ponerla en el modelo `User`:
-
-```ruby
-class User < ApplicationRecord
- has_many :bank_movements
-
- def generate_report
- # lógica para generar el reporte
- end
-end
-```
-
-La otra opción sería:
-
-```ruby
-class BankMovement < ApplicationRecord
- belongs_to :user
-
- def self.generate_report(user)
- # lógica para generar el reporte
- end
-end
-```
-
-Pero la verdad es que en ambos casos no "se siente" muy correcto, ¿no?
-
-Ya los imagino pensando cosas como: "¿donde voy a meter el próximo reporte? ¡la clase `User` se volverá gigante!"
-
-Una posible solución a este problema (la que hemos decidido utilizar en Platanus) es poner esta lógica en jobs. Algo así:
-
-```ruby
-class GenerateUserBankMovementsReportJob < ApplicationJob
- def perform(user)
- # lógica para generar el reporte
- end
-end
-```
-
-Con lo anterior logramos encapsular la lógica de creación del reporte y evitar que modelos como `User` o `BankMovement` empiecen a crecer y a tener múltiples responsabilidades.
-
-> 💡 Vale la pena aclarar que hasta no hace mucho tiempo atrás usábamos [comandos de power types](https://github.com/platanus/power-types#commands) para resolver este asunto pero, actualmente, dejamos de utilizarlos por considerar que podíamos lograr lo mismo usando solamente los jobs de Rails.
-
-### ¿Cómo los usamos?
-
-Primero creamos el job con el generador:
-
-```bash
-bundle exec rails g job generate_user_bank_movements_report
-```
-
-Hacer esto generará dos archivos:
-
-En job `app/jobs/generate_user_bank_movements_report_job.rb`:
-
-```ruby
-class GenerateUserBankMovementsReportJob < ApplicationJob
- queue_as :default
-
- def perform(*args)
- # Do something later
- end
-end
-```
-
-y su test: `spec/jobs/generate_user_bank_movements_report_job_spec.rb`
-
-```ruby
-require 'rails_helper'
-
-RSpec.describe GenerateUserBankMovementsReportJob, type: :job do
- pending "add some examples to (or delete) #{__FILE__}"
-end
-```
-
-Luego, para ejecutar la tarea:
-
-```ruby
-GenerateUserBankMovementsReportJob.perform_now(user)
-```
-
-¡Eso es todo!
-
-Ahora supongamos que la generación del reporte es una tarea "pesada" y queremos ejecutarla en background. Es decir, no queremos que se procese inmediatamente sino que queremos mandarla a una cola para que se ejecute luego. Esto se hace llamando a `perform_later` en vez de `perform_now` así:
-
-```ruby
-GenerateUserBankMovementsReportJob.perform_later(user)
-```
-
-Como decíamos, el código anterior no ejecutará inmediatamente la tarea sino que:
-
-* Persistirá (serialize) el job en algún medio definido por el "queuing backend" (sidekiq, delayed_job, etc.) que estemos usando. Por ejemplo: sidekiq utiliza redis y delayed_job postgres o mysql.
-
-* Cuando el "queuing backend" decida, recuperará (deserialize) el job y ejecutará la tarea.
-
-### Instalación
-
-ActiveJob viene con Rails pero en Platanus usamos [Potassium](https://github.com/platanus/potassium) para modificar algunas cosas e instalar sidekiq como queuing backend.
-Si generaste el proyecto con [Potassium](https://github.com/platanus/potassium) seguramente ya tendrás todo configurado pero, si no es así, puedes ejecutar: `potassium install background_processor`.
-
-El instalador:
-
-* Agrega el archivo `config/initializers/sidekiq.rb` con la configuración básica de sidekiq: conexión con Redis, autenticación del panel de control, etc.
-
-* Agrega el archivo `config/sidekiq.yml` que permite configurar colas, prioridades y concurrencia entre otras cosas. Por ejemplo:
-
- ```yaml
- production:
- :concurrency: 5
- :queues:
- - critical
- - default
- - low
- ```
-
- En el archivo anterior, se configuró que en poducción podrán correr a la vez un máximo de 5 jobs (`concurrency: 5`), que habrán 3 colas (`critical`, `default` y `low`) y que `critical` será la más prioritaria (debido al lugar que ocupa en la lista y no al nombre de la cola).
-
-* En los archivos de environment, agrega la opción `config.active_job.queue_adapter` con los valores:
-
- * `:async` en `config/environments/development.rb`: para correr jobs en RAM. Esto nos sirve en ambiente de desarrollo pero no para producción ya que un reinicio del server eliminará los jobs que tengamos pendientes de ser ejecutados.
-
- * `:test` en `config/environments/test.rb`: para obtener helpers que nos ayuden a testear jobs fácilmente.
-
- * `:sidekiq` en `config/environments/production.rb`: para correr jobs con un "backend serio". En Platanus usamos [Sidekiq](https://github.com/mperham/sidekiq)
-
-* Modifica el archivo `Procfile` y le agrega la línea `worker: bundle exec sidekiq`. Esto nos permitirá levantar sidekiq en un worker de Heroku cuando estemos en ambiente de producción.
-
-### Active Job
-
-Como les expliqué al inicio, Active Job es un framework que nos permite definir tareas pero, además, es una "wrapper" del queuing backend. La utilidad de esto es que podemos definir jobs independientemente del backend que utilicemos.
-
-
-
-Entonces, cuando escribimos por ej:
-
-```ruby
-class GenerateUserBankMovementsReportJob < ApplicationJob
- queue_as :default
-
- def perform(*args)
- # Do something later
- end
-end
-```
-
-lo que estamos haciendo es definir un Job de ActiveJob y no nos interesa si por debajo lo ejecutará [sidekiq](https://github.com/mperham/sidekiq), [delayed_job](https://github.com/collectiveidea/delayed_job) o lo que sea.
-
-El mismo job definido en [Sidekiq](https://github.com/mperham/sidekiq), por fuera de ActiveJob, se vería así:
-
-```ruby
-class GenerateUserBankMovementsReportJob
- include Sidekiq::Worker
-
- def perform(*args)
- # Do something
- end
-end
-```
-
-Definir tareas y configurar cosas por fuera de ActiveJob es algo que deberíamos evitar, ya que al hacerlo nos volvemos dependientes del backend y, si el día de mañana decidimos usar otro ([delayed_job](https://github.com/collectiveidea/delayed_job) por ejemplo), romperemos alguna funcionalidad.
-
-### Configuraciones de un Job
-
-En la sección de instalación definimos distintas *queues*. En caso que tengamos distintas prioridades para ciertos procesos, podemos especificar la cola a usar con la opción `queue_as`.
-
-También podemos especificar la política en caso de que no se logre ejecutar de manera correcta el job. Esto lo hacemos especificando la opción `retry`. ActiveJob por defecto tiene una política de **reintentar** 5 veces, cada una separada por 3 segundos. Luego de esto se usa la implementación por defecto de Sidekiq, en el que se vuelven a encolar estos jobs pero con un [delay exponencial](https://github.com/mperham/sidekiq/wiki/Error-Handling#automatic-job-retry).
-
-Además podemos especificar que el `Job` se **descarte** en caso de una excepción en específico.
-
-A modo de ejemplo, un proceso que use las dos opciones descritas puede ser definido de la siguiente manera:
-
-```ruby
-class ReallyImportantJob < ActiveJob::Base
- queue_as :critical
- discard_on CustomAppException
-
- def perform(*args)
- # ...
- end
-end
-```
-
-Las que nombre son configuraciones de las más frecuentes. Para ver más información relacionada con esto, recurre a [la guía de Rails](https://guides.rubyonrails.org/active_job_basics.html).
-
-### Formas de encolar un Job
-
-* **Ejecutar lo antes posible:** en cuanto la cola definida se libere se ejecutará el job. Es importante mencionar aquí que aunque la cola esté vacía, el job correrá de manera asíncrona.
-
- ```ruby
- GenerateUserBankMovementsReportJob.perform_later(user)
- ```
-
-* **Ejecutar en un momento dado:** se ejecutará el job después del tiempo definido.
-
- ```ruby
- GenerateUserBankMovementsReportJob.set(wait_until: Date.tomorrow.noon).perform_later(user)
- ```
-
-* **Ejecutar pasado cierto tiempo:** se ejecutará después del plazo dado.
-
- ```ruby
- GenerateUserBankMovementsReportJob.set(wait: 1.week).perform_later(user)
- ```
-
-* **Ejecutar inmediatamente:** corre el job de manera inmediata, bloqueando la ejecución de tu aplicación. Ojo, esto no llega a Sidekiq, se ejecuta antes.
-
- ```ruby
- GenerateUserBankMovementsReportJob.perform_now(user)
- ```
-
-### Panel de control de Sidekiq
-
-La gema ofrece una vista donde se puede monitorear el estado de los jobs en la aplicación. Si vas a `config/routes.rb`, vas a ver algo del estilo `mount Sidekiq::Web => '/queue'`. Esto indica que la vista puede ser accedida desde `http://localhost:3000/queue`.
-
-
-
-> El password para poder ingresar al dashboard estará definido en la variable de entorno: SIDEKIQ_ADMIN_PASSWORD.
-
-### Mails
-
-ActiveJob se integra muy bien con [ActionMailer](https://guides.rubyonrails.org/action_mailer_basics.html) y nos permite mandar mails a la cola de manera simple. Por ejemplo:
-
-Si tengo el mailer:
-
-```ruby
-class RecruitingProcessMailer < ApplicationMailer
- def personal_interview_mail(recruiting_process)
- # ...
- end
-end
-```
-
-puedo mandarlo a sidekiq de la siguiente manera:
-
-```ruby
-Recruiting::ProcessMailer.personal_interview_mail(recruiting_process).deliver_later
-```
-
-sin la necesidad de escribir un job específico para esto.
-
-Es importante destacar además que los mails son agregados a la cola `mailers` y que potassium configura esto en `config/sidekiq.yml`.
-
-```yaml
-production:
- :concurrency: 5
-:queues:
- - mailers
-```
-
-### Trabajos recurrentes
-
-Ponte en el caso en que quisieras mandar un correo a los usuarios todos los días a las 8:00hrs con un chiste para acompañar su café. Para esto podemos usar la gema `sidekiq-scheduler`. Esta también viene en la configuración de Potassium. Pero también puedes agregarla ejecutando `potassium install schedule`.
-
-Supongamos que tenemos nuestro Job definido:
-
-```ruby
-class SendUsersAJokeJob < ApplicationJob
- queue_as :default
-
- def perform
- # Get a funny meme and send it to all users.
- end
-end
-```
-
-Podemos definir la recurrencia de este Job usando un [Cron](https://crontab.guru/). Estos se definen en `config/sidekiq.yml`
-
-```yaml
-:schedule:
- SendUsersAJokeJob:
- cron: '0 8 * * * *' # Runs all days at 8:00 hrs.
- class: HelloWorld
-```
-
-### Recursos útiles
-
-* [Presentación Platanus sobre el tema](https://www.youtube.com/watch?v=P-Vqh5z5418)
-
-* [Sidekiq Docs](https://github.com/mperham/sidekiq/wiki/Getting-Started)
-
-* [Sidekiq + ActiveJob](https://github.com/mperham/sidekiq/wiki/Active+Job)
-
-* [Sidekiq-history](https://github.com/russ/sidekiq-history)
-
-* [ActiveJob](https://edgeguides.rubyonrails.org/active_job_basics.html)
-
-* [Buenas prácticas](https://github.com/mperham/sidekiq/wiki/Best-Practices)
-
-* [Active Job Log](https://github.com/platanus/active_job_log)
diff --git a/stack/ruby_rails/assets/active-job-1.png b/stack/ruby_rails/assets/active-job-1.png
deleted file mode 100644
index 11e49b6..0000000
Binary files a/stack/ruby_rails/assets/active-job-1.png and /dev/null differ
diff --git a/stack/ruby_rails/assets/active-job-2.png b/stack/ruby_rails/assets/active-job-2.png
deleted file mode 100644
index 4a65a77..0000000
Binary files a/stack/ruby_rails/assets/active-job-2.png and /dev/null differ
diff --git a/stack/ruby_rails/assets/power-api-1.png b/stack/ruby_rails/assets/power-api-1.png
deleted file mode 100644
index ad9c58c..0000000
Binary files a/stack/ruby_rails/assets/power-api-1.png and /dev/null differ
diff --git a/stack/ruby_rails/assets/power-api-2.png b/stack/ruby_rails/assets/power-api-2.png
deleted file mode 100644
index 446e117..0000000
Binary files a/stack/ruby_rails/assets/power-api-2.png and /dev/null differ
diff --git a/stack/ruby_rails/assets/pry-1.png b/stack/ruby_rails/assets/pry-1.png
deleted file mode 100644
index 132968c..0000000
Binary files a/stack/ruby_rails/assets/pry-1.png and /dev/null differ
diff --git a/stack/ruby_rails/assets/pry-2.png b/stack/ruby_rails/assets/pry-2.png
deleted file mode 100644
index 08a13e9..0000000
Binary files a/stack/ruby_rails/assets/pry-2.png and /dev/null differ
diff --git a/stack/ruby_rails/assets/pundit-1.png b/stack/ruby_rails/assets/pundit-1.png
deleted file mode 100644
index 6f8c04e..0000000
Binary files a/stack/ruby_rails/assets/pundit-1.png and /dev/null differ
diff --git a/stack/ruby_rails/assets/pundit-2.png b/stack/ruby_rails/assets/pundit-2.png
deleted file mode 100644
index eeab6da..0000000
Binary files a/stack/ruby_rails/assets/pundit-2.png and /dev/null differ
diff --git a/stack/ruby_rails/assets/pundit-3.png b/stack/ruby_rails/assets/pundit-3.png
deleted file mode 100644
index e85a044..0000000
Binary files a/stack/ruby_rails/assets/pundit-3.png and /dev/null differ
diff --git a/stack/ruby_rails/assets/pundit-4.png b/stack/ruby_rails/assets/pundit-4.png
deleted file mode 100644
index c3bbc16..0000000
Binary files a/stack/ruby_rails/assets/pundit-4.png and /dev/null differ
diff --git a/stack/ruby_rails/assets/pundit-5.png b/stack/ruby_rails/assets/pundit-5.png
deleted file mode 100644
index 32e8fc3..0000000
Binary files a/stack/ruby_rails/assets/pundit-5.png and /dev/null differ
diff --git a/stack/ruby_rails/assets/pundit-6.png b/stack/ruby_rails/assets/pundit-6.png
deleted file mode 100644
index 0816517..0000000
Binary files a/stack/ruby_rails/assets/pundit-6.png and /dev/null differ
diff --git a/stack/ruby_rails/assets/pundit-7.png b/stack/ruby_rails/assets/pundit-7.png
deleted file mode 100644
index 490a686..0000000
Binary files a/stack/ruby_rails/assets/pundit-7.png and /dev/null differ
diff --git a/stack/ruby_rails/data_migrate.md b/stack/ruby_rails/data_migrate.md
deleted file mode 100644
index 7e6576f..0000000
--- a/stack/ruby_rails/data_migrate.md
+++ /dev/null
@@ -1,100 +0,0 @@
-# Data Migrate
-
-[Data Migrate](https://github.com/ilyakatz/data-migrate) es una gema que introduce el concepto de migraciones de data, que corren junto a las migraciones normales de rails que modifican el *schema*.
-
-## ¿Por qué la usamos?
-
-El propósito principal de las migraciones normales de Rails es cambiar el *schema* de la aplicación. Sin embargo, a veces es necesario hacer cambios a la data misma, y a veces esos cambios deben estar coordinados con un cambio al *schema*. Se podría manipular la data en una misma migración corriente de Rails, pero `data_migrate` nos permite separar esa responsabilidad y mantenerlo más ordenado.
-
-## ¿Cómo la usamos?
-
-### Instalación
-
-La gema viene instalada si el proyecto se generó usando [Potassium](https://github.com/platanus/potassium). También se incluyen configuración necesaria para que [annotate](https://github.com/ctran/annotate_models) se corra al usar las tasks que nos da la gema. Si por alguna razón el proyecto no tiene la gema, se puede agregar, junto a la configuración mencionada, corriendo `potassium install data_migrate`.
-
-### Generar nueva migración de data
-
-```bash
-rails g data_migration backfill_some_column_in_some_model
-```
-
-> Warning: Si se crea la migración de datos junto a una de schema hay que asegurarse que no compartan el mismo nombre. Si se llamaran igual, las clases creadas en ambas migraciones también tendrían el mismo nombre y Rails se podría confundir. Ver este issue para más detalles.
-
-### Corriendo las migraciones de schema junto a las de data
-
-> El README de la gema describe todos los comandos que agrega la gema. Como se menciona ahí, también se pueden ver corriendo rake -T data.
-
-Hay que tener en mente que en vez de correr `rake db:migrate`, para correr ambos tipos de migraciones juntas se debe usar `rake db:migrate:with_data`. Este comando también está incluido en el archivo `bin/release` en nuestros proyectos, para que se corra en heroku al hacer deploy.
-
-Usar este comando es importante ya que si se corrieran primero las de *schema* y luego las de *data* podrían haber problemas. Para entender esto consideremos el siguiente ejemplo:
-
-Teníamos un usuario con una columna string `address`. Recientemente se cambió el modelo de datos y `address` pasó a ser su propio modelo `Location` con información adicional. Para esto se realizaron tres migraciones:
-
-1. Una de schema que genera la tabla para el modelo `Location` y agrega la referencia a la tabla de usuarios
-
-1. Una de data que para cada usuario le crea una `Location` y parsea el contenido de la columna `address` al formato del nuevo modelo
-
-1. Una de schema que elimina la columna `address` de los usuarios
-
-Si se corrieran las migraciones por separado usando `rake db:migrate` y luego `rake data:migrate` (orden 1 -> 3 -> 2) la migración de data se caería ya que se eliminó la columna `address` antes. Para esto usamos `rake db:migrate:with_data` que las corre todas en orden de creación.
-
-### Posibles problemas relacionados al deploy
-
-Hay algunos casos en que pueden haber problemas con la aplicación en staging o producción:
-
-1. Cuando ocurre un problema y es necesario restaurar un backup y correr las migraciones que se hayan generado entre la fecha del backup y el presente. Esto puede generar problemas con las migraciones de datos si en ellas se accede a cosas que existían a nivel de código cuando se generó el backup pero con la versión actual del código ya no existen. Este no es un problema exclusivo de la gema, siempre que se manipule data en migraciones puede suceder esto.
-
- Para evitar lo anterior hay un par de alternativas:
-
- * Definir un modelo "temporal" en la migración de data, asociado a la tabla que se use, y usar este exclusivamente. Esto nos independiza del modelo real, y nos obliga a usar solo lo que esté efectivamente definido en la tabla al momento de correr la migración de data. Es decir, no se correrán validaciones ni callbacks del modelo original, ni tampoco se podrán usar scopes definidos ahí. Este es el *approach* que mencionan en la [guía de estilo de rails de rubocop](https://github.com/rubocop-hq/rails-style-guide#define-model-class-migrations).
-
- Como ejemplo, digamos que tenemos un usuario que pasa de tener una columna `boolean` que indica si es gerente o no, a tener un string `role`:
-
- ```ruby
- class BackfillRoleInUsers < ActiveRecord::Migration[6.0]
- class MigrationUser < ApplicationRecord
- self.table_name = :users
- end
-
- def up
- MigrationUser.all.each do |user|
- user.update!(role: user.is_manager ? 'manager' : 'worker')
- end
- end
-
- def down
- raise ActiveRecord::IrreversibleMigration
- end
- end
- ```
-
- * Usar `ActiveRecord::Base.connection.execute(query)` para correr una consulta SQL directamente, donde `query` es el string con esa query. Esto también nos obligaría a usar solo lo que existe en DB al momento de correr la migración.
-
-1. Cuando se corre una migración que crea una nueva columna, y una migración de data a continuación que hace backfill de esa columna. A veces pasa que la migración de data se corre sin problemas, pero los cambios en verdad no se aplicaron. Este problema es especialmente peligroso, porque es posible que en local este problema no ocurra, pero sí en staging/production. Esto pasa porque Rails cachea la información de las columnas al empezar las migraciones, y en la migración de datos se usa ese cache, por lo que el modelo no tiene esa nueva columna, entonces no sabe como guardar ese valor, pero tampoco falla porque sí existe la columna a nivel de DB (esta es una deducción de lo que hemos podido observar en casos que esto ha ocurrido). Esto puede ocurrir incluso si se hace el modelo temporal del paso anterior (suponemos que esto implica que el cache es a nivel de la tabla, no del modelo). Para evitar este problema, la recomendación es **siempre empezar las migraciones de datos reseteando la información de las columnas de todos los modelos que vayamos a usar, usando **[**reset_column_information**](https://api.rubyonrails.org/classes/ActiveRecord/ModelSchema/ClassMethods.html#method-i-reset_column_information). Con esto, la migración del punto anterior quedaría así:
-
- ```ruby
- class BackfillRoleInUsers < ActiveRecord::Migration[6.0]
- class MigrationUser < ApplicationRecord
- self.table_name = :users
- end
-
- def up
- MigrationUser.reset_column_information
- MigrationUser.all.each do |user|
- user.update!(role: user.is_manager ? 'manager' : 'worker')
- end
- end
-
- def down
- raise ActiveRecord::IrreversibleMigration
- end
- end
- ```
-
-### Recursos útiles
-
-* [Repo](https://github.com/ilyakatz/data-migrate)
-
-* [Otro ejemplo de posible problema en deploy](https://medium.com/@jeffcoh23/why-you-should-avoid-activerecord-when-using-ruby-on-rails-data-migrate-gem-2651739395d9): caso detallado en que hay problemas por diferencias entre el código que se usa en la migración de data y el *codebase* del proyecto al momento de correrla en producción. Explica también como reemplazar el código problemático por SQL directo
-
-
diff --git a/stack/ruby_rails/engines_modularizacion_en_rails.md b/stack/ruby_rails/engines_modularizacion_en_rails.md
deleted file mode 100644
index 6195d85..0000000
--- a/stack/ruby_rails/engines_modularizacion_en_rails.md
+++ /dev/null
@@ -1,449 +0,0 @@
-# Engines - Modularización en Rails
-
-> 💡 Antes de arrancar, quiero aclarar que la siguiente guía habla desde la perspectiva de https://github.com/platanus/nest ya que es el único proyecto donde estamos experimentando con engines.
-
-Para modularizar nest, estamos usando [engines de Rails](https://guides.rubyonrails.org/engines.html).
-
-Se debe tener en cuenta que el concepto es el mismo que el de la [guía de rails](https://guides.rubyonrails.org/engines.html) pero en nest estamos poniendo los módulos/engines dentro del repositorio de la main app.
-
-También es importante aclarar que consideramos la "main app" a todo lo que está directamente bajo el root del proyecto. Por ej: un modelo en `app/models/user.rb` es un modelo de la main app. En cambio un modelo en `engines/recruiting/app/models/recruiting/user.rb` es un modelo del engine de reclutamiento.
-
-## Generador
-
-Para facilitar la tarea de crear nuevos engines usamos [Metagem](https://github.com/platanus/metagem) de la siguiente manera:
-
-```bash
-bin/rails g metagem:new_engine engine_name
-```
-
-Ejemplo:
-
-```bash
-bin/rails g metagem:new_engine recruiting
-```
-
-Contesta las preguntas y terminarás teniendo la estructura base de tu engine.
-
-> Si modificas algo en algún engine, por favor traspasa ese conocimiento a Metagem. La idea de tener un generador es que este vaya recogiendo el conocimiento común y así evitar que otros devs tengan que enfrentarse a los mismos problemas.
-
-## Conceptos generales
-
-### Diferencia entre engines y gemas
-
-### Gemas
-
-* Son código encapsulado Ruby. Una librería.
-
-* Se utilizan para extraer alguna funcionalidad específica.
-
-* Se puede utilizar en proyectos que no sean Rails.
-
-* NO tienen código Rails: controllers, modelos, etc.
-
-* Ejemplos de gemas: `httparty`, `remove_bg`, `pry`, etc.
-
-### Engines
-
-* Los engines también son gemas de Ruby.
-
-* Se utilizan para extender Rails.
-
-* Tienen la misma estructura de directorios (app, config, etc) que una app Rails.
-
-* En Platanus las utilizamos para extraer funcionalidades (módulos) completas.
-
-* Ejemplos de engines: `devise`, `paranoia`, `active admin`, etc.
-
-### Estructura
-
-### Main App
-
-Es donde está todo el código que es común a todos los módulos y aquí es donde se conectarán todos los feature engines.
-
-### Feature Engine
-
-Existirán n feature engines. Uno por cada funcionalidad independiente de Platanus.
-Los feature engines deben pensarse como **funcionalidad que se puede desconectar de la main app sin romperla.** Por ejemplo el módulo de reclutamiento es un ejemplo de feature engine.
-
-### Testing
-
-Los tests de una funcionalidad de la main app van dentro de /spec en cambio los tests de una funcionalidad de un engine va dentro de engines/engine_name/spec. Por ej: engines/recruiting/spec/models/recruiting/process_spec.rb es el test de un modelo del módulo/engine de reclutamiento
-
-### Initializer
-
-Se utilizan para configurar gemas. Para agregar configuración al engine, primero deberás definir el atributo en `engines/tu_engine/lib/tu_engine.rb`. Por ejemplo: supongamos que queremos pasar un token al engine `SlackUtils`.
-
-Primero agregamos el atributo `api_token` en `engines/slack_utils/lib/slack_utils.rb` así:
-
-```ruby
-require "slack_utils/engine"
-
-module SlackUtils
- extend self
-
- attr_accessor :api_token
-
- def configure
- yield self
- require "slack_utils"
- end
-end
-```
-
-Luego, en el initializer del engine (ubicado en `/app/config/initializers/slack_utils.rb` siguiendo el ejemplo):
-
-```ruby
-EnginesUtil.initialize_engine(:slack_utils) do |config|
- config.api_token = "XXX"
-end
-```
-
-Después, dentro del engine, podrás usarlo así:
-
-```ruby
-SlackUtils.api_token #=> "XXX"
-```
-
-### Referenciar gemas locales en otras gemas/engines locales.
-
-Es común que pase que en un engine que definimos localmente (dentro de la main app) queramos poner como dependencia a otro engine o gema local. Para explicarles cómo se hace supongamos que tengo el engine de reclutamiento (recruiting) que requiere el engine slack_utils:
-
-Primero, en el `Gemfile` de recruiting agregaremos la referencia a la gema así:
-
-```ruby
-source ''
-git_source(:github) { |repo| "" }
-
-gemspec
-
-gem "slack_utils", path: "../../engines/slack_utils"
-```
-
-> Como se puede ver, se hace una referencia a un path local.
-
-y en el `recruiting.gemspec`:
-
-```ruby
-spec.add_dependency "slack_utils"
-```
-
-De esta manera, al hacer `bundle install`, el `Gemfile.lock` referenciará al path local en vez de a una gema publicada en rubygems.
-
-> Es importante destacar que las dependencias entre engines se hacen a nivel de gemspec/Gemfile y a través del initializer.
-
-### Particularidades de los Engines
-
-### Ubicación
-
-Los engines se crean dentro de `/engines` y se agregan automáticamente en el `Gemfile` de la app principal.
-Los tests van dentro de `engines/tu_engine/spec`
-
-### Isolated namespace
-
-Los engines ejecutan un método `isolate_namespace MyEngine` así:
-
-```ruby
-module Recruiting
- class Engine < ::Rails::Engine
- isolate_namespace Recruiting
- # ...
-```
-
-Ese método se encarga de aislar controllers, models, routes, etc. dentro de un namespace para evitar colisiones de nombres y overrides. Por ejemplo, al crear un nuevo modelo, este se creará dentro del namespace y la tabla tendrá como prefijo el nombre del engine.
-
-Se creará:
-
-```ruby
-module Recruiting
- class ProcessType < ApplicationRecord
- # ...
-```
-
-en vez de:
-
-```ruby
-class ProcessType < ApplicationRecord
- # ...
-```
-
-y la tabla se llamará `recruiting_process_types` en vez de simplemente `process_types`
-
-### Extender clases
-
-Es común que el engine extienda models, controllers, etc. existentes. Estas extensiones se agregan dentro del engine en el directorio que corresponda. Por ejemplo: si dentro del engine fdbk quiero extender el modelo `TeamMember` esa extensión debería ponerla acá: `engines/nombre_engine/app/models/nombre_engine/nombre_modelo_ext.rb` -> `engines/fdbk/app/models/fdbk/team_member_ext.rb`. Si en cambio quiero extender por ej un observer, debería ponerlo acá: `engines/nombre_engine/app/observers/nombre_engine/nombre_observer_ext.rb` -> `engines/fdbk/app/observers/fdbk/team_member_observer_ext.rb`.
-
-Por ej, para extener el modelo `TeamMember` los pasos a seguir son:
-
-1. Hacer que el modelo nos indique que está listo para ser extendido.
-
- Esto se hace dispatcheando el evento **al final **de la clase definida en `app/models/team_member.rb`
-
- ```ruby
- class TeamMember < ApplicationRecord
- #...
-
- ActiveSupport.run_load_hooks("TeamMember", self)
- end
- ```
-
-1. Definir la extensión.
-
- Como es un modelo, debo agregarla acá: `engines/tu_engine/app/models/tu_engine/team_member_ext.rb` y se ve algo así:
-
- ```ruby
- module TuEngine
- module TeamMemberExt
- extend ActiveSupport::Concern
-
- included do
- def hola
- puts "soy un método que agregó el engine"
- end
- end
- end
- end
- ```
-
-1. Extender la funcionalidad.
-
- Se hace dentro de `engines/tu_engine/lib/tu_engine/extensions.rb`
-
- ```ruby
- ActiveSupport.on_load("TeamMember") do
- include TuEngine::TeamMemberExt
- end
- ```
-
- El código anterior indica que una vez que se ejecute el evento `TeamMember`, extienda la funcionalidad del modelo `TeamMember` incluyendo la extensión `TuEngine::TeamMemberExt`.
-
-Pasa saber si una funcionalidad va dentro de la main app o de una extensión tienen que imaginarse que ponen ese método en la main app y contestarse la siguiente pregunta: "si borrara el engine el método que quedó en la main app sigue funcionando o se rompe". Si la respuesta es: "se rompe" es que va en una extensión. Por ej: supongamos que agrego la siguiente relación a `TeamMember`:
-
-```ruby
-class TeamMember < ApplicationRecord
- has_many :fdbk_schedules, class_name: "::Fdbk::Schedule", dependent: :nullify
-end
-```
-
-Como ven es una relación que apunta a una tabla del engine Fdbk. Entonces me hago la pregunta: "¿Si borro el engine Fdbk esa relación se rompe?" la respuesta en este caso sería que sí ya que al borrar el engine se borraría la tabla `fbdk_schedules` y por lo tanto se rompería la relación. Para que esto no suceda, la extensión se coloca dentro del engine Fdbk y si el día de mañana se borrara (junto a la tabla `fbdk_schedules`) se borraría también la relación `has_many :fdbk_schedules` y todo (lo de la main app) quedaría intacto.
-
-### Activar código de un feature engine en la main app (core)
-
-Para eso se puede utilizar el helper `EnginesUtil`.
-
-```ruby
-# como bloque
-EnginesUtil.with_available_engine(:nombre_de_tu_engine) do
- # ejecuto acá código que debe correrse solo si el engine está cargado
-end
-```
-
-```ruby
-# como condicional
-if EnginesUtil.available_engine?(:nombre_de_tu_engine)
- # ejecuto acá código que debe correrse solo si el engine está cargado
-end
-```
-
-Ejemplo:
-
-```ruby
-EnginesUtil.with_available_engine(:recruiting) do
- # ...
-end
-```
-
-En general cuando estoy trabajando en un engine debo intentar por todos los medios poner vistas, modelos, etc. dentro del engine. El problema es que muchas veces eso no se podría hacer. Supongamos el caso de una navbar que vive en la main app. Si quiero agregar un link a alguna vista de reclutamiento no me queda otra que agregarlo dentro de la main app, no? Bueno, en casos como estos se puede usar los métodos mencionados anteriormente. Por ej: en el dashboard de active admin que vive dentro de la main app, se agrega info de los diferentes engines solo si están activos. Este es el código:
-
-```ruby
-ActiveAdmin.register_page "Dashboard" do
- menu priority: 1, label: proc { I18n.t("active_admin.dashboard") }
-
-
- content title: proc { I18n.t("active_admin.dashboard") } do
- panel I18n.t("active_admin.pages.dashboard.search_platanus_github") do
- render partial: 'search_github_form'
- end
-
- if EnginesUtil.available_engine?(:recruiting)
- render("admin/dashboard/recruiting_processes")
- end
-
- if EnginesUtil.available_engine?(:fdbk)
- render("admin/dashboard/fdbk_sessions")
- end
-
- panel I18n.t("active_admin.pages.dashboard.tasks") do
- render partial: 'tasks'
- end
- end
-end
-```
-
-Con el código anterior, si se borrara el engine de reclutamiento, esta vista `admin/dashboard/recruiting_processes` se borarría también. Si no existiera la condición `if EnginesUtil.available_engine?(:recruiting)` daría un error. Como si existe la condición, simplemente no se mostrará info relacionada al reclutamiento y ya.
-
-### Agregar modelos, controllers, etc.
-
-Los engines tienen la misma estructura que una rails app. Entonces si quieres que tu engine agregue por ejemplo un job, harás lo mismo que harías en la main app pero dentro del engine. Es decir, lo agregarás dentro de: `/engines/recruiting/app/jobs/recruiting/assignation_job.rb`
-
-```ruby
-module Recruiting
- class AssignationJob < Recruiting::ApplicationJob
- # ...
- end
-end
-```
-
-Ten en cuenta que:
-
-1. Tu job heredará de `TuEngine::ApplicationJob`. En este caso: `Recruiting::ApplicationJob`
-
-1. Se definirá dentro del namespace `TuEngine`. En este caso: `Recruiting`.
-
-> Lo 2 puntos anteriores se aplican para controllers, modelos, etc.
-
-### Creación de modelos y migraciones
-
-Los modelos para los engines se crean ejecutanto el siguiente comando en el root del proyecto:
-
-```bash
-bin/rails g model engine_name/model_name --engine-model
-```
-
-Como se puede ver, la única diferencia con la creación de un modelo normal de Rails es la opción `--engine-model`
-
-Por ej:
-
-```bash
-bin/rails g model guides/media_file type:string name:string identifier:string file_data:text section:references --engine-model
-```
-
-Algo a tener en cuenta es que, como metagem está en desarrollo, hoy en día los modelos colocan las migraciones donde corresponde pero si luego creamos más migraciones, estas se crearán en la main app y deberemos moverlas a mano a `engines/nombre_engine/db/migrate/xxx.rb` -> ej: `engines/guides/db/migrate/20220927140455_create_guides_sections.rb`
-
-### Rutas
-
-Las rutas de un engine se definen dentro del engine pero, para que estén disponibles dentro de la main app, deberás montarlas en `/config/routes.rb` así:
-
-```ruby
-Rails.application.routes.draw do
- mount Recruiting::Engine, at: '/recruiting'
-
- #...
-```
-
-En el engine `/engines/recruiting/config/routes.rb`
-
-```ruby
-Recruiting::Engine.routes.draw do
- get '/dashboard' => 'dashboard#index'
- root 'dashboard#index'
-end
-```
-
-Un detalle importante a considerar es que desde main app se tiene acceso a los helpers de los engines disponibles según su nombre de ruta que se puede obtener con los comandos anteriores. Por ejemplo:
-
-```ruby
-# app/views/shared/navbar/_right_menu.html.erb (desde main_app)
-
-link_to recruiting.recruiting_index_url
-```
-
-Y, también, desde los engines, se tiene acceso a `main_app`, con lo que se tiene acceso directo a los helpers de rutas del `main_app`. Por ejemplo:
-
-```ruby
-# engines/recruiting/app/views/recruiting/shared/_main_header.html.erb (desde recruiting)
-
-link_to "Ingresar", main_app.recruiting_new_user_session_path
-```
-
-### Testing
-
-* Al ejecutar `export ENABLED_ENGINES='' bin/rspec spec` se correrán **solo los tests de la main app con todos los engines prendidos**.
-
-* Al ejecutar `export ENABLED_ENGINES=false bin/rspec spec` se correrán **solo los tests de la main app con todos los engines apagados**.
-
-* Al ejecutar `export ENABLED_ENGINES='engine_name' bin/rspec engines/engine_name/spec` -> ej: `export ENABLED_ENGINES='recruiting' bin/rspec engines/recruiting/spec` se correrán los **test de un engine específico**. En este caso los de reclutamiento.
-
-* Al ejecutar `export ENABLED_ENGINES='guides' bin/rspec engines/recruiting/spec` **veremos el error ****`uninitialized constant Recruiting`** porque estamos tratando de correr los tests para reclutamiento activando solo el engine `guides`.
-
-Quizás la forma más cómoda de correr los tests localmente es dejar la variable de entorno seteada en vacío -> `ENABLED_ENGINES=''` para que estén todos los engines habilitados ya que igual, en CircleCI, los tests correrán de manera independiente para asegurar que no tienen dependencias indebidas.
-
-Si hacemos esto último entonces:
-
-* Al ejecutar `bin/rspec spec` se correrán **solo los tests de la main app con todos los engines prendidos.**
-
-* Al ejecutar `bin/rspec engines/recruiting/spec` se correrán **solo los tests del engine de reclutamiento con todos los engines prendidos.**
-
-También puedes usar `bin/guard` para que los tests corran cuando modifiques las clases o sus tests. Recuerda tener `ENABLED_ENGINES=''`.
-
-## Front related
-
-Las siguientes secciones no están muy chequeadas ya que todavía estamos explorando el tema modularización y engines. Solo se han dejado en esta guía como futura referencia.
-
-### Webpacker
-
-Cada `engine` debe tener su propio pack en `main_app`, como, por ejemplo:
-
-* `app/javascript/packs/recruiting/recruiting.js`
-
-Ahí, debe definir las siguientes cosas:
-
-* Dependencias JS definidas en `package.json` de `main_app`
-
-* Punto de entrada a SCSS definidos en `javascript/stylesheets` del `engine`
-
-* Punto de entrada a Assets definidos en `javascript/assets` del `engine`
-
-* Componentes Vue definidos en `javascript/components` del `engine`
-
-Es bueno definir un custom alias del directorio `javascript` del `engine`:
-
-```javascript
-// config/webpack/custom.js
-
-'@recruiting': path.resolve(__dirname, '..', '..', 'engines/recruiting/app/javascript')
-```
-
-Para así, luego usar de forma más limpia en el Pack del `engine`:
-
-```javascript
-// Pack: app/javascript/packs/recruiting/recruiting.js (partial)
-import '@recruiting/stylesheets/recruiting/recruiting';
-
-Vue.component('component-name', () => import('@recruiting/components/recruiting/component-name'));
-```
-
-```javascript
-// Pack: app/javascript/packs/recruiting/recruiting.js (partial)
-require.context('@recruiting/assets/recruiting', true);
-```
-
-### Componentes Vue
-
-Los componentes Vue deben quedar en la carpeta `javascript/components` del `engine`.
-
-Ejemplo:
-
-* `engines/recruiting/app/javascript/components/recruiting`
-
-Y deben ser referenciados desde su Pack particular como se mencionó anteriormente.
-
-### SCSS
-
-Un detalle importante al escribir SCSS, es utilizar apropiadamente las referencias a assets.
-
-Por ejemplo, para usar `url()` debes considerar lo siguiente:
-
-* Si el asset está en el `engine`, debes usar un path relativo, como: `url('../../path/to/asset.png')`
-
-* Pero, si el asset está en `main_app`, debes usar este tipo de path partiendo desde `app/javascript` como referencia: `url('~path/to/asset.png')`
-
-### Assets
-
-Assets en el pipeline de Sprockets, se pueden usar directamente igual que en `main_app`.
-Siguiendo la misma estructura de directorio y configurando el `initializer` respectivo.
-
-Como ejemplo, considera el caso de Discovery:
-
-* `engines/recruiting/config/initializers/assets.rb`
-
-* `engines/recruiting/app/assets/images/defaults/recruiting/topic/image.png`
diff --git a/stack/ruby_rails/gems.md b/stack/ruby_rails/gems.md
deleted file mode 100644
index c7075da..0000000
--- a/stack/ruby_rails/gems.md
+++ /dev/null
@@ -1,31 +0,0 @@
-# Gems
-
-Para crear una gema, en vez de usar el generador de `bundler`, podemos utilizar [gemaker](https://github.com/platanus/gemaker) que hace lo mismo pero, además:
-
-* Permite elegir entre dos tipos de gemas: gema de Ruby o engine de Rails.
-
-* Modifica el `README` para adaptarse al estándar de Platanus.
-
-* Agrega en un archivo `CHANGELOG.md` para animarnos a documentar que se hizo en cada versión de la gema.
-
-* Modifica el archivo de licencia nombrando a Platanus en él.
-
-* Agrega la estructura básica para que nuestra gema tenga una CLI.
-
-* Configura el ambiente de test utilizando RSpec.
-
-* Agrega un generador para instalar la gema.
-
-* Configura Circle CI.
-
-```bash
-gemaker new my_gem
-```
-
-Al correr el comando anterior gemaker, antes de crear la gema, nos hará una serie de preguntas para:
-
-* Dejar listo (o casi listo) archivos como el `README` o el `.gemspec`.
-
-* Que seleccionemos aquella funcionalidad que es opcional y que necesitemos en nuestra gema. Por ej: podríamos no necesitar un CLI o un instalador o querer que nuestra gema sea una extensión de Rails (engine) o no.
-
-
diff --git a/stack/ruby_rails/potassium.md b/stack/ruby_rails/potassium.md
deleted file mode 100644
index 3fe95b0..0000000
--- a/stack/ruby_rails/potassium.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Potassium
-
-Para crear nuestras aplicaciones Rails con toda la configuración y herramientas que comúnmente utilizamos en Platanus.
-
-[https://github.com/platanus/potassium](https://github.com/platanus/potassium)
diff --git a/stack/ruby_rails/power_types.md b/stack/ruby_rails/power_types.md
deleted file mode 100644
index f48bdf2..0000000
--- a/stack/ruby_rails/power_types.md
+++ /dev/null
@@ -1,7 +0,0 @@
-# Power Types
-
-[General](power_types/general.md)
-
-[Patrones](power_types/patrones.md)
-
-
diff --git a/stack/ruby_rails/power_types/general.md b/stack/ruby_rails/power_types/general.md
deleted file mode 100644
index c441b71..0000000
--- a/stack/ruby_rails/power_types/general.md
+++ /dev/null
@@ -1,55 +0,0 @@
-# General
-
-Power Types es una [gema](https://github.com/platanus/power-types) desarrollada por **Platanus** que promueve el uso de estos poderosos patrones: **Services**, **Commands**, **Utils** y **Values**.
-
-Estos se basan en el [SRP (Single Responsability Principe)](https://blog.platan.us/solid-single-responsability), que nos dice que cada clase debe tener **1** sola función. Por ejemplo, si tenemos un modelo con operaciones complejas como este:
-
-```ruby
-class User
- def upgrade_membership
- # ...
- end
-
- def notify_external_system
- # ...
- end
-
- def register_payment_card
- # ...
- end
-end
-```
-
-Deberíamos llevar cada una de sus funciones a Commands o Services independientes:
-
-```ruby
-class UpgradeMembership < Command
- # ...
-end
-
-class ExternalNotifierService < Service
- # ...
-end
-
-class RegisterPaymentCard < Command
- # ...
-end
-```
-
-Estructurando nuestro código de forma modular y desacoplada tenemos las siguientes ventajas:
-
-* Menos riesgo: Aislar errores, no pisar variables
-
-* Más claridad, que hace cada clase
-
-* DRYness
-
-* Unit Testing de cada funcionalidad
-
-## Referencias
-
-Para mayor información sobre esta gema, visita los siguientes vínculos:
-
-* [Services, Commands y otros poderosos patrones en Rails](https://blog.platan.us/services-commands-y-otros-poderosos-patrones-en-rails-27c2d3aa7c2e)
-
-* [Anatomy of a Rails Service Object](http://multithreaded.stitchfix.com/blog/2015/06/02/anatomy-of-service-objects-in-rails/)
diff --git a/stack/ruby_rails/power_types/patrones.md b/stack/ruby_rails/power_types/patrones.md
deleted file mode 100644
index 2e9b23b..0000000
--- a/stack/ruby_rails/power_types/patrones.md
+++ /dev/null
@@ -1,17 +0,0 @@
-# Patrones
-
-[Commands](patrones/commands.md)
-
-[Utils](patrones/utils.md)
-
-[Services](patrones/services.md)
-
-[Values](patrones/values.md)
-
-[Observers](patrones/observers.md)
-
-[Clients](patrones/clients.md)
-
-[Presenters](patrones/presenters.md)
-
-
diff --git a/stack/ruby_rails/power_types/patrones/assets/observers-1.png b/stack/ruby_rails/power_types/patrones/assets/observers-1.png
deleted file mode 100644
index 29986aa..0000000
Binary files a/stack/ruby_rails/power_types/patrones/assets/observers-1.png and /dev/null differ
diff --git a/stack/ruby_rails/power_types/patrones/commands.md b/stack/ruby_rails/power_types/patrones/commands.md
deleted file mode 100644
index cbac679..0000000
--- a/stack/ruby_rails/power_types/patrones/commands.md
+++ /dev/null
@@ -1,29 +0,0 @@
-# Commands
-
-> 🚨 En Platanus dejamos de usar comandos. Ahora estamos usando Active Job como lugar para la lógica de negocio
-
-Los *Comandos* son clases destinadas a realizar operaciones acotadas e
-independientes. Se implementan a través de un método `perform` que recibe
-argumentos y realiza operaciones con ellos entregando un resultado. También
-poseen un generador para construir su estructura,
-
-```bash
-$ rails generate command DoSomething foo
-```
-
-Esto generará una clase que implementa el método `perform`
-
-```ruby
-class DoSomething < PowerTypes::Command.new(:foo, :bar)
- def perform(args)
- end
-end
-```
-
-Luego pueden ser llamados y ejecutados de la siguiente forma,
-
-```ruby
-result = DoSomething.for(foo: waffle, bar: pancake)
-```
-
-Donde `:foo, :bar` son los argumentos. Están disponibles en el comando como variables de instancia `@foo, @bar`
diff --git a/stack/ruby_rails/power_types/patrones/observers.md b/stack/ruby_rails/power_types/patrones/observers.md
deleted file mode 100644
index e743483..0000000
--- a/stack/ruby_rails/power_types/patrones/observers.md
+++ /dev/null
@@ -1,87 +0,0 @@
-# Observers
-
-Un observer es una clase que, como dice su nombre se encarga de observar al modelo que lleva su nombre y tienen una labor muy parecida o igual a los [callbacks](https://guides.rubyonrails.org/active_record_callbacks.html) de modelos.
-
-Por ejemplo si queremos llamar una función cada vez que se crea una instancia de un modelo lo podemos hacer de la siguiente manera:
-
-```bash
-$ rails generate observer MyModel
-```
-
-Esto generará un observer de `MyModel`
-
-```ruby
-class MyModelObserver < PowerTypes::Observer
- after_create: :puts_hello
-
- def puts_hello
- puts 'hello'
- end
-end
-```
-
-Luego en `MyModel` debemos añadir:
-
-```ruby
-class MyModel < ActiveRecord::Base
- include PowerTypes::Observable
-end
-```
-
-Ahora cada vez que se ejecute
-
-```ruby
-MyModel.create
-```
-
-se ejecutará `puts_hello`.
-
-Esto también se puede hacer con callbacks de la siguiente manera:
-
-```ruby
-class MyModel < ActiveRecord::Base
- after_create: :puts_hello
-
- def puts_hello
- puts 'hello'
- end
-end
-```
-
-Como pueden ver las dos formas son equivalentes. Entonces ¿Por qué usar observers?
-
-### **¿Por qué ocupar observers?**
-
-Porque nos permite desacoplar lógicas de los modelos que no están directamente relacionadas con ellos.
-
-### **¿Cuál es el criterio para poner algo en un callback o en el observer?**
-
-Por un lado en los callbacks va todo lo necesario para mantener la integridad del objeto, por ejemplo, el formateo de un rut. Si guardamos un objeto sin formato de rut y todos los demás están formateados, entonces ese modelo en sí estará "corrupto".
-
-Por otro lado, en observers debería ir toda la lógica que está relacionada con el modelo pero que no es necesaria para mantener la integridad de este, por ejemplo, el envío de un mail.
-
-Entonces la regla general sería algo como: ¿Mi objeto puede vivir sin esto?, si la respuesta es sí, entonces va en un observer, si la respuesta es no, va en un callback.
-
-Por último la lógica no debe estar literalmente dentro del observer, lo mejor es que en el observer se llame algún job, comando, value, etc y que estos manejen la lógica. Por ejemplo:
-
-```ruby
-class SalesObserver
- after_update :add_to_sales_report
-
- def generate_report
- AddToSalesReportJob.perform_later(object) # En este job va toda la lógica
- end
-end
-```
-
-### **¿Qué son y cuándo usar los callbacks after commit?**
-
-Los modelos de ActiveRecord tienen [callbacks de transacción](https://guides.rubyonrails.org/active_record_callbacks.html#transaction-callbacks) dentro de los que está el callback `after_commit`. Este puede ser ejecutado después del `create`, `update` o `save`, asegurando que su ejecución sea después de que los cambios hayan sido guardados efectivamente en la base de datos, es decir, después del commit.
-
-PowerTypes permite utilizar estos callbacks dentro de los observers. Se llaman `after_create_commit`, `after_update_commit` y `after_save_commit`.
-
-La recomendación es usar estos callbacks cuando se ejecute algo que deberá volver a buscar el objeto a la base de datos. Por ejemplo, si en un callback `after_save` se encola un job que recibe el objeto como parámetro, y este job es ejecutado instantáneamente por Sidekiq, ocurrirá que irá a buscar la instancia a la base de datos justo antes de que se haga el commit de los cambios, generando una ejecución inconsistente del job ya que se utilizó la versión no actualizada del objeto. En la siguiente imagen se muestra un ejemplo:
-
-
-
-En el ejemplo anterior, el callback `after_save` es ejecutado antes de que el objeto sea guardado en la base de datos, mientras que el `after_save_commit` es ejecutado después del commit, asegurando que los cambios ya están guardados en la base de datos.
diff --git a/stack/ruby_rails/power_types/patrones/services.md b/stack/ruby_rails/power_types/patrones/services.md
deleted file mode 100644
index 4ddeb9e..0000000
--- a/stack/ruby_rails/power_types/patrones/services.md
+++ /dev/null
@@ -1,96 +0,0 @@
-# Services
-
-Los servicios son objetos de Ruby destinados a separar la lógica de negocios del resto de la aplicación. Éste permite agrupar muchos métodos que pertenezcan a la misma lógica de negocios.
-
-A menudo pueden ser confundidos con `Clients`, ya que su estructura es bastante similar: una clase que agrupa métodos de una misma lógica. La diferencia entre estos radica en su función más general, como explicamos antes los servicios agrupan lógica de negocios (de la aplicación), mientras que los clientes son hechos para comunicarnos con con servicios externos, como por ejemplo consumir una API. Este podría ser removido de la aplicación actual, ser utilizado en otra y seguir funcionando. Un servicio puede consumir un cliente.
-
-Acá puedes ver un servicio de `Bsale` (sistema de ventas) que agrupa 2 métodos que toman una orden (modelo de la aplicación), crean un factura o una nota en `Bsale` (a través de un `Client`) y guardan el documento generado en la base de datos del proyecto.
-
-```bash
-$ rails generate service BsaleService order
-```
-
-Esto generará una clase cuyo nombre termina, por convención en `..Service`
-
-```ruby
-class BsaleService < PowerTypes::Service.new(:order)
- def create_commercial_invoice
- return unless @order.invoiceable?
-
- document = client.post_commercial_invoice(@order)
- return unless document.success?
-
- save_document(document, 'commercial_invoice')
- @order.generate_invoice!
- end
-
- def create_credit_note
- return if invoice.nil?
-
- invoice_details = client.get_commercial_invoice_details(invoice)
- return unless invoice_details.success?
-
- credit_note = client.post_credit_note(@order, invoice, invoice_details)
- return unless credit_note.success?
-
- document = client.get_document(credit_note)
- return unless document.success?
-
- save_document(document, 'credit_note')
- @order.cancel_invoice!
- end
-
- private
-
- def client
- @client ||= BsaleClient.new
- end
-
- def invoice
- @invoice ||= @order.last_invoice
- end
-
- def save_document(document, document_type)
- @order.last_payment.documents.create!(
- document_type: document_type,
- document_url: document.url,
- document_identifier: document.id
- )
- end
-end
-```
-
-Luego pueden ser utilizados fácilmente instanciando la clase y llamando a sus métodos.
-
-```ruby
-service = BsaleService.new(order: order)
-result = service.create_credit_note
-```
-
-### **Servicios vs Jobs**
-
-Los jobs tienen una función similar a la de los servicios, separar la lógica de negocios del resto de la aplicación, sin embargo, estos separan una función en específico, mientras que el servicio puede separar un grupo de funciones que apuntan a la misma lógica.
-
-Aún así, podemos replicar el funcionamiento de un servicio con varios jobs bajo un mismo namespace, donde cada uno tiene una función específica correspondiente a uno de los métodos del service.
-
-```plain text
-- Jobs
- - Bsale
- - base_job.rb
- - create_commercial_invoice_job.rb
- - create_credit_note_job.rb
-```
-
-```ruby
-class Bsale::CreateCreditNoteJob < ApplicationJob
- def perform(resource)
- # code
- end
-end
-```
-
-Entonces, ¿Cuándo debemos usar cada una de estas opciones? La verdad es que no hay ninguna regla que diga cuándo usar alguno de estos, por lo que queda a gusto del consumidor, pero podemos nombrarte los pro y contras de cada opción.
-
-Al usar servicios es fácil compartir lógica en todos sus métodos mediante algún método privado, es más cómodo encontrar los métodos que encapsulan toda esa lógica de negocios en un solo archivo, sin embargo, es fácil que este empiece a crecer con lógica que realmente no pertenece a ese espacio, quizá lógica necesaria para llevarlo a cabo, pero que no debería estar ahí.
-
-Al usar jobs el compartir lógica entre todos se debe realizar mediante un `base_job` y todos los demás jobs deben heredar de este, los archivos están separados por cada función, lo cual lo hace un poco más verboso, sin embargo, al tener en cada job una función específica, no se presenta el problema de hacer crecer estos archivos con lógica que no pertenece a ellos, o es más fácil de detectar.
diff --git a/stack/ruby_rails/power_types/patrones/utils.md b/stack/ruby_rails/power_types/patrones/utils.md
deleted file mode 100644
index a8bb94d..0000000
--- a/stack/ruby_rails/power_types/patrones/utils.md
+++ /dev/null
@@ -1,74 +0,0 @@
-# Utils
-
-Las utils son helpers que nos permiten agrupar métodos relacionados. Tienen las siguientes particularidades:
-
-* No tienen lógica del dominio de la aplicación donde se usan.
-
-* Suelen ser clases estáticas.
-
-* Su uso normalmente se repite por toda la aplicación.
-
-## ¿Por qué la usamos?
-
-Usamos utils porque Rails no define un lugar específico para este tipo de helpers.
-
-En Rails todo lo que no es del dominio de una app se coloca dentro de `/lib`. En platanus reservamos este directorio para toda aquella lógica que sea candidata a convertirse en una gema y no para simples helpers.
-
-## ¿Cómo la usamos?
-
-### Instalación
-
-Vienen con la gema [power-types](https://github.com/platanus/power-types#installation) que normalmente viene instalada en nuestro proyectos generados con [potassium](https://github.com/platanus/potassium).
-
-### Uso básico
-
-Luego de instalada la gema, se corre el generador:
-
-`bin/rails generate util util_name method_1 method_2 method_n`
-
-Por ejemplo si queremos crear una clase util para simplificar el uso de enums podemos hacer:
-
-`bin/rails generate util Enums translate_enum_attr translate_aasm_attr enum_options_for_select`
-
-Esto generará el helper en: `app/utils/enums_utils.rb`
-
-```ruby
-class EnumsUtils < PowerTypes::BaseUtil
- def self.translate_enum_attr(class_name, enum, key)
- I18n.t(
- "activerecord.attributes.#{class_name.model_name.i18n_key}.#{enum.to_s.pluralize}.#{key}"
- )
- end
-
- def self.enum_options_for_select(class_name, enum)
- class_name.send(enum.to_s.pluralize).map do |key, value|
- [translate_enum_attr(class_name, enum, key), value]
- end
- end
-end
-```
-
-Luego se usaría así:
-
-```ruby
-supply_purchase.supply_type = :fertilizer
-EnumUtils.translate_enum_attr(Supply, :supply_type, supply_purchase.supply_type) #=> "Fertilizante"
-
-EnumUtils.enum_options_for_select(Supply, :supply_type) #=> [["Fertilizante", :fertilizer], ["Agroquímicos", :agrochemical]]
-```
-
-Repasemos las particularidades de las utils contra el ejemplo:
-
-* “No tienen lógica del dominio de la aplicación donde se usan.” → En este caso se cumple ya que `EnumsUtils` podría moverse a otra aplicación y seguiría funcionando. No hay lógica específica del proyecto.
-
-* “Suelen ser clases estáticas.” → Como se puede ver `translate_enum_attr` y `enum_options_for_select` son métodos de clase. No crea una instancia de `EnumUtils` en ningún momento.
-
-* “Su uso normalmente se repite por toda la aplicación” → Sirve para el modelo `Supply` que usamos en el ejemplo pero también podría servir para cualquier otro modelo de la aplicación.
-
-### Recursos útiles
-
-* [Presentación platanus](https://www.youtube.com/watch?v=vAVq-WxIodI&list=PL4jJY1sbBn7C9aNISuaOwRnmhYKjJYdfA&index=2&t=1944s)
-
-* [Documentación de power types](https://github.com/platanus/power-types#utils)
-
-
diff --git a/stack/ruby_rails/power_types/patrones/values.md b/stack/ruby_rails/power_types/patrones/values.md
deleted file mode 100644
index 2a7f00d..0000000
--- a/stack/ruby_rails/power_types/patrones/values.md
+++ /dev/null
@@ -1,33 +0,0 @@
-# Values
-
-Los values corresponden a clases Ruby que pueden ser utilizadas para contener información que no persiste en la base de datos, y por lo tanto solo existe en memoria. Entonces si por ejemplo, generamos dinámicamente un reporte, en vez de retornarlo como `Hash`:
-
-```ruby
-class BuildCleaningReport < PowerTypes::Command.new(:data)
- def perform
- # execute report logic, and finally return:
- {
- date: @date,
- area: cleaned_area,
- duration: cleaning.time,
- effiency: cleaned_area / cleaning.time
- }
- end
-end
-```
-
-Mejor encapsular el resultado en una clase `Report`:
-
-```ruby
-# app/values/report.rb
-class Report
- attr_accesor :date, :area, :duration
-
- def eficciency
- area / duration
- end
-end
-```
-
-Estos objetos pueden ser utilizados para mover la información de forma
-estructurada dentro de las distintas capas de la aplicación.
diff --git a/stack/ruby_rails/pry.md b/stack/ruby_rails/pry.md
deleted file mode 100644
index 380afb2..0000000
--- a/stack/ruby_rails/pry.md
+++ /dev/null
@@ -1,72 +0,0 @@
-# Pry
-
-[Pry](https://github.com/pry/pry) es una consola para ruby que apunta a ser un reemplazo a IRB. En particular ofrece una mejor experiencia añadiendo funcionalidades que no están presentes en la consola built-in, como syntax highlighting, navegación en clases y debugging.
-
-## ¿Por qué lo usamos?
-
-La consola es más cómoda que IRB y tiene ciertos añadidos que mejoran la experiencia. Además pry nos permite introducir breakpoints en el código sin esfuerzo. Esto sirve para debuggear fácilmente en cualquier sector del código, ya sean controladores, servicios, comandos, modelos, inicializadores, etc. El debugging es muy importante en el proceso de desarrollar aplicaciones y ahorra muchísimo tiempo.
-
-Por otro lado, la integración de pry con los proyectos es transparente y sin complicaciones, por lo que esta herramienta es ideal para ser incluida.
-
-## ¿Cómo lo usamos?
-
-Pry viene incluido por defecto en Potassium, por lo que los proyectos generados incluyen un archivo de configuración `.pryrc` que, entre otras cosas, agrega alias para ciertas funcionalidades. En proyectos que no lo tengan, se puede instalar usando `potassium install pry [--force]` con el cuidado de revisar los cambios en git.
-
-Al ejecutar `bundle exec rails c` ya se está usando pry, sin embargo, una forma que entrega mucho valor a la hora de debuggear es introduciendo breakpoints en el código. Para hacerlo, en cualquier parte del código se puede introducir el siguiente statement: `binding.pry`. Con eso, cuando corra el servidor y la aplicación llegue a ese punto, la ejecución se verá pausada y en la consola se podrá explorar y acceder al scope y variables que tiene la aplicación en ese momento.
-
-## Ejemplo
-
-Supongamos que queremos debuggear el método show del siguiente controlador:
-
-```ruby
-class CarsController < Api::V1::BaseController
- def show
- respond_with car
- end
-
- private
-
- def car
- @car ||= Car.find(params.require(:id))
- end
-end
-```
-
-Para hacerlo debemos agregar un breakpoint, de forma que el método show quede así:
-
-```ruby
-def show
- binding.pry
- respond_with car
-end
-```
-
-Al ir a esa ruta, en este caso un `GET` al endpoint `/api/v1/cars/`, la ejecución se detendrá cuando llegue a esa línea y en consola donde esté corriendo el servidor, se verá lo siguiente:
-
-
-
-Una vez ahí, hay acceso a todo el scope que tiene ese método, y pry nos permite, entre otras cosas, lo siguiente:
-
-* Explorar los métodos y atributos disponibles en el contexto ejecutando `ls`, en este caso todo lo presente dentro del método del controlador.
-
-* Ver los valores de las variables a las que se puede acceder. En este caso podremos por ejemplo explorar el valor de `params` y las llaves que contiene.
-
-* Avanzar por línea con `next` o el alias `n`.
-
-* Avanzar por llamado a función con `step` o el alias `s`.
-
-* Salir del breakpoint con `continue` o `c` y seguir en la ejecución.
-
-Acá un ejemplo de debugging completo:
-
-
-
-Se puede ver que pry muestra no solo la línea donde se detuvo la ejecución, sino que también lo que la rodea, y el nombre del scope donde está parado. En este caso se ve que en primera instancia se detuvo en la línea 9, dentro del controlador señalado como `#`.
-
-Como nota adicional, es importante que estos breakpoints no deben aparecer nunca en código que se va a subir y publicar, se deben mantener solo de manera temporal para development. De todas maneras contamos con la ayuda de linters configurados para que aleguen cuando se nos esté pasando una de esas líneas a un commit.
-
-## Recursos Útiles
-
-* [Pry cheat sheet](https://gist.github.com/lfender6445/9919357): [@lfender6445](https://gist.github.com/lfender6445) tiene una guía rápida con algunos de los comandos útiles que provee pry.
-
-* [Repositorio](https://github.com/pry/pry): tiene documentación, guía de instalación y una [wiki](https://github.com/pry/pry/wiki) bastante completa, sin embargo cubre casos de uso mucho más profundos/complejos. Los ejemplos que ya se incluyen en esta guía cubren la mayor parte de los casos de uso comunes.
diff --git a/stack/ruby_rails/pundit.md b/stack/ruby_rails/pundit.md
deleted file mode 100644
index c524860..0000000
--- a/stack/ruby_rails/pundit.md
+++ /dev/null
@@ -1,446 +0,0 @@
-# Pundit
-
-[Pundit](https://github.com/varvet/pundit) es la gema de Ruby que utilizamos para dar permisos de acceso en nuestras aplicaciones.
-
-## ¿Por qué la usamos?
-
-* Para encapsular la lógica de permisos en un solo lugar (las policies) y separarla de los recursos (controllers y modelos).
-
-* Porque es simple de entender y configurar.
-
-* Porque se integra bien con Active Admin.
-
-## ¿Cómo la usamos?
-
-### Instalación
-
-Podemos instalar la gema con [potassium](https://github.com/platanus/potassium) al crear un proyecto o luego así:
-
-
-
-### Uso básico
-
-Autorizar un recurso con Pundit sigue este camino:
-
-
-
-**Punto 1**
-
-Un usuario hace una request a un recurso. En este caso `User#1` intenta acceder a un blog post a través de la url `GET /blog_posts/2`
-
-**Punto 2**
-
-La solicitud llega a `BlogPostsController#show` action.
-
-```ruby
-class BlogPostsController < ApplicationController
- def show
- authorize blog_post
- respond_with blog_post
- end
-
- private
-
- def blog_post
- @blog_post ||= BlogPost.find(params[:id])
- end
-end
-```
-
-La primera línea que se ejecuta es `authorize(blog_post)`. Este método `authorize` viene con Pundit y lo que hace es buscar dentro de `/app/policies` **una cuyo nombre contenga la clase del recurso que pasamos a ****`authorize`**** como primer parámetro + el sufijo Policy**. En este caso como el objeto `blog_post` es una instancia del modelo `BlogPost`, Pundit buscará la policy `BlogPostPolicy` en `/app/policies/blog_post_policy.rb`. Si no la encuentra, lanzará el error `Pundit::NotDefinedError` y deberás agregarla. Si la encuentra, Pundit **internamente** hará algo así:
-
-```ruby
-BlogPostPolicy.new(current_user, blog_post).show?
-```
-
-Donde:
-
-1. `BlogPostPolicy` es la policy que se infiere a partir de la clase del recurso. En el ejemplo se infiere a partir de `BlogPost.find(3)`.
-
-1. `current_user` es el usuario logueado. En el ejemplo: `User.find(1)`.
-
-1. `blog_post` es el recurso sobre el que decidimos si el usuario puede acceder o no. En el ejemplo: `BlogPost.find(params[:id])` o `BlogPost.find(3)` que es lo mismo.
-
-1. `show?` es un método en la policy que tiene la lógica de permisos para la acción `BlogPostsController#show`. Es importante tener en cuenta que esto también funciona por convención. Si la acción del controller es `show` en la policy Pundit buscará internamente **un método de instancia con el mismo nombre + ?** → `show?`
-
-**Punto 3**
-
-Como dijimos en el punto anterior, al encontrar la `BlogPostPolicy` se busca y ejecuta el método de instancia `show?`. Este método define la lógica de permisos para un recurso específico (una instancia del modelo `BlogPost`) y una acción de controller específica (`show` en este caso).
-
-Entonces, si tenemos la policy:
-
-```ruby
-class BlogPostPolicy < ApplicationPolicy
- def show?
- user.blog_posts.where(id: record.id).any?
- end
-end
-```
-
-y la siguiente información en la DB:
-
-
-
-Veamos qué ocurre con los siguientes flujos:
-
-* `User#1` intenta acceder a `/blog_posts/2`.
-
- En este caso:
-
- * `user`: es igual a `current_user` o lo que es igual `User.find(1)`
-
- * `blog_posts`: es una colección (de Active Record) de `BlogPost`s que contiene dos instancias. Una con id 1 y otra con id 2. Esto porque `user` es dueño de esos dos posts según la info que definimos más arriba.
-
- * `record`: es el recurso. En el ejemplo `BlogPost.find(2)`
-
- Con lo anterior podemos ver si `User#1` tiene acceso sobre `BlogPost#2` en la acción `BlogPostsController#show`
-
- ```ruby
- class BlogPostPolicy < ApplicationPolicy
- def show?
- user.blog_posts.where(id: record.id).any? #=> true
- # User.find(1).blog_posts.where(id: BlogPost.find(2).id).any? #=> true
- # User#1 que es dueño de los blog posts 1 y 2 contiene a 2? #=> true
- end
- end
- ```
-
- Como resulta ser que sí tiene permisos, el método `authorize` de `BlogPostsController#show` devuelve `true` y permite ejecutar `respond_wih blog_post` devolviendo la información requerida por `User#1`.
-
-* `User#2` intenta acceder a `/blog_posts/3`.
-
- En este caso ocurre algo similar al ejemplo anterior porque `User#2` es dueño del `BlogPost#3`.
-
-* `User#1` intenta acceder a `/blog_posts/3`.
-
- En este caso el código en `show?` evalúa `false` y Pundit lanza la exception `Pundit::NotAuthorizedError` que impide que `User#1` acceda al recurso `BlogPost#3`
-
- ```ruby
- class BlogPostPolicy < ApplicationPolicy
- def show?
- user.blog_posts.where(id: record.id).any? #=> false
- # User.find(1).blog_posts.where(id: BlogPost.find(3).id).any? #=> false
- # User#1 que es dueño de los blog posts 1 y 2 contiene a 3? #=> false
- end
- end
- ```
-
-### Integración con Active Admin
-
-Normalmente en nuestros proyectos Platanus tenemos dos dominios bien definidos:
-
-1. La app.
-
-1. El admin o back office.
-
-En el primero normalmente el usuario logueado es una instancia de `User`. En cambio, en el segundo, es un `AdminUser`. Algo también común, no solo en los proyectos Platanus, es que los permisos sobre un mismo recurso (supongamos una instancia de `BlogPost`) son bien distintos en un dominio u otro. Por ej es frecuente que un usuario admin pueda acceder a recursos de otros usuarios pero este comportamiento es poco frecuente del lado de la app.
-
-Con este nuevo panorama, modifiquemos la policy del ejemplo para autorizar un recurso en ambos dominios.
-
-Habíamos dicho que para que `User#1` acceda a `/blog_posts/2` la policy se ve así:
-
-```ruby
-class BlogPostPolicy < ApplicationPolicy
- def show?
- user.blog_posts.where(id: record.id).any? #=> true
- # User.find(1).blog_posts.where(id: BlogPost.find(2).id).any? #=> true
- # User#1 que es dueño de los blog posts 1 y 2 contiene a 2? #=> true
- end
-end
-```
-
-Ahora supongamos que `AdminUser#1` quiere acceder a `/admin/blog_posts/2` utilizando la misma policy.
-
-```ruby
-class BlogPostPolicy < ApplicationPolicy
- def show?
- user.blog_posts.where(id: record.id).any? #=> NoMethodError: undefined method `blog_posts'
- # AdminUser.find(1).blog_posts.where(id: BlogPost.find(2).id).any?
- end
-end
-```
-
-Es hacer esto devuelve un error ya que la variable `user` de `show?` en vez de contener una instancia de `User` ahora contiene una de `AdminUser`. El problema con esto es que como `AdminUser` no tiene la relación `blog_posts` definida, el código falla. La forma de arreglar esto es agregar lógica de acceso entendiendo que la variable `user` a veces será una instancia de `User` (cuando se acceda desde el dominio de la app) pero otras de `AdminUser` (cuando se acceda desde back office). La policy corregida se ve así:
-
-```ruby
-class BlogPostPolicy < ApplicationPolicy
- def show?
- case user
- when User
- user.blog_posts.where(id: record.id).any?
- when AdminUser
- true
- else
- raise 'invalid user type'
- end
- end
-end
-```
-
-Ahora el código funciona porque antes de evaluar el permiso estamos viendo si el recurso se está accediendo a través de un `User` o un `AdminUser`. Si bien esta solución es “aceptable”, mantener esta estrategia complicaría la lógica de las policies ya que todas arrastrarían el problema de “si es admin hacer x pero si es user hacer y”. Para evitar esto, en Platanus **usamos policies diferentes para la app y el admin.** El código con este cambio queda de la siguiente manera:
-
-En `/app/policies/blog_post_policy.rb` seguimos poniendo, igual que antes, los permisos para el dominio de la app.
-
-```ruby
-class BlogPostPolicy < ApplicationPolicy
- def show?
- user.blog_posts.where(id: record.id).any?
- end
-end
-```
-
-En cambio para el admin, agregamos una nueva policy para el mismo recurso (`BlogPost`) dentro de `app/policies/back_office/blog_post_policy.rb`
-
-```ruby
-class BackOffice::BlogPostPolicy < BackOffice::DefaultPolicy
- def show?
- true
- end
-end
-```
-
-De esta manera se simplifica la lógica ya que del lado de la app `user` siempre será una instancia de `User` en cambio en el admin, siempre será `AdminUser`.
-
-### Pundit Admin Adapter
-
-Como mencioné anteriormente, una de las ventajas de usar Pundit es que [se integra bien con Active Admin](https://activeadmin.info/13-authorization-adapter.html) que es el framework de administración que usamos en Platanus.
-
-En la práctica, esto significa que para autorizar un recurso en la back office solo se debe definir la policy y Active Admin hará el resto. Por ej: si tengo el recurso `Team` y la policy:
-
-```ruby
-class BackOffice::TeamPolicy < BackOffice::DefaultPolicy
- def show?
- false
- end
-end
-```
-
-Active Admin esconderá automáticamente la opción “Ver” de ese recurso y la ruta `/admin/teams/:id` no existirá.
-
-
-
-Algo importante a tener en cuenta es que al trabajar con [acciones custom](https://activeadmin.info/8-custom-actions.html) (aquellas definidas por `member_action` o `collection_action`) Active Admin manejará la visibilidad de la ruta pero los links habrá que manejarlos manualmente. Por ej si tengo:
-
-```ruby
-ActiveAdmin.register Team do
- member_action :import_form, method: :get do
- render("admin/import_form", locals: { team: resource })
- end
-
- action_item :import_form, only: [:show] do
- link_to "Ir al import form", import_form_admin_team_path(resource)
- end
-end
-```
-
-y la policy:
-
-```ruby
-class BackOffice::TeamPolicy < BackOffice::DefaultPolicy
- def import_form?
- false
- end
-end
-```
-
-la ruta `/admin/teams/3/import_form` no existirá pero el link en el header sí:
-
-
-
-Para corregir esto, se debe agregar una condición al `action_item` así:
-
-```ruby
-action_item :import_form, only: [:show], if: proc { authorized?(:import_form, resource) } do
- link_to "Ir al import form", import_form_admin_team_path(resource)
-end
-```
-
-De esta manera, solo se muestra el link si `authorized?(:import_form, resource)` evalúa `true`. Como ya se imaginarán, `authorized?` es un método de Active Admin que conecta con Pundit y permite preguntar para una acción específica (en este caso `:import_form` definida por la `member_action`) si el admin logueado puede acceder a `resource` (en este caso una instancia de `Team` → `resource = Team.find(3)`) o no.
-
-### Default Policy
-
-Para controlar el acceso **por default **a los recursos es que agregamos una clase base a las policies. Entonces, las de la app heredarán de `ApplicationPolicy` → `/app/policies/application_policy.rb` y las de admin de `BackOffice::DefaultPolicy` → `/app/policies/back_office/default_policy.rb`.
-
-Por ej, si tengo la policy:
-
-```ruby
-class BackOffice::BlogPostPolicy < BackOffice::DefaultPolicy
-end
-```
-
-con la siguiente clase base:
-
-```ruby
-class BackOffice::DefaultPolicy
- # ...
-
- def index?
- false
- end
-
- def show?
- false
- end
-
- def create?
- false
- end
-
- def new?
- create?
- end
-
- def update?
- false
- end
-
- def edit?
- update?
- end
-
- def destroy?
- false
- end
-
- # ...
-end
-```
-
-si como un admin user intento acceder a `/admin/blog_posts/3`, no podré hacerlo porque al no tener `BackOffice::BlogPostPolicy` definida la acción `show?` se accederá **por defecto **al método `:show?` definido en `BackOffice::DefaultPolicy`, evaluará `false` y no me dejará acceder a la vista.
-
-> ℹ️ Dependiendo del tipo de aplicación y lo sensible que sea el tema seguridad en la misma, nos convendrá o no hacer que las clases base sean más o menos restrictivas.
-
-### Preguntas frecuentes
-
-**¿Cómo manejar un admin que puede ver todo y otro que tiene acceso restringido?**
-
-Supongamos que tenemos el modelo `BlogPost` que tiene un atributo `deleted_at` que se llena cuando alguien borra el blog. Supongamos además que queremos que desde la back office, un `AdminUser` con rol “super admin” pueda ver absolutamente todos los blogs pero uno con rol “supervisor” solo pueda ver aquellos que no fueron borrados. Para lograr esto haremos lo siguiente:
-
-1. Agregar scopes a `BlogPost` para poder obtener fácilmente recursos borrados.
-
- ```ruby
- class BlogPost < ApplicationRecord
- scope :published, -> { where(deleted_at: nil) }
- end
- ```
-
-1. Definir un atributo rol en el modelo `AdminUser` así:
-
- ```ruby
- class AdminUser < ApplicationRecord
- enum role: { supervisor: 0, super_admin: 1 }, _default: 'supervisor', _prefix: true
- end
- ```
-
-1. Modificar la policy para definir las reglas de acceso para cada rol:
-
- ```ruby
- class BackOffice::BlogPostPolicy < BackOffice::DefaultPolicy
- def show?
- scope = admin_user.role_supervisor? ? :published : :all
- BlogPost.send(scope).where(id: record.id).any?
- end
- end
- ```
-
- En el código anterior se puede ver que se buscará el blog post (`record`) específico en la colección completa en el caso de que el `AdminUser` logueado (`admin_user`) sea un `super_admin` o en la colección filtrada (solo aquellos que no fueron borrados) en el caso de que sea `supervisor`.
-
-**¿Cómo testear una policy con RSpec?**
-
-Tomemos como ejemplo la policy de la pregunta anterior:
-
-```ruby
-class BackOffice::BlogPostPolicy < BackOffice::DefaultPolicy
- def show?
- scope = admin_user.role_supervisor? ? :published : :all
- BlogPost.send(scope).where(id: record.id).any?
- end
-end
-```
-
-Pasos a seguir:
-
-1. Agregar `require "pundit/rspec"` al archivo `spec/rails_helper.rb` para tener los helpers de RSpec específicos de Pundit si es que no viene ya con Potassium.
-
-1. Agregar la policy en `spec/policies/back_office/blog_post_policy_spec.rb`
-
-1. Definir en `let`s todo aquellos que puede variar:
-
- 1. `admin_user`
-
- 1. `record`
-
- ```ruby
- describe BackOffice::BlogPostPolicy do
- subject { described_class }
-
- let(:role) { "super_admin" }
- let(:admin_user) { create(:admin_user, role: role) }
- let(:deleted_at) { nil }
- let(:record) { create(:block_post, deleted_at: deleted_at) }
-
- # ...
- end
- ```
-
-1. Probar los permisos sobre una acción variando los `let`s.
-
- ```ruby
- describe BackOffice::BlogPostPolicy do
- subject { described_class }
-
- let(:role) { "super_admin" }
- let(:admin_user) { create(:admin_user, role: role) }
- let(:deleted_at) { nil }
- let(:record) { create(:block_post, deleted_at: deleted_at) }
-
- permissions :show? do
- context "with super_admin role" do
- let(:role) { "super_admin" }
-
- it { expect(subject).to permit(admin_user, record) }
-
- context "with deleted record" do
- let(:deleted_at) { DateTime.current }
-
- it { expect(subject).to permit(admin_user, record) }
- end
- end
-
- context "with supervisor role" do
- let(:role) { "supervisor" }
-
- it { expect(subject).to permit(admin_user, record) }
-
- context "with deleted record" do
- let(:deleted_at) { DateTime.current }
-
- it { expect(subject).not_to permit(admin_user, record) }
- end
- end
- end
- end
- ```
-
-**¿Qué significa el error ****`Pundit::NotDefinedError`****?**
-
-
-
-Significa que nos falta agregar la policy para el recurso que acabamos de agregar. Se arregla agregando la policy y definiendo permisos para todas las acciones (que pueden ser las que vienen por default de `BackOffice::DefaultPolicy`)
-
-```ruby
-class BackOffice::TeamMemberPolicy < BackOffice::DefaultPolicy
-end
-```
-
-**¿Por qué en active admin no puedo acceder a un recurso?**
-
-
-
-Posiblemente porque el permiso está evaluando `false`. Por ej si vemos el error al entrar a `/admin/team_members/666` debemos revisar en `BackOffice::TeamMemberPolicy` si el permiso en `show?` está evaluando `true` o `false`. Si resulta que la acción no está definida en esa policy habrá que revisar `BackOffice::DefaultPolicy`
-
-## Recursos útiles
-
-* [Github de Pundit](https://github.com/varvet/pundit)
-
-* [Receta](https://github.com/platanus/potassium/blob/master/lib/potassium/recipes/pundit.rb) de potassium (puede ser útil para ver qué se instala)
diff --git a/stack/ruby_rails/shrine.md b/stack/ruby_rails/shrine.md
deleted file mode 100644
index a948fc9..0000000
--- a/stack/ruby_rails/shrine.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Shrine
-
-[General](shrine/general.md)
-
-[Manejo y procesamiento de imágenes](shrine/manejo_y_procesamiento_de_imagenes.md)
diff --git a/stack/ruby_rails/shrine/assets/manejo-y-procesamiento-de-imagenes-1.qt b/stack/ruby_rails/shrine/assets/manejo-y-procesamiento-de-imagenes-1.qt
deleted file mode 100644
index 9c26e3b..0000000
Binary files a/stack/ruby_rails/shrine/assets/manejo-y-procesamiento-de-imagenes-1.qt and /dev/null differ
diff --git a/stack/ruby_rails/shrine/assets/manejo-y-procesamiento-de-imagenes-2.qt b/stack/ruby_rails/shrine/assets/manejo-y-procesamiento-de-imagenes-2.qt
deleted file mode 100644
index 993bde1..0000000
Binary files a/stack/ruby_rails/shrine/assets/manejo-y-procesamiento-de-imagenes-2.qt and /dev/null differ
diff --git a/stack/ruby_rails/shrine/assets/manejo-y-procesamiento-de-imagenes-3.png b/stack/ruby_rails/shrine/assets/manejo-y-procesamiento-de-imagenes-3.png
deleted file mode 100644
index 3fafe05..0000000
Binary files a/stack/ruby_rails/shrine/assets/manejo-y-procesamiento-de-imagenes-3.png and /dev/null differ
diff --git a/stack/ruby_rails/shrine/general.md b/stack/ruby_rails/shrine/general.md
deleted file mode 100644
index a51db3e..0000000
--- a/stack/ruby_rails/shrine/general.md
+++ /dev/null
@@ -1,188 +0,0 @@
-# General
-
-[Shrine](https://github.com/shrinerb/shrine) es una gema para manejar de manera simple la tarea de subir y adjuntar archivos.
-
-## ¿Por qué la usamos?
-
-Anteriormente usábamos Paperclip, hasta que fue deprecada en favor de la solución incluida en Rails, ActiveStorage. Dado esto, nos cambiamos a ActiveStorage (AS) para no quedarnos con una herramienta sin soporte.
-
-Sin embargo, hay algunos detalles que se manejan mejor en Shrine, por ejemplo:
-
-* Shrine tiene un diseño modular, utilizando `plugins` para las distintas funcionalidades que ofrece. Esto permite cargar solo las que en verdad se usen
-
-* Shrine promueve separación de responsabilidades, introduciendo el concepto de `Uploader`. Estos se encargan de la lógica de subida de un tipo de archivo en particular
-
-* AS no tiene validaciones, por ejemplo de tamaño o tipo de archivo. En Shrine se pueden agregar fácilmente usando el plugin [validation_helpers](https://shrinerb.com/docs/plugins/validation_helpers)
-
-* Recién en Rails 6.1 se está dando soporte a acceso público de archivos, con Shrine se puede definir a nivel de configuración o por `Uploader`
-
-* Con Shrine se pueden setear `default_url` por Uploader, para cuando el archivo es `nil`
-
-## ¿Cómo la usamos?
-
-### Instalación
-
-La gema viene instalada si el proyecto se generó usando [Potassium](https://github.com/platanus/potassium) con la opción `Shrine` elegida como *storage*. También se incluyen un par de uploaders que sirven como base. Si el proyecto se generó sin una opción de storage, se puede agregar corriendo `potassium install file_storage` y seleccionando `Shrine`.
-
-### Ejemplo básico
-
-> Para estos ejemplos se utilizó la versión 3.2.1 de Shrine
-
-Supongamos que tenemos un `Uploader` de imágenes genérico, que podría verse así:
-
-```ruby
-class ImageUploader < Shrine
- plugin :validation_helpers
-
- Attacher.validate do
- validate_mime_type %w[image/jpeg image/jpg image/png image/svg+xml image/gif]
- end
-end
-```
-
-Aquí tenemos el plugin [validation_helpers](https://shrinerb.com/docs/plugins/validation_helpers), que nos permite usar `validate_mime_type` para verificar que el tipo del archivo corresponda efectivamente a una imagen.
-
-Con esto ya podemos empezar a incluir imágenes en los modelos. Si queremos un `attachment` llamado `photo` habría que agregar la columna `photo_data` a la tabla (tipo `text` o `jsonb`) y agregar lo siguiente en el modelo:
-
-```ruby
-include ImageUploader::Attachment(:photo)
-```
-
-> El nombre de la columna siempre debe incluir el sufijo _data
-
-### Ejemplo: heredando de un uploader
-
-Ahora, imaginemos que se quiere agregar una imagen de perfil para los usuarios. Podríamos usar el `ImageUploader` definido antes, pero se quieren un par de cosas extra para esta `profile_picture`:
-
-* Límite de peso, 5 MB. Pueden haber muchas imágenes de perfil distintas en una misma vista y no queremos que quede muy pesada
-
-* El usuario puede elegir no tener una foto de perfil
-
-* Comúnmente se usará la imagen en dos tamaños
-
-Como esta sigue siendo una imagen y queremos mantener la validación definida en `ImageUploader`, vamos a definir un nuevo uploader que herede de este:
-
-```ruby
-class ProfilePictureUploader < ImageUploader
-end
-```
-
-**Límite de peso**
-
-Para esto recurrimos nuevamente al `validations_helper`, esta vez usando `validate_max_size`. Para mantener las validaciones de la clase padre, [se debe llamar a ](https://shrinerb.com/docs/plugins/validation#inheritance)[`super()`](https://shrinerb.com/docs/plugins/validation#inheritance), quedando así:
-
-```ruby
-Attacher.validate do
- super()
- validate_max_size 5*1024*1024
-end
-```
-
-**Sin foto de perfil**
-
-En estos casos queremos poner una imagen por defecto, que indique claramente la ausencia de la foto de perfil. Digamos que tenemos una imagen para este propósito en `/app/assets/images/no-profile-picture.png`. Podemos usar el [plugin ](https://shrinerb.com/docs/plugins/default_url)[`default_url`](https://shrinerb.com/docs/plugins/default_url) para usarla siempre que no haya una imagen de perfil:
-
-```ruby
-plugin :default_url
-
-Attacher.default_url do |**options|
- ActionController::Base.helpers.image_url('no-profile-picture.png')
-end
-```
-
-`ActionController::Base.helpers.image_url` nos ayuda a obtener la url final de uno de los assets del proyecto.
-
-Con esto, todo usuario cuya `profile_picture` sea `nil` retornará la url del asset al hacer `user.profile_picture_url`.
-
-**Distintos tamaños**
-
-Vamos a procesar la imagen para tener dos tamaños aparte del original: `small` y `medium`. Shrine tiene dos opciones para realizar el procesamiento: dinámicamente [cuando se pide](https://shrinerb.com/docs/plugins/derivation_endpoint) la transformación (on-the-fly) o [con anterioridad](https://shrinerb.com/docs/plugins/derivatives), guardando el resultado. Para este ejemplo usaremos la segunda opción.
-
-Como prerequisito necesitamos agregar la gema [image_processing](https://github.com/janko/image_processing). Luego podemos definir las `derivatives` para los tamaños `small` y `medium`:
-
-```ruby
-require "image_processing/vips"
-
-...
-
-plugin :derivatives
-
-Attacher.derivatives do |original|
- vips = ImageProcessing::Vips.source(original)
-
- {
- small: vips.resize_to_limit!(300, 300),
- medium: vips.resize_to_limit!(500, 500),
- }
-end
-```
-
-Después en el controlador se debe gatillar la creación de estas derivadas:
-
-```ruby
-user = User.new(..., profile_picture: file)
-
-if user.valid?
- user.profile_picture_derivatives! if user.profile_picture_changed?
- user.save
-end
-```
-
-Y para acceder a la url de una de las derivadas:
-
-```ruby
-User.last.profile_picture_url(:small)
-```
-
-**Resultado**
-
-Con todo esto, nuestro `Uploader` que hereda de `ImageUploader` quedaría así:
-
-```ruby
-require "image_processing/vips"
-
-class ProfilePictureUploader < ImageUploader
- plugin :default_url
- plugin :derivatives
-
- Attacher.validate do
- super()
- validate_max_size 5*1024*1024
- end
-
- Attacher.default_url do |**options|
- ActionController::Base.helpers.image_url('no-profile-picture.png')
- end
-
- Attacher.derivatives do |original|
- vips = ImageProcessing::Vips.source(original)
-
- {
- small: vips.resize_to_limit!(300, 300),
- medium: vips.resize_to_limit!(500, 500)
- }
- end
-end
-```
-
-Y para agregar el attachment al modelo de usuario:
-
-```ruby
-include ProfilePictureUploader::Attachment(:profile_picture)
-```
-
-### Recursos útiles
-
-* [Documentación oficial](https://shrinerb.com/): muy buena, con guías y secciones explicando los distintos plugins
-
-* [Repo en Github](https://github.com/shrinerb/shrine)
-
-* [Direct S3 Upload](https://github.com/shrinerb/shrine/wiki/Adding-Direct-S3-Uploads) / [Direct App Upload](https://github.com/shrinerb/shrine/wiki/Adding-Direct-App-Uploads): ambos sirven para subir un archivo antes de que se le haga submit a un form. La diferencia radica en que el de S3 lo sube directamente a AWS, mientras que el otro lo sube a un `upload_endpoint` de la aplicación. Para ambientes que no tienen un bucket S3 (como `development`, en que se guardan los archivos en el filesystem) habría que usar Direct App Upload
-
-* [Demo Direct Upload + Vue](https://drive.google.com/file/d/1fwrZ1tLZa_xeSp2j57iKFgjNlgDxXAM9/view?usp=sharing): presentación al equipo de Platanus para introducir Shrine. Se arma un componente Vue para manejar el Direct Upload que puede ser usado dentro de un form de Rails
-
-* [Testing](https://shrinerb.com/docs/testing): en la sección de [Test data](https://shrinerb.com/docs/testing#test-data) dan un ejemplo de un helper que puede ser usado en las factories. En la sección de [Acceptance tests](https://shrinerb.com/docs/testing#acceptance-tests) dan un ejemplo de como agregar un archivo como parámetro en tests de controladores usando `Rack::Test::UploadedFile`
-
-### Recursos útiles para plataneros
-
-* [Implementación Direct Upload](https://github.com/platanus/gret/pull/22): PR implementando Direct Upload para ser usado en ActiveAdmin
diff --git a/stack/ruby_rails/shrine/manejo_y_procesamiento_de_imagenes.md b/stack/ruby_rails/shrine/manejo_y_procesamiento_de_imagenes.md
deleted file mode 100644
index 3372b90..0000000
--- a/stack/ruby_rails/shrine/manejo_y_procesamiento_de_imagenes.md
+++ /dev/null
@@ -1,263 +0,0 @@
-# Manejo y procesamiento de imágenes
-
-## Motivación
-
-Consideremos una vista con imágenes subidas por usuarios, donde tenemos menos control sobre lo que suben. Si las mostramos tal cuál fueron subidas con un `` y nada más, podríamos tener una carga no muy atractiva, algo así:
-
-[Ver video](assets/manejo-y-procesamiento-de-imagenes-1.qt)
-
-Hay varios problemas con esto:
-
-* Mientras las imágenes no se han cargado se ve un espacio vacío
-
-* Cuando se van cargando hay un momento que se ven a la mitad
-
-* Se demoran harto en cargar todas
-
-En esta sección de la guía vamos a ver algunas cosas que se pueden hacer con Shrine para que potencialmente la carga sea más amigable:
-
-[Ver video](assets/manejo-y-procesamiento-de-imagenes-2.qt)
-
-Las cosas que veremos acá, también [fueron implementadas en Potassium](https://github.com/platanus/potassium/pull/398), ahí en el PR puedes ver más detalles también.
-
-## [Blurhash](https://blurha.sh/)
-
-Del video anterior, probablemente lo que más llama la atención son esas versiones borrosas de las imágenes que aparecen antes de que las imágenes mismas se muestren. Eso se logra con Blurhash: una herramienta que permite representar una imagen como un string que luego se decodifica en el frontend y se obtiene algo que se pueden pintar en un `