<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Juan Manuel Ramallo</title>
    <description>The latest articles on DEV Community by Juan Manuel Ramallo (@juanmanuelramallo).</description>
    <link>https://dev.to/juanmanuelramallo</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F73282%2Fd890c5af-20a3-4b52-aced-69e07d8f3737.jpeg</url>
      <title>DEV Community: Juan Manuel Ramallo</title>
      <link>https://dev.to/juanmanuelramallo</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/juanmanuelramallo"/>
    <language>en</language>
    <item>
      <title>How to deploy Apache Superset with Dokku?</title>
      <dc:creator>Juan Manuel Ramallo</dc:creator>
      <pubDate>Sun, 02 Jul 2023 20:44:22 +0000</pubDate>
      <link>https://dev.to/juanmanuelramallo/how-to-deploy-apache-superset-with-dokku-2i8m</link>
      <guid>https://dev.to/juanmanuelramallo/how-to-deploy-apache-superset-with-dokku-2i8m</guid>
      <description>&lt;p&gt;&lt;strong&gt;Why?&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Because it is much easier than doing everything manually&lt;/li&gt;
&lt;li&gt;Configuration changes are deployed via git (&lt;code&gt;git push dokku main&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Reverse proxy (nginx) is configured by Dokku (no lines of nginx config files are touched, not even looked at)&lt;/li&gt;
&lt;li&gt;SSL is configured by a Dokku plugin (I don't even know how to use certbot)&lt;/li&gt;
&lt;li&gt;Ideal to spin up a Superset instance quickly to test things out&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Why not?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Well, if the plan is to serve thousands of dashboards and charts for thousands of users, maybe look into the kubernetes installation.&lt;/p&gt;




&lt;p&gt;Before we start, ACME is used as an example of a company name. Feel free to replace with your own.&lt;/p&gt;

&lt;h3&gt;
  
  
  In your virtual machine
&lt;/h3&gt;

&lt;h2&gt;
  
  
  1. Install dokku
&lt;/h2&gt;

&lt;p&gt;Go to &lt;a href="https://dokku.com/docs/getting-started/installation/"&gt;Dokku's installation page&lt;/a&gt; and follow the steps. There's no need to have Docker pre-installed, Dokku installer will take care of that.&lt;/p&gt;

&lt;p&gt;Make sure to follow all steps until the end including the ones about adding your SSH key and setting the global domain. A domain is required to run Superset with SSL.&lt;/p&gt;

&lt;p&gt;The SSH key must belong to the machine from where you intend to deploy Superset, so that &lt;code&gt;git push&lt;/code&gt; can authenticate.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Create the app in Dokku
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dokku apps:create acme-superset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3. Install postgres/redis
&lt;/h2&gt;

&lt;p&gt;Install &lt;a href="https://github.com/dokku/dokku-postgres"&gt;postgres plugin&lt;/a&gt; and &lt;a href="https://github.com/dokku/dokku-redis"&gt;redis plugin&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Then create the services and link them to the app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;dokku plugin:install https://github.com/dokku/dokku-postgres.git postgres
&lt;span class="nb"&gt;sudo &lt;/span&gt;dokku plugin:install https://github.com/dokku/dokku-redis.git redis

dokku postgres:create acme-superset
dokku postgres:link acme-superset acme-superset

dokku redis:create acme-superset
dokku redis:link acme-superset acme-superset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is optional, but I recommend doing it.&lt;/p&gt;

&lt;p&gt;By default Superset uses SQLite, which will not allow us to create multiple Datasets pointing to different tables but with the same name. This is solved by using Postgres as the metastore (metastore is how Superset documentation refers to the database where Supersets objects are stored—dashboards, charts, etc).&lt;/p&gt;

&lt;p&gt;Redis is also optional but it's nice to have in case you want to configure data caching.&lt;/p&gt;

&lt;p&gt;Postgres and Redis connection strings will be present as environment variables in the acme-superset application:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dokku config:show acme-superset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;small&gt;After running the link command you may encounter a message like &lt;code&gt;App image (dokku/acme-superset:latest) not found&lt;/code&gt;, just ignore them.&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Configure the default port
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dokku proxy:ports-add acme-superset http:80:8088
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default Superset uses the &lt;code&gt;8088&lt;/code&gt; port, but in order to properly configure SSL it is required for us to proxy the port &lt;code&gt;80&lt;/code&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  In your local machine
&lt;/h3&gt;

&lt;h2&gt;
  
  
  1. Create a new local repository
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;acme-superset
&lt;span class="nb"&gt;cd &lt;/span&gt;acme-superset
git init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a new folder and initialize git. We'll use this folder as the Dokku application to deploy.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Create a config.py
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;curl https://raw.githubusercontent.com/apache/superset/2.1.0/superset/config.py -o config.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The config.py from your repo will be placed instead of the default config.py.&lt;/p&gt;

&lt;p&gt;Grab the default config.py from &lt;a href="https://github.com/apache/superset/blob/2.1.0/superset/config.py"&gt;github.com/apache/superset/blob/&lt;strong&gt;VERSION&lt;/strong&gt;/superset/config.py&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To use the database connection string from the &lt;code&gt;DATABASE_URL&lt;/code&gt; env var, update the config.py file as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;SQLALCHEMY_DATABASE_URI&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"DATABASE_URL"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To use the redis connection string from the &lt;code&gt;REDIS_URL&lt;/code&gt; en var, update the config.py file as follows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Cache for datasource metadata and query results
&lt;/span&gt;&lt;span class="n"&gt;DATA_CACHE_CONFIG&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;CacheConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"CACHE_TYPE"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"RedisCache"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"CACHE_DEFAULT_TIMEOUT"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;int&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timedelta&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;hours&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="n"&gt;total_seconds&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
    &lt;span class="s"&gt;"CACHE_KEY_PREFIX"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"superset_data_cache_"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"CACHE_REDIS_URL"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;environ&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"REDIS_URL"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure anything else as needed.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Create a Dockerfile
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# ./Dockerfile&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; apache/superset:2.1.0&lt;/span&gt;
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; root&lt;/span&gt;

&lt;span class="c"&gt;# Set my secret key&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; SUPERSET_SECRET_KEY=SUPER_SECRET_KEY_PLEASE_REPLACE_ME&lt;/span&gt;

&lt;span class="c"&gt;# Use my config&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; config.py superset/config.py&lt;/span&gt;

&lt;span class="c"&gt;# Add database drivers&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;psycopg2
&lt;span class="k"&gt;RUN &lt;/span&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;sqlalchemy-bigquery

&lt;span class="c"&gt;# Adds vim to be able to enter the container and read files with vim&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;apt-get update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; apt-get &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nb"&gt;install &lt;/span&gt;vim

&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; superset&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;First line allows us to select what version to use. This example uses 2.1.0, latest version as of this writing.&lt;/p&gt;

&lt;p&gt;Add any database driver needed. This example adds the driver for Bigquery. &lt;a href="https://superset.apache.org/docs/databases/installing-database-drivers"&gt;More drivers can be found here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The base image can be found in &lt;a href="https://hub.docker.com/r/apache/superset"&gt;Docker hub&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Add Dokku's remote repository
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote add dokku dokku@dokku.acme.com:acme-superset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Where &lt;code&gt;dokku.acme.com&lt;/code&gt; is the global domain configured for Dokku and &lt;code&gt;acme-superset&lt;/code&gt; is the name of the application in Dokku.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Deploy
&lt;/h2&gt;

&lt;p&gt;Commit all files and push to Dokku's remote.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'Superset initial configuration'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push dokku main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The deploy will start and you should have output in the terminal about the Dockerfile steps we defined previously.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Enumerating objects: 4, &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Counting objects: 100% &lt;span class="o"&gt;(&lt;/span&gt;4/4&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Delta compression using up to 8 threads
Compressing objects: 100% &lt;span class="o"&gt;(&lt;/span&gt;4/4&lt;span class="o"&gt;)&lt;/span&gt;, &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Writing objects: 100% &lt;span class="o"&gt;(&lt;/span&gt;4/4&lt;span class="o"&gt;)&lt;/span&gt;, 23.48 KiB | 5.87 MiB/s, &lt;span class="k"&gt;done&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Total 4 &lt;span class="o"&gt;(&lt;/span&gt;delta 0&lt;span class="o"&gt;)&lt;/span&gt;, reused 0 &lt;span class="o"&gt;(&lt;/span&gt;delta 0&lt;span class="o"&gt;)&lt;/span&gt;, pack-reused 0
&lt;span class="nt"&gt;-----&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Cleaning up...
&lt;span class="nt"&gt;-----&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; Building jm-superset from Dockerfile
remote: build context to Docker daemon  66.56kB
Step 1/14 : FROM apache/superset:2.1.0
...

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally &lt;a href="https://dokku.com/docs/deployment/application-deployment/#setting-up-ssl"&gt;configure SSL&lt;/a&gt; in the Dokku machine via the &lt;a href="https://github.com/dokku/dokku-letsencrypt"&gt;letsencrypt plugin&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# This is performed in the virtual machine, where Dokku is running&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;dokku plugin:install https://github.com/dokku/dokku-letsencrypt.git

&lt;span class="c"&gt;# Set global email for letsencrypt&lt;/span&gt;
dokku letsencrypt:set &lt;span class="nt"&gt;--global&lt;/span&gt; email admin@acme.com

dokku letsencrypt:enable acme-superset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it, open &lt;code&gt;acme-superset.dokku.acme.com&lt;/code&gt; in your browser.&lt;/p&gt;

&lt;p&gt;Superset is running and you are able to change its configuration, install new drivers, change version, and pretty much do anything in a very easy manner.&lt;/p&gt;




&lt;h3&gt;
  
  
  Final considerations
&lt;/h3&gt;

&lt;p&gt;The first time you deploy superset, you may need to create an admin user, migrate the database and run &lt;code&gt;superset init&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Enter the container by running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dokku enter acme-superset
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Create an admin&lt;/span&gt;
superset fab create-admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--username&lt;/span&gt; admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--firstname&lt;/span&gt; Superset &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--lastname&lt;/span&gt; Admin &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--email&lt;/span&gt; admin@superset.com &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--password&lt;/span&gt; admin

&lt;span class="c"&gt;# Migrate DB&lt;/span&gt;
superset db upgrade

&lt;span class="c"&gt;# Init and setup roles&lt;/span&gt;
superset init
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Refer to &lt;a href="https://superset.apache.org/docs/intro"&gt;Superset documentation&lt;/a&gt;{:target="_blank"} for more information about its configuration.&lt;/p&gt;

&lt;p&gt;Happy dashboarding.&lt;/p&gt;

&lt;p&gt;&lt;small&gt;I have a Superset instance running smoothly in a Digital Ocean Droplet. &lt;a href="https://m.do.co/c/cc1a72a2e544"&gt;Get $200 in credit over 60 days to try this out.&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;

</description>
      <category>dokku</category>
      <category>superset</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>How to find the PID of a process using a specific port</title>
      <dc:creator>Juan Manuel Ramallo</dc:creator>
      <pubDate>Tue, 14 Dec 2021 00:20:37 +0000</pubDate>
      <link>https://dev.to/juanmanuelramallo/how-to-find-the-pid-of-a-process-using-a-specific-port-o0e</link>
      <guid>https://dev.to/juanmanuelramallo/how-to-find-the-pid-of-a-process-using-a-specific-port-o0e</guid>
      <description>&lt;p&gt;To find out what processes are using a specific port, use &lt;code&gt;lsof&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;lsof &lt;span class="nt"&gt;-i&lt;/span&gt; :PORT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Examples
&lt;/h2&gt;

&lt;p&gt;List all processes using the 5432 port (commonly used by postgres)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;lsof &lt;span class="nt"&gt;-i&lt;/span&gt; :5432
COMMAND  PID   USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
postgres 777    1ma    7u  IPv6 0x0123456789abcdef 0t0  TCP localhost:postgresql &lt;span class="o"&gt;(&lt;/span&gt;LISTEN&lt;span class="o"&gt;)&lt;/span&gt;
postgres 777    1ma    8u  IPv4 0x1123456789abcdef 0t0  TCP localhost:postgresql &lt;span class="o"&gt;(&lt;/span&gt;LISTEN&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List all processes using the 4000 port (this blog in development)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;lsof &lt;span class="nt"&gt;-i&lt;/span&gt; :4000
COMMAND   PID   USER   FD   TYPE             DEVICE SIZE/OFF NODE NAME
ruby    59182    1ma   10u  IPv4 0x2123456789abcdef      0t0  TCP localhost:terabase &lt;span class="o"&gt;(&lt;/span&gt;LISTEN&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Alternatives
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;netstat&lt;/code&gt; can be used with grep to look for the port, but not possible in macOS as &lt;code&gt;netstat&lt;/code&gt; won't output the process ID.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ps -ef&lt;/code&gt; can be used with grep too, but we'll need some bash sorcery to keep the first line of the &lt;code&gt;ps&lt;/code&gt; output (the headers line).&lt;/p&gt;

</description>
      <category>linux</category>
    </item>
    <item>
      <title>How to see all AWS services currently in use</title>
      <dc:creator>Juan Manuel Ramallo</dc:creator>
      <pubDate>Thu, 27 May 2021 13:52:02 +0000</pubDate>
      <link>https://dev.to/juanmanuelramallo/how-to-see-all-aws-services-currently-in-use-2ho</link>
      <guid>https://dev.to/juanmanuelramallo/how-to-see-all-aws-services-currently-in-use-2ho</guid>
      <description>&lt;p&gt;Ever wondered what a black hole looks like? My AWS Billing—or yours—might be one of the closest things to a black hole on earth. So scary, and so hard to remove things from it, firstly because we don’t know what services to remove nor the regions for those unwanted services.&lt;/p&gt;

&lt;p&gt;Let’s focus on how to remove things from the AWS bill, which is the main task that will definitively cleanse our bill. It’s so easy to spin up new services in AWS and forget about them, some might be cheap some others might not be that cheap; and given that AWS—at the time of this writing—provides over 175 services in several regions each, it becomes quite a task to manage all of your services in all of the regions you used.&lt;/p&gt;

&lt;p&gt;Enter the Bills page. Go to your Billing Dashboard, and then on the left sidebar, under the Billing section, click on Bills. Below AWS Service Charges you can see all of the services you have signed up for, and inside each of them, you can see the incurred costs per region. This way you just need to figure out what services are not in use, then go to their regions and remove them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--JtnzOcmb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lhr9b98txb013vma4u2v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--JtnzOcmb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/lhr9b98txb013vma4u2v.png" alt="Billing page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This way I was able to spot an unused RDS instance and a very old Amplify instance, both burning money with no returns.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>billing</category>
    </item>
    <item>
      <title>Why avoid too many abstractions in tests? </title>
      <dc:creator>Juan Manuel Ramallo</dc:creator>
      <pubDate>Sat, 13 Feb 2021 21:20:08 +0000</pubDate>
      <link>https://dev.to/juanmanuelramallo/why-avoid-too-many-abstractions-in-tests-3bi7</link>
      <guid>https://dev.to/juanmanuelramallo/why-avoid-too-many-abstractions-in-tests-3bi7</guid>
      <description>&lt;h3&gt;
  
  
  Intro
&lt;/h3&gt;

&lt;p&gt;There's production code, and there's test code. Production code refers to any portion of the codebase that is executed in production environments. Similarly, test code refers to any portion of the codebase that is solely executed in testing environments, where production code is exercised via the test code.&lt;/p&gt;

&lt;p&gt;Test code needs to be explicit, easy to read and understand what's going on. Test abstractions, like shared examples, are usually against what we need out of test code.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why not to DRY test code?
&lt;/h3&gt;

&lt;p&gt;Refactoring production code tested with several abstractions in test code becomes harder. This is because one single test abstraction is used across several places in test code, potentially causing your changes to break some tests but not all of them. Finally, triggering a refactoring task on the test abstraction as well or maybe even updating your tests that failed to stop using the abstraction cause it no longer fits the test.&lt;/p&gt;

&lt;p&gt;Test reports with failures are harder to read. If a test using an abstraction failed inside the abstraction then the report will show where it failed first, and that is inside the abstraction. It requires the developer to read over the stack trace to check what test called the abstraction in order to find out what test is actually failing.&lt;/p&gt;

&lt;p&gt;This is the output of RSpec's Shared Examples&lt;/p&gt;

&lt;pre&gt;
Finished in 3.17 seconds (files took 2.49 seconds to load)
47 examples, 5 failures

Failed examples:

rspec ./spec/requests/api/v1/users_spec.rb[1:2:2:2:1:1:1] # Api::V1::Users PATCH #update behaves like ...
rspec ./spec/requests/api/v1/users_spec.rb[1:2:4:1:1:1] # Api::V1::Users PATCH #reset_change_email behaves like ...
rspec ./spec/requests/api/v1/users_spec.rb[1:2:5:1:1:1:1] # Api::V1::Users PATCH #request_remove_user behaves like ...
rspec ./spec/requests/api/v1/users_spec.rb[1:2:1:1:1:1] # Api::V1::Users GET #show behaves like ...
rspec ./spec/requests/api/v1/users_spec.rb[1:2:3:1:1:1] # Api::V1::Users PATCH #reset_api_key behaves like ...
&lt;/pre&gt;

&lt;p&gt;beer's on me if you can tell where the failure occurred&lt;/p&gt;

&lt;h3&gt;
  
  
  Don't get me wrong
&lt;/h3&gt;

&lt;p&gt;Too many abstractions for our test code is like highlighting every single word of the book you're reading. You'd only want to highlight the most important parts right?&lt;/p&gt;

&lt;p&gt;With test code is quite similar, we don't want to abstract everything but we'd rather keep test code as explicit and readable as possible and at the same time provide abstractions for the parts that are not really meaningful for test cases. For instance, the factory pattern to create objects for test code is perfect – re: &lt;a href="https://github.com/thoughtbot/factory_bot"&gt;FactoryBot&lt;/a&gt;. &lt;a href="https://relishapp.com/rspec/rspec-expectations/docs/custom-matchers"&gt;Custom matchers&lt;/a&gt; are also a valid way to introduce abstractions while keeping our test code explicit and declarative.&lt;/p&gt;

&lt;p&gt;To sum up, abstractions for non meaningful parts of test code are valid and really useful. Abstractions for test code that describes actual behavior of the system are never[&lt;cite&gt;*&lt;/cite&gt;] good.&lt;/p&gt;

&lt;p&gt;* I wrote a shared example last month, out of a code review suggestion, and I can live with it&lt;/p&gt;

</description>
      <category>testing</category>
      <category>ruby</category>
      <category>rspec</category>
      <category>sharedexamples</category>
    </item>
    <item>
      <title>I don't use nil</title>
      <dc:creator>Juan Manuel Ramallo</dc:creator>
      <pubDate>Sat, 17 Oct 2020 22:01:33 +0000</pubDate>
      <link>https://dev.to/juanmanuelramallo/i-don-t-use-nil-55h4</link>
      <guid>https://dev.to/juanmanuelramallo/i-don-t-use-nil-55h4</guid>
      <description>&lt;p&gt;I don't use &lt;code&gt;nil&lt;/code&gt;, or at least I try not to.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NoMethodError: undefined method 'some_method' for nil:NilClass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is one of the most common errors seen on production systems. And it's likely to happen everywhere where a variable can reference to a null value. Here are some case studies about the topic.&lt;/p&gt;

&lt;h2&gt;
  
  
  Migrations
&lt;/h2&gt;

&lt;p&gt;When creating/updating a table I always consider adding a &lt;code&gt;null: false&lt;/code&gt; constraint and a default value accordingly. For string values, the default would be an empty string. For number values, the default value would be zero.&lt;/p&gt;

&lt;p&gt;This allows us to do operations with those columns without worrying about them being &lt;code&gt;nil&lt;/code&gt;, because we took care of that at the database level. For instance, let's consider a &lt;code&gt;Post&lt;/code&gt; model in a blogging system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# db/migrate/...&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreatePosts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;6.0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;
    &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="ss"&gt;:posts&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="c1"&gt;# [snip]&lt;/span&gt;

      &lt;span class="c1"&gt;# not bad&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="ss"&gt;:excerpt&lt;/span&gt;

      &lt;span class="c1"&gt;# better&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt; &lt;span class="ss"&gt;:excerpt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;

&lt;span class="c1"&gt;# app/views/posts/show.html.erb&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sx"&gt;%= @post.excerpt.downcase %&amp;gt;
&amp;lt;/p&amp;gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this particular example, sending &lt;code&gt;downcase&lt;/code&gt; to &lt;code&gt;Post#excerpt&lt;/code&gt; will always work because we ensured that the value will always return a String object.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enums
&lt;/h2&gt;

&lt;p&gt;Let's consider that any post can have a certain category. Since these categories won't have any extra info nor behavior, we'll use an enum attribute in the post model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# db/migrate/...&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CreatePosts&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ActiveRecord&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Migration&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mf"&gt;6.0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;change&lt;/span&gt;
    &lt;span class="n"&gt;create_table&lt;/span&gt; &lt;span class="ss"&gt;:posts&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
      &lt;span class="c1"&gt;# [snip]&lt;/span&gt;

      &lt;span class="c1"&gt;# not bad&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;integer&lt;/span&gt; &lt;span class="ss"&gt;:category&lt;/span&gt;

      &lt;span class="c1"&gt;# better&lt;/span&gt;
      &lt;span class="n"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;integer&lt;/span&gt; &lt;span class="ss"&gt;:category&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;null: &lt;/span&gt;&lt;span class="kp"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;default: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can easily add a default enum option to represent a post without a category. And why is it better? For the simple reason that the &lt;code&gt;Post#category&lt;/code&gt; method will always return the same object type. This will cause the code to avoid checking for &lt;code&gt;nil&lt;/code&gt; when working with the category, and will also make the safe navigational operator useless when working with this attribute.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# app/models/post.rb&lt;/span&gt;
&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;enum&lt;/span&gt; &lt;span class="ss"&gt;category: &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;general: &lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;lifestlye: &lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;art: &lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;misc: &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example we are using the category named &lt;code&gt;general&lt;/code&gt; as the default one.&lt;/p&gt;

&lt;h2&gt;
  
  
  NullObject
&lt;/h2&gt;

&lt;p&gt;This approach is an elegant way to represent a null value. It is somewhat similar to the enum's case study. Here's an excellent &lt;a href="https://thoughtbot.com/blog/rails-refactoring-example-introduce-null-object"&gt;article from thoughtbot&lt;/a&gt; about this pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  undefined method 'conclusion' for nil:NilClass
&lt;/h2&gt;

&lt;p&gt;To sum up, I don't use &lt;code&gt;nil&lt;/code&gt; because I don't want the program to potentially raise this &lt;code&gt;NoMethodError&lt;/code&gt;. To avoid it, it's just as easy as not handling null values in the codebase. &lt;/p&gt;

&lt;p&gt;And this is Tony Hoare, the inventor of &lt;code&gt;nil&lt;/code&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years. &lt;a href="https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/"&gt;src&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
    </item>
    <item>
      <title>When objects become super objects</title>
      <dc:creator>Juan Manuel Ramallo</dc:creator>
      <pubDate>Tue, 11 Aug 2020 05:16:02 +0000</pubDate>
      <link>https://dev.to/juanmanuelramallo/when-objects-become-super-objects-17ke</link>
      <guid>https://dev.to/juanmanuelramallo/when-objects-become-super-objects-17ke</guid>
      <description>&lt;p&gt;A pencil. We use a pencil to write. And that's it. (Let's forget about some pencils that come with an eraser in one end, in this world they don't exist)&lt;/p&gt;

&lt;p&gt;We are in the "digital transformation" era, right? So I came up with the idea of including a simple scanner in the body of the pencil, to be able to digitalize everything written with the pencil. It's really handy!&lt;/p&gt;

&lt;p&gt;I always struggled to share my technical drawings when exchanging thoughts in groups, so I think a mini projector on top of the pencil would be a great addition. It's amazing, I can show my drawings directly in the wall, no need for any external projector!&lt;/p&gt;

&lt;p&gt;Oh, and a laser pointer on it. You know, everyone loves playing with lasers.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;We call it The Pencil 3000 ™&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--1OK3FObR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kp2hraso8orvpz836a7d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--1OK3FObR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/i/kp2hraso8orvpz836a7d.png" alt="the-pencil-3000"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's stop here. I've experienced this very same line of thinking when creating/updating classes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Multiple responsibility classes: Real life examples
&lt;/h2&gt;

&lt;p&gt;These are the places where I've encountered myself working with this approach:&lt;/p&gt;

&lt;p&gt;&lt;small&gt;&lt;em&gt;CTMPS&lt;/em&gt; means "comment to my past self".&lt;/small&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Models
&lt;/h4&gt;

&lt;p&gt;Several times I wrote methods in the model that were only being used in the views. &lt;em&gt;CTMPS&lt;/em&gt;: It'd be better to use a &lt;a href="https://www.rubyguides.com/2019/09/rails-patterns-presenter-service/"&gt;presenter object&lt;/a&gt; for those.&lt;/p&gt;

&lt;p&gt;Numerous times I found myself writing callback methods in the model, with the purpose of sending emails or adding default values in the model. &lt;em&gt;CTMPS&lt;/em&gt;: Sending emails seems like a side effect of a user action, why don't trigger them in the controller instead? And about setting default values, what about setting them using the &lt;a href="https://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html"&gt;ActiveRecord::Attributes API&lt;/a&gt;?&lt;/p&gt;

&lt;p&gt;Don't get me started with inline validation methods. This I one I abused. I'm sorry. &lt;em&gt;CTMPS&lt;/em&gt;: There are no taxes for new classes, create a &lt;a href="https://api.rubyonrails.org/classes/ActiveModel/Validator.html"&gt;validator class&lt;/a&gt; and be done with it. Peace out.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Form objects
&lt;/h4&gt;

&lt;p&gt;Select tags with options that are not DB backed. Guilty. I more than once wrote a method in the form object to provide the options to be used in the select tag, in the view. &lt;em&gt;CTMPS&lt;/em&gt;: It's ok no big deal, but what about using a presenter object for this? Given it's a view-only matter.&lt;/p&gt;




&lt;p&gt;At this point it feels like I'm seeking absolution from my sins. Hopefully you haven't experienced any of these. But in any case, let me share you one more example.&lt;/p&gt;




&lt;h4&gt;
  
  
  3. Page objects
&lt;/h4&gt;

&lt;p&gt;Testing usually requires &lt;a href="https://thoughtbot.com/blog/four-phase-test"&gt;4 steps&lt;/a&gt;: setup, exercise, assertions and teardown. And when testing web applications, end users interact with HTML documents. &lt;em&gt;Enter page objects&lt;/em&gt;. So we use &lt;a href="https://martinfowler.com/bliki/PageObject.html"&gt;page objects&lt;/a&gt; to wrap the HTML and provide a meaningful API to be used in test code. This includes the exercise and assertion steps of testing. But I gotta be honest, it is so easy and pleasant to just write a new method into the page object to do the setup and create instances, update stuff, set up environment variables and whatever needed for our tests to run. &lt;em&gt;CTMPS&lt;/em&gt;: It's as easy and pleasant to extract the creational part into a support &lt;a href="https://rubyapi.org/2.7/o/module"&gt;module&lt;/a&gt; in your specs, and keep your page objects fulfilling their &lt;em&gt;true purpose&lt;/em&gt;.&lt;/p&gt;




&lt;p&gt;Remember The Pencil 3000™? I can't imagine how would I sharp the pencil, with all that stuff around it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Single-responsibility principle
&lt;/h2&gt;

&lt;p&gt;"[...] each software module should have one and only one reason to change." &lt;a href="https://blog.cleancoder.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html"&gt;Better read it from here&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>oop</category>
    </item>
    <item>
      <title>Do you use form objects in your Rails apps?</title>
      <dc:creator>Juan Manuel Ramallo</dc:creator>
      <pubDate>Fri, 10 Apr 2020 02:48:22 +0000</pubDate>
      <link>https://dev.to/juanmanuelramallo/yet-another-active-form-4874</link>
      <guid>https://dev.to/juanmanuelramallo/yet-another-active-form-4874</guid>
      <description>&lt;p&gt;Introducing &lt;a href="https://github.com/rootstrap/yaaf" rel="noopener noreferrer"&gt;yaaf&lt;/a&gt;, a gem to ease the usage of the form object pattern in rails apps.&lt;/p&gt;

&lt;h2&gt;
  
  
  My pain points (before yaaf)
&lt;/h2&gt;

&lt;p&gt;The form object pattern is widely used across Rails apps, and yet we tend to write these form objects in a different manner from project to project.&lt;/p&gt;

&lt;p&gt;Most of the time, they don't even have the same interface across form objects in the same app.&lt;/p&gt;

&lt;p&gt;We also tend to write validations twice, in the model and in the form object, just so that the form object has the errors and the view can display them.&lt;/p&gt;

&lt;p&gt;Well, I can say that most of our in-house form objects make good use of database transactions. But I bet we rarely provide the controller the ability to use a banged method, expecting it to raise an error if things go south.&lt;/p&gt;

&lt;p&gt;Business logic is present everywhere in your app, controllers, models, helpers (some hardcore scenarios might include Rails initializers), but is that right? Many times we find ourselves with the need to add business logic around the creation of an object, such as sending an email, updating other records or even calling an external service. We know that the controller is not a good place, and the model would be terrible as well because it violates SRP. So we end up creating several service objects and throwing the business logic there as if it were a trashcan.&lt;/p&gt;

&lt;h2&gt;
  
  
  Instead of all of that, we can...
&lt;/h2&gt;

&lt;p&gt;use yaaf, it solves all of that in an easy way, let me explain:&lt;/p&gt;

&lt;p&gt;Same interface? yaaf will encourage you to use &lt;code&gt;save&lt;/code&gt; and just &lt;code&gt;save&lt;/code&gt; to, well you know, SAVE the data from your form into models.&lt;/p&gt;

&lt;p&gt;Contextual validations only? Please put them in your form objects, and keep data integrity validations where they belong, in your service objects, haha no, of course no, in your models! yaaf will take the liberty to promote all model errors collections into the form object errors collection, so you can show them in your views&lt;/p&gt;

&lt;p&gt;So, one of the form objects need to raise an error when not saved? As you may have guessed already, use the dangerous &lt;code&gt;save!&lt;/code&gt; method. yaaf already defines it so that it raises an error just as any Active Record model does. Of course, it will also trigger a database rollback when the models couldn't be saved. Both &lt;code&gt;save&lt;/code&gt; and &lt;code&gt;save!&lt;/code&gt; methods are available.&lt;/p&gt;

&lt;p&gt;Not sure where to put business logic related to the creation of an object? Well, the form object is the place. yaaf will allow you to use callbacks the same way as your model callbacks work. For instance, if you want to send an email after the form has been submitted and persisted, the &lt;code&gt;after_commit&lt;/code&gt; callback is the one you're looking for. More info can be found in the &lt;a href="https://github.com/rootstrap/yaaf#callbacks" rel="noopener noreferrer"&gt;readme&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Bob Ross of form objects
&lt;/h2&gt;

&lt;p&gt;Using yaaf will allow you to standardize your form object definitions across your project's production code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why not use this?
&lt;/h2&gt;

&lt;p&gt;If you would rather have controllers deal with all these responsibilities, then it's fine to keep doing it, you may not need yaaf if that's the case.&lt;/p&gt;

&lt;p&gt;Are you a service-objects-for-all person? Then you might feel better writing service objects rather than form objects.&lt;/p&gt;

&lt;p&gt;If you work with Java, well you don't want to use this.&lt;/p&gt;

&lt;h2&gt;
  
  
  A bit of history
&lt;/h2&gt;

&lt;p&gt;We have been using this very same approach on a production app for almost a year now, more than 10 form objects written this way, and it has served us well. That's why we decided to extract it to a gem. (yaaf is no more than 64 lines currently, and we need no more)&lt;/p&gt;

&lt;p&gt;Big thanks to &lt;a href="https://twitter.com/santib6_" rel="noopener noreferrer"&gt;@santib&lt;/a&gt; for shaping up this gem from its very beginning.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8oph1n9lr3ow2t321o9a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fi%2F8oph1n9lr3ow2t321o9a.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The lovely branding from yaaf was done by &lt;a href="https://dribbble.com/SofiSalazar" rel="noopener noreferrer"&gt;Sofi Salazar&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/rootstrap" rel="noopener noreferrer"&gt;
        rootstrap
      &lt;/a&gt; / &lt;a href="https://github.com/rootstrap/yaaf" rel="noopener noreferrer"&gt;
        yaaf
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Easing the form object pattern in Rails applications
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


</description>
      <category>rails</category>
      <category>ruby</category>
      <category>patterns</category>
      <category>formobject</category>
    </item>
    <item>
      <title>Have you heard of API Blueprint? 📘</title>
      <dc:creator>Juan Manuel Ramallo</dc:creator>
      <pubDate>Tue, 21 May 2019 01:38:52 +0000</pubDate>
      <link>https://dev.to/juanmanuelramallo/have-you-heard-of-api-blueprint-pp5</link>
      <guid>https://dev.to/juanmanuelramallo/have-you-heard-of-api-blueprint-pp5</guid>
      <description>&lt;p&gt;Recently, I came across the need of providing some sort of documentation for an API I was building. At that moment, I had never heard of the API Blueprint specification. Though they might be several options to write a good API documentation, I found this one to be very helpful for the following reasons.&lt;/p&gt;

&lt;h1&gt;
  
  
  1. Easy to read
&lt;/h1&gt;

&lt;p&gt;Sections are well defined and grouped by route. Each route can have several different HTTP methods with names associated to each method. The documentation shows:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Attributes listed with their descriptions (when needed)&lt;/li&gt;
&lt;li&gt;Requests examples (headers and body)&lt;/li&gt;
&lt;li&gt;Responses of those examples (also headers and body)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://apiblueprint.org/documentation/tutorial.html"&gt;See more.&lt;/a&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  2. Mock server
&lt;/h1&gt;

&lt;p&gt;This language allows you to create a mock server that anyone can use to test the response of the actual server (there are tools for this, &lt;a href="https://apiblueprint.org/tools.html#mock%20servers"&gt;see more.&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;So the mock server is actually a server that responds to the endpoints defined in the documentation and returns the expected responses (it does not care about authentication or headers, cause it actually does not run the production code, it just returns the response to the endpoint hit). So it's very helpful to have this kind of server in the early stages of an app, where the client application is still being built and the backend is not fully deployed to a production/testing cloud based environment.&lt;/p&gt;

&lt;h1&gt;
  
  
  3. Lots of tools
&lt;/h1&gt;

&lt;p&gt;Renderers are services that take your blueprint as input and create a nice web page for others to refer to, when using you API. &lt;a href="https://apiary.io"&gt;Apiary&lt;/a&gt; is one of them, and a widely used one. Apiary can be connected with your remote repository, so whenever you blueprint changes, it will automatically reflect the changes in the documentation page.&lt;/p&gt;

&lt;p&gt;But I don't want to write another language, and probably neither do you. So here's where docs-generator libraries take place. For those using Ruby❤️, &lt;a href="https://github.com/zipmark/rspec_api_documentation"&gt;RSpec API documentation&lt;/a&gt; turned out to be a great tool to generate this blueprint (though it can be used to generate other type of docs). You just need to write some RSpec tests and run a rake task to generate the blueprint.&lt;/p&gt;

&lt;p&gt;Here's an example of a basic endpoint being documented using this gem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;resource&lt;/span&gt; &lt;span class="s1"&gt;'Books'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;create_list&lt;/span&gt; &lt;span class="ss"&gt;:book&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;                &lt;span class="c1"&gt;# Creating some books &lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;route&lt;/span&gt; &lt;span class="s1"&gt;'/books'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Books collection'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="c1"&gt;# Name of the section&lt;/span&gt;
    &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s1"&gt;'Index'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;                      &lt;span class="c1"&gt;# HTTP method with name&lt;/span&gt;
      &lt;span class="n"&gt;example&lt;/span&gt; &lt;span class="s1"&gt;'Ok'&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;                   &lt;span class="c1"&gt;# A request example&lt;/span&gt;
        &lt;span class="n"&gt;do_request&lt;/span&gt;

        &lt;span class="n"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt; &lt;span class="n"&gt;eq&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;        &lt;span class="c1"&gt;# Basic expectation of the request&lt;/span&gt;
      &lt;span class="k"&gt;end&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;After running &lt;code&gt;rails docs:generate&lt;/code&gt; it'll generate a file with something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight apiblueprint"&gt;&lt;code&gt;&lt;span class="p"&gt;#&lt;/span&gt;&lt;span class="k"&gt; Group &lt;/span&gt;&lt;span class="gh"&gt;Books&lt;/span&gt;

&lt;span class="p"&gt;##&lt;/span&gt;&lt;span class="gh"&gt; Books collection &lt;/span&gt;&lt;span class="s"&gt;[/books]&lt;/span&gt;

&lt;span class="p"&gt;###&lt;/span&gt;&lt;span class="gh"&gt; Index &lt;/span&gt;&lt;span class="s"&gt;[GET]&lt;/span&gt;
&lt;span class="p"&gt;
+&lt;/span&gt; Request Ok
&lt;span class="p"&gt;
+&lt;/span&gt; Response 200 ()&lt;span class="sb"&gt;

    + Body

            {
              ... // The response body would be here
            }
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;If you are writing an API give this language a try. Make your fellow developers happy by giving them a proper documentation of your API.&lt;/p&gt;

&lt;h1&gt;
  
  
  To sum up, some things I like about it
&lt;/h1&gt;

&lt;ol&gt;
&lt;li&gt;Production code does not get mixed with the doc-generating code&lt;/li&gt;
&lt;li&gt;All documentation related code lives in one place&lt;/li&gt;
&lt;li&gt;As soon as a change is made in routes or in the response of the controllers the documentation gets updated automatically by the generator. I don't need to write anything new.&lt;/li&gt;
&lt;li&gt;Mock server just uses the data from the docs (it does not run the production code)&lt;/li&gt;
&lt;li&gt;There are bunch of &lt;a href="https://apiblueprint.org/tools.html#renderers"&gt;services to render&lt;/a&gt; the documentation (renderers) or I can &lt;a href="https://github.com/jejacks0n/apitome"&gt;host my own 😎&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;My API blueprint file lives in the same repository, so I can assure it's always synced with the current branch&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;br&gt;
&lt;a href="https://github.com/juanmanuelramallo/target-mvd/tree/master/spec/acceptance"&gt;In case you want to look a full set of doc-generating test examples&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

</description>
      <category>api</category>
      <category>apiblueprint</category>
      <category>rails</category>
      <category>rspec</category>
    </item>
    <item>
      <title>Does anyone else think HTML5 multiple select sucks?</title>
      <dc:creator>Juan Manuel Ramallo</dc:creator>
      <pubDate>Thu, 11 Oct 2018 03:58:28 +0000</pubDate>
      <link>https://dev.to/juanmanuelramallo/does-anyone-else-think-html5-multiple-selects-sucks-36ib</link>
      <guid>https://dev.to/juanmanuelramallo/does-anyone-else-think-html5-multiple-selects-sucks-36ib</guid>
      <description>&lt;p&gt;Am I the only one who thinks that html5 multiple selects are not intuitive for the web? I mean they are fine, work fine but I have to use CMD (or CTRL) to select more than one, doesn't feel as a web-ish thing 🙃&lt;/p&gt;

&lt;p&gt;Maybe it's just me being paranoid 🤪&lt;/p&gt;

&lt;p&gt;The thing is that I'm looking for a decent javascript module to handle multiple selects like &lt;a href="https://harvesthq.github.io/chosen/"&gt;Chosen&lt;/a&gt; or &lt;a href="https://select2.org/"&gt;Select2&lt;/a&gt; but &lt;strong&gt;without jQuery as dependency&lt;/strong&gt;. Any suggestions?&lt;/p&gt;

</description>
      <category>discuss</category>
      <category>javascript</category>
      <category>html</category>
      <category>webdev</category>
    </item>
    <item>
      <title>I was billed for 14k USD on Amazon Web Services 😱</title>
      <dc:creator>Juan Manuel Ramallo</dc:creator>
      <pubDate>Tue, 03 Jul 2018 01:11:38 +0000</pubDate>
      <link>https://dev.to/juanmanuelramallo/i-was-billed-for-14k-usd-on-amazon-web-services-17fn</link>
      <guid>https://dev.to/juanmanuelramallo/i-was-billed-for-14k-usd-on-amazon-web-services-17fn</guid>
      <description>

&lt;p&gt;We may agree that we lie to ourselves once in a while. I remember thinking of:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I'll never put my code on a public repository since it's a freelance project maintained only by myself.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The truth is, anything can happen in life.&lt;/p&gt;




&lt;p&gt;Month to month I use S3 from AWS to store photos and documents from several apps I got on production and also use Route 53 to route the domain with the app itself. My monthly bill is about 1 USD. Yep, that's almost nothing, but this month &lt;em&gt;(april last year)&lt;/em&gt; I had to pay 14,267.86 USD (well at least that's what my bill says)&lt;/p&gt;

&lt;p&gt;On april 13th (a sleepy Monday), it was like 10:00 in the morning when I got a happy email from Amazon Web Services giving me the welcome to EC2 services. By the time I received that email I neither know the existence of that service. That made me wonder if I had received that email by mistake or if my account … had been hacked.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5ypsybbx0ps9cyc1x41.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp5ypsybbx0ps9cyc1x41.jpg" alt="Billing dashboard" width="800" height="496"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Minutes later I wrote a message to AWS support asking about that email and they answered me very quickly and called me like five times in less than 48 hours. They told me that my account may had been compromised, and gave me some list of things to do in order to strengthen my account security and to avoid further EC2 service usage. I deleted all access keys I was using, added multi-factor authentication and changed my password. (I'm not endorsing or promoting AWS but I have to say that the guys from AWS support were extremely kind and useful in this case)&lt;/p&gt;

&lt;p&gt;It all started in a boring weekend (at least this is what I believe). I was on the chat with a friend telling him about a side-project I had been working on. And suddenly I decided to put my code on github to show him off all the stuff. It was up, in the web, for like 10 minutes max, after I switched it to be hosted on gitlab to make it private. Two days later I received that email from AWS I told you before.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;What happened?&lt;/em&gt;&lt;br&gt;
A file containing my AWS credentials hadn't have been ignored in git, so when I pushed my local repository it all, even my credentials, went online in github (for couple of minutes, but they were there).&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Suggestion&lt;/em&gt;&lt;br&gt;
Please, store your credentials secretly ALWAYS. You can use environment variables for storing access keys and credentials that may compromise your accounts or bills. And never think of a local repository only, when everyone knows that nowadays the internet is all over around.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;How this happened?&lt;/em&gt;&lt;br&gt;
Since you can list all public repositories on github, I imagine of a job/task/process/program running constantly and cloning each project and looking for .yml files and keywords like "KEY" or "ACCESS_KEY" or something like that. This is only my thought of how could this happen. If it happen like so, it makes me sad of how people can be malicious and with no concerns of consequences of their acts.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;TL;DR&lt;/em&gt;&lt;br&gt;
Never leave your api keys on public repositories (always check before uploading)&lt;br&gt;
Take a look at your email frequently and don't take a single email as a mistake&lt;br&gt;
Be a hacker so you can track and catch the guys who stole your keys (well that's only a dream)&lt;/p&gt;

&lt;p&gt;Hope you have a good day!&lt;br&gt;
(If you're still wondering, no, I didn't have to pay for what I didn't use)&lt;/p&gt;

</description>
      <category>aws</category>
      <category>rails</category>
      <category>config</category>
      <category>security</category>
    </item>
  </channel>
</rss>
