<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" ><generator uri="https://jekyllrb.com/" version="4.0.1">Jekyll</generator><link href="https://1ma.dev/feed.xml" rel="self" type="application/atom+xml" /><link href="https://1ma.dev/" rel="alternate" type="text/html" /><updated>2024-03-15T19:58:13+00:00</updated><id>https://1ma.dev/feed.xml</id><title type="html">Write it simple</title><subtitle>Simple programming short articles. Mostly ruby focused.</subtitle><entry><title type="html">About side projects</title><link href="https://1ma.dev/general/2024/03/03/about-side-projects.html" rel="alternate" type="text/html" title="About side projects" /><published>2024-03-03T00:00:00+00:00</published><updated>2024-03-03T00:00:00+00:00</updated><id>https://1ma.dev/general/2024/03/03/about-side-projects</id><content type="html" xml:base="https://1ma.dev/general/2024/03/03/about-side-projects.html">&lt;p&gt;There is something about writing code for me and no one else.&lt;/p&gt;

&lt;p&gt;It just feels different, it feels great.&lt;/p&gt;

&lt;!--more--&gt;

&lt;p&gt;Handcrafting a tool, that serves me exactly how I want.&lt;/p&gt;

&lt;p&gt;It may be similar as composing a song.&lt;/p&gt;

&lt;p&gt;Say hi to &lt;a href=&quot;https://onlydocs.1ma.dev&quot;&gt;Only Docs&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;For the past few weeks I’ve been working on a side project to generate slides from markdown. To be honest, I did nothing I just added a UI to existing tools I’ve been using. I’ve been teching Rails for a few years now. Two years ago I decided to revamp my slides and gave reveal.js a try. I fell in love.&lt;/p&gt;

&lt;p&gt;Reveal.js is an amazing tool to create slides—not only from markdown. It has lots of configurations available, the speaker view is just fantastic, adding notes work just fine, vertical and horizontal slides served me very well to group topics in my lectures, and given it’s just HTML everything is easy to further customize.&lt;/p&gt;

&lt;p&gt;These slides were written in Markdown and built with reveal.js—&lt;a href=&quot;https://github.com/I110IS/labs/blob/master/build&quot;&gt;the script used can be found here&lt;/a&gt;.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;In addition to that, after preparing a written document for a course I was taking in the university, the teachers asked us to present the work. Given we had already written the document in markdown, we only removed little bits and used reveal.js to create a presentation. The presentation was simple and nice, everything we needed to talk about the work done.&lt;/p&gt;

&lt;p&gt;We used Pandoc to generate the PDF first. It used the markdown as source. And turns out Pandoc also supports reveal.js as an output format. Nice.&lt;/p&gt;

&lt;hr /&gt;

&lt;p&gt;Eventually, this side project will not only support generating slides from markdown, but also a printable PDF files or ebooks or any other format I may need.&lt;/p&gt;

&lt;p&gt;Who knows, maybe in time it becomes a &lt;del&gt;side&lt;/del&gt; main project.&lt;/p&gt;

&lt;p&gt;For now, I’ll keep spending my days writing code and tests, but at nights only writing code.&lt;/p&gt;

&lt;p&gt;This is the start of &lt;a href=&quot;https://onlydocs.1ma.dev&quot;&gt;Only Docs&lt;/a&gt;&lt;/p&gt;</content><author><name></name></author><category term="general" /><category term="general" /><category term="onlydocs" /><summary type="html">There is something about writing code for me and no one else. It just feels different, it feels great.</summary></entry><entry><title type="html">How to deploy Apache Superset with Dokku?</title><link href="https://1ma.dev/how-to/2023/06/28/how-to-deploy-apache-superset-with-dokku.html" rel="alternate" type="text/html" title="How to deploy Apache Superset with Dokku?" /><published>2023-06-28T00:00:00+00:00</published><updated>2023-06-28T00:00:00+00:00</updated><id>https://1ma.dev/how-to/2023/06/28/how-to-deploy-apache-superset-with-dokku</id><content type="html" xml:base="https://1ma.dev/how-to/2023/06/28/how-to-deploy-apache-superset-with-dokku.html">&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 class=&quot;highlighter-rouge&quot;&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;hr /&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 id=&quot;in-your-virtual-machine&quot;&gt;In your virtual machine&lt;/h3&gt;

&lt;h2 id=&quot;1-install-dokku&quot;&gt;1. Install dokku&lt;/h2&gt;

&lt;p&gt;Go to &lt;a href=&quot;https://dokku.com/docs/getting-started/installation/&quot;&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 class=&quot;highlighter-rouge&quot;&gt;git push&lt;/code&gt; can authenticate.&lt;/p&gt;

&lt;h2 id=&quot;2-create-the-app-in-dokku&quot;&gt;2. Create the app in Dokku&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dokku apps:create acme-superset
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;3-install-postgresredis&quot;&gt;3. Install postgres/redis&lt;/h2&gt;

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

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

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;dokku plugin:install https://github.com/dokku/dokku-postgres.git postgres
&lt;span class=&quot;nb&quot;&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;/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 and using 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;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dokku config:show acme-superset
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

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

&lt;h2 id=&quot;4-configure-the-default-port&quot;&gt;4. Configure the default port&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dokku proxy:ports-add acme-superset http:80:8088
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

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

&lt;hr /&gt;

&lt;h3 id=&quot;in-your-local-machine&quot;&gt;In your local machine&lt;/h3&gt;

&lt;h2 id=&quot;1-create-a-new-local-repository&quot;&gt;1. Create a new local repository&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;mkdir &lt;/span&gt;acme-superset
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;acme-superset
git init
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 id=&quot;2-create-a-configpy&quot;&gt;2. Create a config.py&lt;/h2&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&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;/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=&quot;https://github.com/apache/superset/blob/2.1.0/superset/config.py&quot; target=&quot;_blank&quot;&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 class=&quot;highlighter-rouge&quot;&gt;DATABASE_URL&lt;/code&gt; env var, update the config.py file as follows:&lt;/p&gt;

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

&lt;p&gt;To use the redis connection string from the &lt;code class=&quot;highlighter-rouge&quot;&gt;REDIS_URL&lt;/code&gt; en var, update the config.py file as follows:&lt;/p&gt;

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

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

&lt;h2 id=&quot;3-create-a-dockerfile&quot;&gt;3. Create a Dockerfile&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;nb&quot;&gt;touch &lt;/span&gt;Dockerfile
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;c&quot;&gt;# ./Dockerfile&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; apache/superset:2.1.0&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; root&lt;/span&gt;

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

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

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

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

&lt;span class=&quot;k&quot;&gt;USER&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; superset&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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=&quot;https://superset.apache.org/docs/databases/installing-database-drivers&quot; target=&quot;_blank&quot;&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=&quot;https://hub.docker.com/r/apache/superset&quot; target=&quot;_blank&quot;&gt;Docker hub&lt;/a&gt;.&lt;/p&gt;

&lt;h2 id=&quot;4-add-dokkus-remote-repository&quot;&gt;4. Add Dokku’s remote repository&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git remote add dokku dokku@dokku.acme.com:acme-superset
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

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

&lt;h2 id=&quot;5-deploy&quot;&gt;5. Deploy&lt;/h2&gt;

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

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git add &lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
git commit &lt;span class=&quot;nt&quot;&gt;-m&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;Superset initial configuration&apos;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;git push dokku main
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;Enumerating objects: 4, &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
Counting objects: 100% &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;4/4&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
Delta compression using up to 8 threads
Compressing objects: 100% &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;4/4&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
Writing objects: 100% &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;4/4&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, 23.48 KiB | 5.87 MiB/s, &lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt;
Total 4 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;delta 0&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, reused 0 &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;delta 0&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;, pack-reused 0
&lt;span class=&quot;nt&quot;&gt;-----&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; Cleaning up...
&lt;span class=&quot;nt&quot;&gt;-----&lt;/span&gt;&lt;span class=&quot;o&quot;&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;/div&gt;

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

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

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

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

&lt;p&gt;That’s it, open &lt;code class=&quot;highlighter-rouge&quot;&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;hr /&gt;

&lt;h3 id=&quot;final-considerations&quot;&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 class=&quot;highlighter-rouge&quot;&gt;superset init&lt;/code&gt;.&lt;/p&gt;

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

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;dokku enter acme-superset
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

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

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

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

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

&lt;p&gt;Refer to &lt;a href=&quot;https://superset.apache.org/docs/intro&quot; target=&quot;_blank&quot;&gt;Superset documentation&lt;/a&gt; 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=&quot;https://m.do.co/c/cc1a72a2e544&quot; target=&quot;_blank&quot;&gt;Get $200 in credit over 60 days to try this out.&lt;/a&gt;&lt;/small&gt;&lt;/p&gt;</content><author><name></name></author><category term="how-to" /><category term="how-to" /><category term="dokku" /><summary type="html">Deploying Apache Superest via Dokku—git deployment, nginx, SSL out of the box</summary></entry><entry><title type="html">How to find the PID of a process using a specific port</title><link href="https://1ma.dev/how-to/2021/12/12/how-to-find-processes-by-port.html" rel="alternate" type="text/html" title="How to find the PID of a process using a specific port" /><published>2021-12-12T00:00:00+00:00</published><updated>2021-12-12T00:00:00+00:00</updated><id>https://1ma.dev/how-to/2021/12/12/how-to-find-processes-by-port</id><content type="html" xml:base="https://1ma.dev/how-to/2021/12/12/how-to-find-processes-by-port.html">&lt;p&gt;To find out what processes are using a specific port, use &lt;code class=&quot;highlighter-rouge&quot;&gt;lsof&lt;/code&gt;.&lt;/p&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;lsof &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; :PORT
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;!--more--&gt;

&lt;h2 id=&quot;examples&quot;&gt;Examples&lt;/h2&gt;

&lt;p&gt;List all processes using the 5432 port (commonly used by postgres)&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;➜ lsof &lt;span class=&quot;nt&quot;&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=&quot;o&quot;&gt;(&lt;/span&gt;LISTEN&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
postgres 777    1ma    8u  IPv4 0x1123456789abcdef 0t0  TCP localhost:postgresql &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;LISTEN&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;List all processes using the 4000 port (this blog in development)&lt;/p&gt;
&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;➜ lsof &lt;span class=&quot;nt&quot;&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=&quot;o&quot;&gt;(&lt;/span&gt;LISTEN&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;alternatives&quot;&gt;Alternatives&lt;/h2&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&gt;netstat&lt;/code&gt; can be used with grep to look for the port, but not possible in macOS as &lt;code class=&quot;highlighter-rouge&quot;&gt;netstat&lt;/code&gt; won’t output the process ID.&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;highlighter-rouge&quot;&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 class=&quot;highlighter-rouge&quot;&gt;ps&lt;/code&gt; output (the headers line).&lt;/p&gt;</content><author><name></name></author><category term="how-to" /><category term="how-to" /><category term="linux" /><summary type="html">To find out what processes are using a specific port, use lsof. lsof -i :PORT</summary></entry><entry><title type="html">How to cleanse your AWS bill</title><link href="https://1ma.dev/how-to/2021/05/23/how-to-cleanse-your-aws-bill.html" rel="alternate" type="text/html" title="How to cleanse your AWS bill" /><published>2021-05-23T00:00:00+00:00</published><updated>2021-05-23T00:00:00+00:00</updated><id>https://1ma.dev/how-to/2021/05/23/how-to-cleanse-your-aws-bill</id><content type="html" xml:base="https://1ma.dev/how-to/2021/05/23/how-to-cleanse-your-aws-bill.html">&lt;p&gt;Ever wondered what a &lt;a href=&quot;https://en.wikipedia.org/wiki/Black_hole#:~:text=A%20black%20hole%20is%20a,to%20form%20a%20black%20hole.&quot; target=&quot;_blank&quot;&gt;black hole&lt;/a&gt; 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 &lt;a href=&quot;https://console.aws.amazon.com/billing/home#/bills&quot; target=&quot;_blank&quot;&gt;Bills&lt;/a&gt;. 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;img src=&quot;/assets/aws-bills-page.png&quot; alt=&quot;AWS Bills Page&quot; /&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;</content><author><name></name></author><category term="how-to" /><category term="how-to" /><category term="aws" /><summary type="html">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.</summary></entry><entry><title type="html">Why avoid too many abstractions in tests?</title><link href="https://1ma.dev/ruby/2021/02/13/why-avoid-too-many-abstractions-in-tests.html" rel="alternate" type="text/html" title="Why avoid too many abstractions in tests?" /><published>2021-02-13T00:00:00+00:00</published><updated>2021-02-13T00:00:00+00:00</updated><id>https://1ma.dev/ruby/2021/02/13/why-avoid-too-many-abstractions-in-tests</id><content type="html" xml:base="https://1ma.dev/ruby/2021/02/13/why-avoid-too-many-abstractions-in-tests.html">&lt;h3 id=&quot;intro&quot;&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 id=&quot;why-not-to-dry-test-code&quot;&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)
&lt;font color=&quot;#CC0000&quot;&gt;47 examples, 5 failures&lt;/font&gt;

Failed examples:

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

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

&lt;h3 id=&quot;dont-get-me-wrong&quot;&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=&quot;https://github.com/thoughtbot/factory_bot&quot;&gt;FactoryBot&lt;/a&gt;. &lt;a href=&quot;https://relishapp.com/rspec/rspec-expectations/docs/custom-matchers&quot;&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;a href=&quot;/ruby/2021/02/13/why-avoid-too-many-abstractions-in-tests.html#i-can-live-with-it&quot;&gt;*&lt;/a&gt;&lt;/cite&gt;] good.&lt;/p&gt;

&lt;p&gt;&lt;sub id=&quot;i-can-live-with-it&quot;&gt;* I wrote a shared example last month, out of a code review suggestion, and I can live with it&lt;/sub&gt;&lt;/p&gt;</content><author><name></name></author><category term="ruby" /><category term="ruby" /><category term="testing" /><category term="rspec" /><category term="shared examples" /><summary type="html">Too many abstractions for our test code is like highlighting every single word of the book you&apos;re reading. You&apos;d only want to highlight the most important parts right?</summary></entry><entry><title type="html">I don’t use nil</title><link href="https://1ma.dev/ruby/2020/10/12/do-not-use-nil.html" rel="alternate" type="text/html" title="I don’t use nil" /><published>2020-10-12T00:00:00+00:00</published><updated>2020-10-12T00:00:00+00:00</updated><id>https://1ma.dev/ruby/2020/10/12/do-not-use-nil</id><content type="html" xml:base="https://1ma.dev/ruby/2020/10/12/do-not-use-nil.html">&lt;p&gt;I don’t use &lt;code class=&quot;highlighter-rouge&quot;&gt;nil&lt;/code&gt;, or at least I try not to.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;NoMethodError: undefined method &apos;some_method&apos; for nil:NilClass
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 id=&quot;migrations&quot;&gt;Migrations&lt;/h2&gt;

&lt;p&gt;When creating/updating a table I always consider adding a &lt;code class=&quot;highlighter-rouge&quot;&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 class=&quot;highlighter-rouge&quot;&gt;nil&lt;/code&gt;, because we took care of that at the database level. For instance, let’s consider a &lt;code class=&quot;highlighter-rouge&quot;&gt;Post&lt;/code&gt; model in a blogging system:&lt;/p&gt;

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

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

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

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

&lt;p&gt;In this particular example, sending &lt;code class=&quot;highlighter-rouge&quot;&gt;downcase&lt;/code&gt; to &lt;code class=&quot;highlighter-rouge&quot;&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 id=&quot;enums&quot;&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;/p&gt;

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

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

      &lt;span class=&quot;c1&quot;&gt;# better&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;integer&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:category&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;null: &lt;/span&gt;&lt;span class=&quot;kp&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;default: &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&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 class=&quot;highlighter-rouge&quot;&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 class=&quot;highlighter-rouge&quot;&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;/p&gt;

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

&lt;p&gt;In this example we are using the category named &lt;code class=&quot;highlighter-rouge&quot;&gt;general&lt;/code&gt; as the default one.&lt;/p&gt;

&lt;h2 id=&quot;nullobject&quot;&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=&quot;https://thoughtbot.com/blog/rails-refactoring-example-introduce-null-object&quot; target=&quot;_blank&quot;&gt;article from thoughtbot&lt;/a&gt; about this pattern.&lt;/p&gt;

&lt;h2 id=&quot;undefined-method-conclusion-for-nilnilclass&quot;&gt;undefined method ‘conclusion’ for nil:NilClass&lt;/h2&gt;

&lt;p&gt;To sum up, I don’t use &lt;code class=&quot;highlighter-rouge&quot;&gt;nil&lt;/code&gt; because I don’t want the program to potentially raise this &lt;code class=&quot;highlighter-rouge&quot;&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 class=&quot;highlighter-rouge&quot;&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=&quot;https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/&quot; target=&quot;_blank&quot;&gt;src&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;</content><author><name></name></author><category term="ruby" /><category term="ruby" /><summary type="html">NoMethodError: undefined method &apos;excerpt&apos; for nil:NilClass</summary></entry><entry><title type="html">When objects become super objects</title><link href="https://1ma.dev/ruby/2020/08/11/when-objects-become-super-objects.html" rel="alternate" type="text/html" title="When objects become super objects" /><published>2020-08-11T00:00:00+00:00</published><updated>2020-08-11T00:00:00+00:00</updated><id>https://1ma.dev/ruby/2020/08/11/when-objects-become-super-objects</id><content type="html" xml:base="https://1ma.dev/ruby/2020/08/11/when-objects-become-super-objects.html">&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;img src=&quot;/assets/pencil-3000.png&quot; alt=&quot;the-pencil-3000&quot; /&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 id=&quot;multiple-responsibility-classes-real-life-examples&quot;&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 id=&quot;1-models&quot;&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=&quot;https://www.rubyguides.com/2019/09/rails-patterns-presenter-service/&quot; target=&quot;_blank&quot;&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=&quot;https://api.rubyonrails.org/classes/ActiveRecord/Attributes/ClassMethods.html&quot; target=&quot;_blank&quot;&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=&quot;https://api.rubyonrails.org/classes/ActiveModel/Validator.html&quot; target=&quot;_blank&quot;&gt;validator class&lt;/a&gt; and be done with it. Peace out.&lt;/p&gt;

&lt;h4 id=&quot;2-form-objects&quot;&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;hr /&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;hr /&gt;

&lt;h4 id=&quot;3-page-objects&quot;&gt;3. Page objects&lt;/h4&gt;

&lt;p&gt;Testing usually requires &lt;a href=&quot;https://thoughtbot.com/blog/four-phase-test&quot; target=&quot;_blank&quot;&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=&quot;https://martinfowler.com/bliki/PageObject.html&quot; target=&quot;_blank&quot;&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=&quot;https://rubyapi.org/2.7/o/module&quot; target=&quot;_blank&quot;&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;hr /&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;hr /&gt;

&lt;h2 id=&quot;single-responsibility-principle&quot;&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=&quot;https://blog.cleancoder.com/uncle-bob/2014/05/08/SingleReponsibilityPrinciple.html&quot; target=&quot;_blank&quot;&gt;Better read it from here&lt;/a&gt;&lt;/p&gt;</content><author><name></name></author><category term="ruby" /><category term="ruby" /><category term="rails" /><category term="oop" /><summary type="html">Comments to my past self about a basic principle of OOP.</summary></entry><entry><title type="html">Use constants to specify the version of your gem</title><link href="https://1ma.dev/why-not/2020/05/04/use-constants-to-write-the-semver-of-your-gems.html" rel="alternate" type="text/html" title="Use constants to specify the version of your gem" /><published>2020-05-04T00:00:00+00:00</published><updated>2020-05-04T00:00:00+00:00</updated><id>https://1ma.dev/why-not/2020/05/04/use-constants-to-write-the-semver-of-your-gems</id><content type="html" xml:base="https://1ma.dev/why-not/2020/05/04/use-constants-to-write-the-semver-of-your-gems.html">&lt;p&gt;When developing gems or external libraries we frequently write the semantic versioning as a string.
&lt;!--more--&gt;&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MyGem&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;VERSION&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;1.4.0&apos;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;And when releasing a new version, git diff will show us this line was modified.&lt;/p&gt;

&lt;h2 id=&quot;why-not-use-a-constant-for-each-semver-element-instead&quot;&gt;Why not use a constant for each semver element instead?&lt;/h2&gt;

&lt;p&gt;Rewriting the previous version like this:&lt;/p&gt;

&lt;div class=&quot;language-ruby highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;MyGem&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;MAJOR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;MINOR&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;PATCH&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;
  &lt;span class=&quot;no&quot;&gt;VERSION&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;MAJOR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;MINOR&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;PATCH&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&apos;.&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;It will allow us to keep our git diff clean when releasing, and also make our version string less prone to typos.&lt;/p&gt;</content><author><name></name></author><category term="why-not" /><category term="why-not" /><category term="ruby" /><category term="gems" /><summary type="html">When developing gems or external libraries we frequently write the semantic versioning as a string.</summary></entry><entry><title type="html">Use error objects instead of strings</title><link href="https://1ma.dev/why-not/2020/05/03/raise-an-error.html" rel="alternate" type="text/html" title="Use error objects instead of strings" /><published>2020-05-03T00:00:00+00:00</published><updated>2020-05-03T00:00:00+00:00</updated><id>https://1ma.dev/why-not/2020/05/03/raise-an-error</id><content type="html" xml:base="https://1ma.dev/why-not/2020/05/03/raise-an-error.html">&lt;p&gt;Every once in a while we need to raise an error so that our program exits and displays the error in the standard error.
And most of the time (seen it several times while code reviewing) we tend to write a lovely: &lt;code class=&quot;highlighter-rouge&quot;&gt;raise &quot;some error explanation&quot;&lt;/code&gt;&lt;/p&gt;

&lt;h2 id=&quot;why-not-use-an-object-instead-of-a-string&quot;&gt;Why not use an object instead of a string?&lt;/h2&gt;

&lt;p&gt;When we raise errors using objects we’ll be able to easily identify those errors, everywhere (logs, console, when rescuing).
Take a look at this example:&lt;/p&gt;
&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[1] pry(main)&amp;gt; raise &quot;some error&quot;
RuntimeError: some error
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;It raised a RuntimeError which is an error that can occur not only here.&lt;/p&gt;

&lt;div class=&quot;highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;[2] pry(main)&amp;gt; class ThisErrorIKnow &amp;lt; StandardError; end;
[3] pry(main)&amp;gt; raise ThisErrorIKnow, &quot;some error explanation&quot;
ThisErrorIKnow: some error explanation
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Here it raised our custom error class which in my opinion is better because we’ll be able to rescue this error specifically later if needed.&lt;/p&gt;</content><author><name></name></author><category term="why-not" /><category term="why-not" /><category term="ruby" /><summary type="html">[...] we tend to write a lovely: raise &apos;some error explanation&apos;</summary></entry><entry><title type="html">Do you use form objects in your Rails apps?</title><link href="https://1ma.dev/gems/2020/04/10/introducing-yaaf.html" rel="alternate" type="text/html" title="Do you use form objects in your Rails apps?" /><published>2020-04-10T21:00:00+00:00</published><updated>2020-04-10T21:00:00+00:00</updated><id>https://1ma.dev/gems/2020/04/10/introducing-yaaf</id><content type="html" xml:base="https://1ma.dev/gems/2020/04/10/introducing-yaaf.html">&lt;p&gt;Introducing &lt;a href=&quot;https://github.com/rootstrap/yaaf&quot;&gt;yaaf&lt;/a&gt;, a gem to ease the usage of the form object pattern in rails apps.&lt;/p&gt;

&lt;h3 id=&quot;my-pain-points-before-yaaf&quot;&gt;My pain points (before yaaf)&lt;/h3&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;h3 id=&quot;instead-of-all-of-that-we-can&quot;&gt;Instead of all of that, we can…&lt;/h3&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 class=&quot;highlighter-rouge&quot;&gt;save&lt;/code&gt; and just &lt;code class=&quot;highlighter-rouge&quot;&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 class=&quot;highlighter-rouge&quot;&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 class=&quot;highlighter-rouge&quot;&gt;save&lt;/code&gt; and &lt;code class=&quot;highlighter-rouge&quot;&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 class=&quot;highlighter-rouge&quot;&gt;after_commit&lt;/code&gt; callback is the one you’re looking for. More info can be found in the &lt;a href=&quot;https://github.com/rootstrap/yaaf#callbacks&quot;&gt;readme&lt;/a&gt;.&lt;/p&gt;

&lt;h3 id=&quot;the-bob-ross-of-form-objects&quot;&gt;The Bob Ross of form objects&lt;/h3&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;h3 id=&quot;why-not-use-this&quot;&gt;Why not use this?&lt;/h3&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;h3 id=&quot;a-bit-of-history&quot;&gt;A bit of history&lt;/h3&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=&quot;https://twitter.com/santib6_&quot;&gt;@santib&lt;/a&gt; for shaping up this gem from its very beginning.&lt;/p&gt;

&lt;p&gt;&lt;a href=&quot;https://github.com/rootstrap/yaaf&quot;&gt;&lt;img src=&quot;/assets/yaaf-logo.png&quot; alt=&quot;yaaf&quot; /&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;a href=&quot;https://github.com/rootstrap/yaaf&quot;&gt;https://github.com/rootstrap/yaaf&lt;/a&gt;&lt;/p&gt;</content><author><name></name></author><category term="gems" /><category term="rails" /><category term="gems" /><summary type="html">Introducing yaaf, a gem to ease the usage of the form object pattern in rails apps.</summary></entry></feed>