<?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: Gonçalo Morais</title>
    <description>The latest articles on DEV Community by Gonçalo Morais (@gnclmorais).</description>
    <link>https://dev.to/gnclmorais</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%2F55917%2F1ce662f4-0af9-4ba9-8771-d16a0aca0eca.jpg</url>
      <title>DEV Community: Gonçalo Morais</title>
      <link>https://dev.to/gnclmorais</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gnclmorais"/>
    <language>en</language>
    <item>
      <title>Run copied CLI commands with a leading “$” sign safer</title>
      <dc:creator>Gonçalo Morais</dc:creator>
      <pubDate>Tue, 20 Jul 2021 10:00:00 +0000</pubDate>
      <link>https://dev.to/gnclmorais/run-copied-cli-commands-with-a-leading-sign-safer-e3k</link>
      <guid>https://dev.to/gnclmorais/run-copied-cli-commands-with-a-leading-sign-safer-e3k</guid>
      <description>&lt;p&gt;I recently came across &lt;a href="https://www.stefanjudis.com/blog/how-to-run-commands-with-a-leading-usd-sign" rel="noopener noreferrer"&gt;Stefan’s tip&lt;/a&gt; on how to run copied commands with a leading dollar sign and I thought it was a great idea! However, this heads-up caught my eye:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Use at your own risk. 🤓 Before implementing any functionality that makes running copied code easier, be aware that the internet’s a bad place. There’s always a chance that a command has malicious intent or &lt;a href="http://thejh.net/misc/website-terminal-copy-paste" rel="noopener noreferrer"&gt;even includes hidden commands&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This made me think: maybe I can build upon his example and add a small layer of protection, to prevent accidental errors when working a bit absentmindedly — we all do that from time to time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ask for confirmation before
&lt;/h2&gt;

&lt;p&gt;Follow his tutorial and, when you get to the part when you’re writing the script, instead of simply executing the commands it gets, ask the user for &lt;em&gt;explicit&lt;/em&gt; confirmation before proceeding. Use this as the content of your &lt;code&gt;$&lt;/code&gt; executable:&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;#!/bin/zsh&lt;/span&gt;
&lt;span class="nb"&gt;read&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="s2"&gt;"Are you sure you? [y/N]: "&lt;/span&gt; &lt;span class="nt"&gt;-n&lt;/span&gt; 1 &lt;span class="nt"&gt;-r&lt;/span&gt;
&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="c"&gt;# move to a new line&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt;&lt;span class="nv"&gt;$REPLY&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;~ ^[Yy]&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="o"&gt;]]&lt;/span&gt;
&lt;span class="k"&gt;then
    &lt;/span&gt;&lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I &lt;a href="https://stackoverflow.com/a/1885534/590525" rel="noopener noreferrer"&gt;used someone’s else suggestion&lt;/a&gt; in order to ask for confirmation, but this is a simple way of adding a small confirmation step. Anything that is not &lt;code&gt;y&lt;/code&gt; or &lt;code&gt;Y&lt;/code&gt; will skip the execution. This will hopefully help with the &lt;a href="http://thejh.net/misc/website-terminal-copy-paste" rel="noopener noreferrer"&gt;hidden commands issue&lt;/a&gt; Stefan made me aware of.&lt;/p&gt;

</description>
      <category>bash</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Deploy and run it automatically</title>
      <dc:creator>Gonçalo Morais</dc:creator>
      <pubDate>Sun, 18 Jul 2021 09:00:00 +0000</pubDate>
      <link>https://dev.to/gnclmorais/deploy-and-run-it-automatically-2bok</link>
      <guid>https://dev.to/gnclmorais/deploy-and-run-it-automatically-2bok</guid>
      <description>&lt;p&gt;Welcome back! Now that we have a script that checks a page and we added SMS notifications, let’s make sure we get this up and running — executing our script regularly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Heroku account
&lt;/h2&gt;

&lt;p&gt;This tutorial relies on Heroku for the simple fact that it allows us to be abstracted from most server-side hassle and we can focus on the project at hands. If you have the project we’ve been building tracked with Git, this will be smooth.&lt;/p&gt;

&lt;p&gt;Make sure you have a Heroku account and install their &lt;a href="https://devcenter.heroku.com/articles/heroku-cli#download-and-install" rel="noopener noreferrer"&gt;CLI&lt;/a&gt;. Then &lt;a href="https://devcenter.heroku.com/articles/heroku-cli#getting-started" rel="noopener noreferrer"&gt;log in&lt;/a&gt; through it so that you have access to your Heroku account through terminal commands.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deploy your app
&lt;/h2&gt;

&lt;p&gt;After making sure you’re on the project folder, run &lt;code&gt;heroku create&lt;/code&gt; to make a new empty application on Heroku. This doesn’t deploy your code yet.&lt;/p&gt;

&lt;p&gt;Before we send our code to Heroku, make sure you have the following lines on your &lt;code&gt;package.json&lt;/code&gt; — it will help us to run our script with the last amount of work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node index.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To send your code to be run by Heroku, execute the following: &lt;code&gt;git push heroku main&lt;/code&gt;. This will take a while and will output a lot of text, keeping you up to date with the remote state of the dyno Heroku is setting up for you. At the end, you’re greeted with a message with a link, something like &lt;code&gt;https://hidden-socks-12321.herokuapp.com/ deployed to Heroku&lt;/code&gt;. In our case, this is not important since we don’t have a page to look at — we’ll get an SMS notifications whenever we find what we want.&lt;/p&gt;

&lt;h2&gt;
  
  
  Schedule regular jobs
&lt;/h2&gt;

&lt;p&gt;Our script is on Heroku, so we’re just missing one last step: making sure it runs regularly. Head out to &lt;a href="https://dashboard.heroku.com/apps" rel="noopener noreferrer"&gt;your dashboard&lt;/a&gt; where you’ll see all your current applications. Find the one we just created and select it.&lt;/p&gt;

&lt;p&gt;On you app’s page, go to “Resources” then click “Find more add-ons”. You’ll be greeted with a long page full of possible addons, but we’re looking for one on the &lt;a href="https://elements.heroku.com/addons#dynos" rel="noopener noreferrer"&gt;Dynos&lt;/a&gt; section. Find &lt;a href="https://elements.heroku.com/addons/scheduler" rel="noopener noreferrer"&gt;Heroku Scheduler&lt;/a&gt; and click on it. You’ll see a button near the top right corner of your screen that says “Install Heroku Scheduler”, press it and, on the next screen, type the name of your app and click “Submit Order Form” — don’t worry, this addon is free.&lt;/p&gt;

&lt;p&gt;The addon is now enabled and we can see the options we have available. You should be back on your app’s page, on the Resources tab, where you should see your newly installed addons. Click on it to access its configuration page.&lt;/p&gt;

&lt;p&gt;On this new page, click “Create job” to show a right-side panel where you can pick if you want to run your script every:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;10 minutes&lt;/li&gt;
&lt;li&gt;Every hour at 00, 10, 20, 30, 40 or 50 minutes in the hour&lt;/li&gt;
&lt;li&gt;Every day at a certain time (in 30 minutes increments)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Pick the frequence you want, pass the command we have to run the script (&lt;code&gt;npm start&lt;/code&gt;) and click “Save Job” at the bottom.&lt;/p&gt;

&lt;h2&gt;
  
  
  One last step, a missing buildpack
&lt;/h2&gt;

&lt;p&gt;Heroku has this concept of &lt;em&gt;buildpacks&lt;/em&gt; that they define as “(…) scripts that are run when your app is deployed. They are used to install dependencies for your app and configure your environment.”&lt;/p&gt;

&lt;p&gt;For this particular project, there is one we must add to have access to Puppeteer, created by &lt;a href="https://github.com/jontewks" rel="noopener noreferrer"&gt;Jon Tewksbury&lt;/a&gt;. It will help Heroku installing all the necessary dependencies (like Chrome) so that our script runs without problems.&lt;/p&gt;

&lt;p&gt;On your app’s dashboard page, go to the Settings tab and scroll down to the section Buildpacks. Click “Add buildpack” and paste &lt;code&gt;https://github.com/jontewks/puppeteer-heroku-buildpack&lt;/code&gt; in the text input of the modal you get. Click “Save changes” and you’re ready to go.&lt;/p&gt;

&lt;p&gt;Buildpacks are used the next time our apps are deployed, so just to be sure everything is in place, let’s trigger a redeploy of our app by pushing an empty commit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git commit &lt;span class="nt"&gt;--allow-empty&lt;/span&gt; &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Trigger deploy after buildpack"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git push heroku main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;That’s it! You have now created, deployed and scheduled a web scraper that notifies you with an SMS whenever it finds what it is looking for! There are numerous other ways to do what we’ve accomplished, however, I’ve found this the most &lt;a href="https://en.wikipedia.org/wiki/Pareto_principle" rel="noopener noreferrer"&gt;Pareto&lt;/a&gt;-like way of spinning something up whenever I have the need to automate checking up on a website. I’ve optimised for simplicity and speed of MVP on these blogs, not for flexibility or power.&lt;/p&gt;

&lt;p&gt;As a last tip, if you are looking for logs to see what is happening with your app from time to time or you’re trying to debug something with it, on your app’s dashboard you should see a “More” button on the top right corner. Click on it to expand the options available and you’ll see a “View logs” item. That’s where you can take a closer look at the logs Heroku collects from your running applications.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>puppeteer</category>
      <category>heroku</category>
    </item>
    <item>
      <title>Add SMS notifications</title>
      <dc:creator>Gonçalo Morais</dc:creator>
      <pubDate>Fri, 18 Jun 2021 17:00:00 +0000</pubDate>
      <link>https://dev.to/gnclmorais/build-and-deploy-a-web-scraper-in-javascript-with-puppeteer-part-2-84a</link>
      <guid>https://dev.to/gnclmorais/build-and-deploy-a-web-scraper-in-javascript-with-puppeteer-part-2-84a</guid>
      <description>&lt;p&gt;Welcome back! Now that we have a basic script that checks for the data on a page, let’s add an SMS notification that will inform us of the current headline we find.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create a Twilio account
&lt;/h2&gt;

&lt;p&gt;We’re going to use Twilio for this, they have a free API that we can quickly reach out to and send a few SMS through it. Head out to &lt;a href="https://twilio.com/try-twilio" rel="noopener noreferrer"&gt;https://twilio.com/try-twilio&lt;/a&gt; and create your account (if you already have one, read along).&lt;/p&gt;

&lt;p&gt;After creating your account and logging in, we want to create a new project (sometimes also called &lt;em&gt;account&lt;/em&gt; on their platform). These are containers for your applications (nothing to do with Docker, though).&lt;/p&gt;

&lt;p&gt;Look for the &lt;em&gt;Create New Account&lt;/em&gt; button or go straight to &lt;a href="https://www.twilio.com/console/projects/create" rel="noopener noreferrer"&gt;twilio.com/console/projects/create&lt;/a&gt;. Pick a name for your account and click &lt;em&gt;Verify&lt;/em&gt;:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj8l815v1ps76s738il8n.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj8l815v1ps76s738il8n.png" alt="New account/project on Twilio" width="800" height="268"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The next screen asks you to verify yourself. Yeah, it’s a bit annoying, but this also helps them to prevent some less benevolent agents from using their platform, so it’s cool.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fub5cszdcn4zpidefnprk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fub5cszdcn4zpidefnprk.png" alt="New account/project on Twilio" width="800" height="323"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Write your phone number down, go to the next page, type the verification code you got on your phone, submit and you’re almost there! The next screen asks us for some goals, in order to send us to the right place and the right features we intend to use. I’ve picked the following for this example:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc7y697wc78wmdt04v2x4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc7y697wc78wmdt04v2x4.png" alt="Answers to Twilio welcome screen" width="800" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After this, we are finally greeted with our account dashboard and… there is a lot of stuff. 😅 Luckily, we’re only here for one thing: a phone number to send SMS from. If you scroll down a bit, you should see a &lt;em&gt;Get a Twilio trial phone number&lt;/em&gt; header. Click the button below it, &lt;em&gt;Get a trial phone number&lt;/em&gt;. You should get a modal with a suggested phone number.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fot0dn8574zuy8moxmzr5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fot0dn8574zuy8moxmzr5.png" alt="Modal with a suggested phone number" width="800" height="275"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There is nothing special with the phone number we’re looking for, so grab the first one you get by clicking on &lt;em&gt;Choose this Number&lt;/em&gt;. Press &lt;em&gt;Done&lt;/em&gt; on the next modal and we have now a number! We’re very close to being able to send messages…&lt;/p&gt;

&lt;h3&gt;
  
  
  Small gotcha
&lt;/h3&gt;

&lt;p&gt;In order to prevent free accounts from being used to spam people, you’ll only be able tos end SMS to verified numbers. You can check the only one you got so far (the one you used to sign up) at &lt;a href="https://www.twilio.com/console/phone-numbers/verified" rel="noopener noreferrer"&gt;twilio.com/console/phone-numbers/verified&lt;/a&gt;. If there are other numbers you want to message through your script, you should add them here now.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integrate Twilio in our script
&lt;/h2&gt;

&lt;p&gt;We’re ready to use Twilio and send some SMS. To do that, we’ll need their npm package, so install it by running &lt;code&gt;npm install twilio&lt;/code&gt; (if you have npm 5 of above, this should save the package on your &lt;code&gt;package.json&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Start now by creating a separate file where we’ll put out notification code, &lt;code&gt;notify.js&lt;/code&gt;. This will be our base code to send notifications:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;twilio&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;twilio&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;sendSMS&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;toNumber&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fromNumber&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TWILLIO_PHONE_NUMBER&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;accountSid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TWILLIO_ACCOUNT_SID&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authToken&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;TWILLIO_AUTH_TOKEN&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;twilio&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;accountSid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;authToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
         &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;from&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fromNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
         &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;toNumber&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
       &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;sid&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;done&lt;/span&gt;&lt;span class="p"&gt;();&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;The code above is almost straight from &lt;a href="https://www.twilio.com/docs/sms/quickstart/node" rel="noopener noreferrer"&gt;their documentation&lt;/a&gt; (which is excellent, by the way!) and I’m always amazed about how little code you need to send SMS!&lt;/p&gt;

&lt;p&gt;You can also notice how we have three lines accessing &lt;code&gt;process.env.*&lt;/code&gt;. You do this in Node to access environment variables, i.e., values that you can set on the fly when you run the command. Think of it as function arguments, but for Node scripts.&lt;/p&gt;

&lt;p&gt;With our notification module ready, go back to your &lt;code&gt;index.js&lt;/code&gt; and we’ll import it to give it a spin:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; const puppeteer = require('puppeteer');
&lt;span class="gi"&gt;+const { sendSMS } = require('./notify');
+
+const toNumber = process.env.MY_PHONE_NUMBER;
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt; (async () =&amp;gt; {
   const browser = await puppeteer.launch({
&lt;span class="p"&gt;@@ -19,7 +22,7 @@&lt;/span&gt; const puppeteer = require('puppeteer');
   const header = await getText(firstArticle, 'h2');
   const summary = await getText(firstArticle, 'p:first-of-type');
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- console.log(`${header}\n${summary}`);
&lt;/span&gt;&lt;span class="gi"&gt;+ sendSMS(`${header}\n${summary}`, toNumber);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;   await browser.close();
 })();
&lt;span class="err"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The text we were passing to &lt;code&gt;console.log&lt;/code&gt; we’ll not send to our phones with the new &lt;code&gt;sendSMS&lt;/code&gt; method we created. Don’t forget to also get the number you are sending this message to (should be the same you used to register to Twilio) through an environment variable as well.&lt;/p&gt;

&lt;h2&gt;
  
  
  Run the code
&lt;/h2&gt;

&lt;p&gt;We have everything in place now, there are just a few consideration to have before heading to a terminal and running all of this.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;process.env.*&lt;/code&gt; variables we set in our code must be provided by us in some way — I say this becuase we can do it in several ways, but we’ll follow the simplest. When running our &lt;code&gt;index.js&lt;/code&gt; script, we’ll pass these environment variables inline. Here’s an example (make sure you use your own credentials that you get from &lt;a href="https://www.twilio.com/console" rel="noopener noreferrer"&gt;the console page&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="nv"&gt;TWILLIO_PHONE_NUMBER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"+19293949596"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;TWILLIO_ACCOUNT_SID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;ACDCadbfe2ce33c691a6dcfdce6e3617bb &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;TWILLIO_AUTH_TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;face0a31ee17c4a2c9b3c0vfefeffa1f &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="nv"&gt;MY_PHONE_NUMBER&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"+447663342007"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  node index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The backslashes allow us to split a long command into multiple lines, for readability’s sake. Instead of harcoding this sensitive data into our code, we extracted it to configurable variables. This will permit us to easily set up an integration pipeline in the future that will run this script automatically, and also to make this project reusable by other people with their own Twilio credentials.&lt;/p&gt;

&lt;p&gt;And that’s it for the time being! You now have a script that send you an SMS with dynamically fetched data.&lt;/p&gt;

&lt;p&gt;See you around soon for the third part of this article…&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>puppeteer</category>
      <category>twilio</category>
    </item>
    <item>
      <title>Write the scraping script</title>
      <dc:creator>Gonçalo Morais</dc:creator>
      <pubDate>Tue, 15 Jun 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/gnclmorais/build-and-deploy-a-web-scraper-in-javascript-with-puppeteer-part-1-1gid</link>
      <guid>https://dev.to/gnclmorais/build-and-deploy-a-web-scraper-in-javascript-with-puppeteer-part-1-1gid</guid>
      <description>&lt;p&gt;I’ve had a few situations in the past where I was waiting for something to get updated on a website and just kept refreshing the page every so often… But when you don’t know when that update is going to happen, this can get tedious and hey, we’re programmers, we can build something to do this for us!&lt;/p&gt;

&lt;p&gt;&lt;em&gt;“&lt;a href="https://www.npmjs.com/package/puppeteer" rel="noopener noreferrer"&gt;Puppeteer&lt;/a&gt; is a Node library which provides a high-level API to control Chrome”&lt;/em&gt; and it’s the one I usually use just because it makes building a simple web scraper super simple. Let’s dig in and build a Minimum Viable Product that, for the sake of this example, grabs the top news from The New York Times’ &lt;a href="https://www.nytimes.com/section/todayspaper" rel="noopener noreferrer"&gt;&lt;em&gt;Today’s Paper&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Project start
&lt;/h2&gt;

&lt;p&gt;Begin by creating a &lt;code&gt;package.json&lt;/code&gt; that will hold the project’s dependencies. You can use &lt;code&gt;npm init&lt;/code&gt; for this, but for simplicity’s sake, I’ll create a stripped-down version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// package.json&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;name&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;web-scraper-with-puppeteer&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;version&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;private&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we add our only dependency, Puppeteer. Run this on the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;puppeteer
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Your &lt;code&gt;package.json&lt;/code&gt; has changed a bit now, here’s the difference:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt; {
   "name": "web-scraper-with-puppeteer",
   "version": "1.0.0",
&lt;span class="gd"&gt;- "private": true
&lt;/span&gt;&lt;span class="gi"&gt;+ "private": true,
+ "dependencies": {
+   "puppeteer": "^9.1.1"
+ }
&lt;/span&gt; }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let’s start with our main script now. Open up a brand new &lt;code&gt;index.js&lt;/code&gt; and write the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// index.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;puppeteer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;puppeteer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;launch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;headless&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;newPage&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;page&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;goto&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://nytimes.com/section/todayspaper&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;browser&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&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;For now, this is a simple script that you can run right now with &lt;code&gt;node index.js&lt;/code&gt; in order to see if everything is going well so far. You should see a Chrome window opening up (because we specified &lt;code&gt;headless: false&lt;/code&gt;) and closing as soon as the page stops loading. So far so good! Let’s now grab from the DOM the first article on the page.&lt;/p&gt;

&lt;p&gt;Add the next lines to your script to grab the first article and just output its HTML, so we can see if we’re retrieving the right thing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;   await page.goto(
     'https://nytimes.com/section/todayspaper'
   );
&lt;span class="gi"&gt;+
+ const firstArticle = await page.$eval(
+   'article:first-of-type',
+   e =&amp;gt; e.outerHTML
+ );
+
+ console.log(firstArticle);
+
&lt;/span&gt;   await browser.close();
 })();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Run your script with &lt;code&gt;node index.js&lt;/code&gt; and you should see a lot of HTML inside an &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; tag on your console. We’re almost there!&lt;/p&gt;

&lt;p&gt;Now, we don’t want the full article, only its headline and summary. Looking closer at the HTML we get, we see an &lt;code&gt;h2&lt;/code&gt; and the first &lt;code&gt;p&lt;/code&gt; that look promising. Let’s refactor our code a bit to have &lt;code&gt;firstArticle&lt;/code&gt; as a variable we can use, create a function to be used for both the header and the summary, and pluck both of them to show on the console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;     'https://nytimes.com/section/todayspaper'
   );
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- const firstArticle = await page.$eval(
- 'article:first-of-type',
- e =&amp;gt; e.outerHTML
- );
&lt;/span&gt;&lt;span class="gi"&gt;+ const firstArticle = await page.$('article:first-of-type');
+
+ const getText = (parent, selector) =&amp;gt; {
+   return parent.$eval(selector, el =&amp;gt; el.innerText);
+ };
+
+ const header = await getText(firstArticle, 'h2');
+ const summary = await getText(firstArticle, 'p:first-of-type');
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;- console.log(firstArticle);
&lt;/span&gt;&lt;span class="gi"&gt;+ console.log(`${header}\n${summary}`);
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;   await browser.close();
 })();
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Go ahead, run that on the terminal and you show see two lines, the top on as the header and the bottom one as the summary of the article!&lt;/p&gt;

&lt;p&gt;To be honest, that’s it! 🎉 &lt;strong&gt;A web scraper doesn’t need to be fancy or complicated&lt;/strong&gt; , it really depends on what you are trying to fetch from a page. I had one running for a few days a while back (which I’ll write about on a following article) and it was basically doing thigs on another page, just checking if a specific string of text has changed already or not.&lt;/p&gt;

&lt;p&gt;Having said that, there is &lt;em&gt;so much more&lt;/em&gt; you can do with Puppeteer — the sky is the limit. Check &lt;a href="https://devdocs.io/puppeteer/" rel="noopener noreferrer"&gt;their documentation&lt;/a&gt; to see the available methods, &lt;a href="https://github.com/puppeteer/examples" rel="noopener noreferrer"&gt;official examples&lt;/a&gt; of wild things you can use it for, and you can even use it to &lt;a href="https://addyosmani.com/blog/puppeteer-recipes/" rel="noopener noreferrer"&gt;automate performance work&lt;/a&gt;!&lt;/p&gt;

&lt;p&gt;See you around soon for the second part of this article…&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>tutorial</category>
      <category>puppeteer</category>
    </item>
    <item>
      <title>Launching ‘Heroes of Computer Science’</title>
      <dc:creator>Gonçalo Morais</dc:creator>
      <pubDate>Wed, 12 May 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/gnclmorais/launching-heroes-of-computer-science-1m0g</link>
      <guid>https://dev.to/gnclmorais/launching-heroes-of-computer-science-1m0g</guid>
      <description>&lt;p&gt;A few months ago, after learning about the death of &lt;a href="https://en.wikipedia.org/wiki/Jean_E._Sammet" rel="noopener noreferrer"&gt;Jean E. Sammet&lt;/a&gt;, I realised that there is this huge amount of personalities that pushed the field of Computer Science and that, even though I went through a master’s degree in Computer Engineering, I still didn’t know who most of them were…&lt;/p&gt;

&lt;p&gt;This gave me an idea for a project. A short newsletter (with an RSS feed as well, for people with &lt;em&gt;email fatigue&lt;/em&gt;) that, every fortnight or so, would introduce you to someone that (in some way) propelled Computer Science forward, someone you should know a little about.&lt;/p&gt;

&lt;p&gt;This eventually led me to &lt;a href="//heroesofcomputer.science"&gt;&lt;strong&gt;Heroes of Computer Science&lt;/strong&gt;&lt;/a&gt; and I’ve sent three issues so far: &lt;a href="https://www.heroesofcomputer.science/issues/heroes-of-computer-science-issue-1-499264" rel="noopener noreferrer"&gt;Ada Lovelace&lt;/a&gt;, &lt;a href="https://www.heroesofcomputer.science/issues/heroes-of-computer-science-issue-2-567730" rel="noopener noreferrer"&gt;Grace Hopper&lt;/a&gt; and &lt;a href="https://www.heroesofcomputer.science/issues/heroes-of-computer-science-issue-3-578033" rel="noopener noreferrer"&gt;Alonzo Church&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I have many more people to go through, but so far, I’m really enjoying discovering about new personalities and re-learning about names I already knew about but not that well.&lt;/p&gt;

&lt;p&gt;If you have a suggestion for someone to be spotlighted on this project, let mw know through &lt;a href="https://airtable.com/shrzC1ttxMN9cdXFI" rel="noopener noreferrer"&gt;this simple form&lt;/a&gt;! I’d love to get more ideas for future issues. 🤩&lt;/p&gt;

</description>
      <category>showdev</category>
      <category>projects</category>
      <category>newsletters</category>
    </item>
    <item>
      <title>Running bash commands with git alias</title>
      <dc:creator>Gonçalo Morais</dc:creator>
      <pubDate>Sat, 08 May 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/gnclmorais/running-bash-commands-with-git-alias-34k0</link>
      <guid>https://dev.to/gnclmorais/running-bash-commands-with-git-alias-34k0</guid>
      <description>&lt;p&gt;This is a quick tip I learned recently and it allowed me to improve my &lt;code&gt;git&lt;/code&gt; workflow a bit more.&lt;/p&gt;

&lt;p&gt;Here’s a new part of my &lt;code&gt;.gitconfig&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;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;alias&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt;
    main &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;git checkout main &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git pull &lt;span class="nt"&gt;--no-tags&lt;/span&gt;
    &lt;span class="nb"&gt;sync&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;git main &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git switch -
    fuse &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;git &lt;span class="nb"&gt;sync&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; git rebase main

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

&lt;/div&gt;



&lt;p&gt;This week I added these three alias to my &lt;code&gt;.gitconfig&lt;/code&gt; but, if you notice, they are not “regular” alias. &lt;strong&gt;The &lt;code&gt;!&lt;/code&gt; at their beginning tells Git that these are not alias to Git commands, but rather bash commands.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This gives you a simple but powerful way to chain executions, so I created three related alias that I can call depending on my goal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If I finished working on a branch and I want to get back to &lt;code&gt;main&lt;/code&gt; and start with the most recent codebase, I’ll run &lt;strong&gt;&lt;code&gt;git main&lt;/code&gt;&lt;/strong&gt;. Notice the &lt;code&gt;--no-tags&lt;/code&gt;, this is motivated by working on a &lt;em&gt;large&lt;/em&gt; monorepo and not needing all the tags of the packages we keep updating;&lt;/li&gt;
&lt;li&gt;If I’m working on a branch and I want to quickly get any changes made to our &lt;code&gt;main&lt;/code&gt; branch but come back to the branch I’m in right now, I’ll call &lt;strong&gt;&lt;code&gt;git sync&lt;/code&gt;&lt;/strong&gt;. As a note, &lt;code&gt;git switch -&lt;/code&gt; gets you back to the branch you were before you moved to the current branch you are now;&lt;/li&gt;
&lt;li&gt;Finally, if I want to bring the current branch I’m at up to speed with the latest code we’ve shipped, I’ll use &lt;strong&gt;&lt;code&gt;git fuse&lt;/code&gt;&lt;/strong&gt;. It will do everything I described on the other commands so far &lt;em&gt;and&lt;/em&gt; rebase our &lt;code&gt;main&lt;/code&gt; branch onto the current branch I’m at.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Cover image by &lt;a href="https://unsplash.com/photos/842ofHC6MaI" rel="noopener noreferrer"&gt;@yancymin&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>git</category>
      <category>bash</category>
    </item>
    <item>
      <title>`this.something = ‘else’` vs `this.set(‘something’, ‘else’)` in Ember</title>
      <dc:creator>Gonçalo Morais</dc:creator>
      <pubDate>Sun, 31 Jan 2021 00:00:00 +0000</pubDate>
      <link>https://dev.to/gnclmorais/this-something-else-vs-this-set-something-else-in-ember-38g5</link>
      <guid>https://dev.to/gnclmorais/this-something-else-vs-this-set-something-else-in-ember-38g5</guid>
      <description>&lt;p&gt;I’ve been getting deeper into Ember.js in my new job, and I learn something new every day. Sometimes, things you’ve been using here and there are a bit mysterious. Eventually, either you or someone else notices them and raises insightful questions, like Michal asked on &lt;a href="https://discord.com/channels/480462759797063690/480523424121356298/803933292444909610" rel="noopener noreferrer"&gt;Ember’s Discord channel&lt;/a&gt;. The question was basically this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;What’s the difference between &lt;code&gt;this.something = 'else'&lt;/code&gt; and &lt;code&gt;this.set('something', 'else')&lt;/code&gt; in tests?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer is surprisingly simple (at the surface, we don’t need to get deep into Ember’s reactivity): &lt;code&gt;this.set&lt;/code&gt; is the way you have to trigger re-render in test. For example, if you set a value with &lt;code&gt;this.value = 'test'&lt;/code&gt; and render a component that will use that &lt;code&gt;this.value&lt;/code&gt;, changing the value of &lt;code&gt;this.value&lt;/code&gt; now &lt;strong&gt;will not&lt;/strong&gt; re-render the partial. If, instead, you set that value with &lt;code&gt;this.set('value', 'test')&lt;/code&gt;, whenever you change it again with &lt;code&gt;this.set('value', 'something else')&lt;/code&gt;, the template will automatically re-render.&lt;/p&gt;

&lt;p&gt;Small caveat (like everything in like): re-rendering will only occur if the value you are reassigning is different that the value previously there, &lt;a href="https://discord.com/channels/480462759797063690/480523424121356298/804027937397276690" rel="noopener noreferrer"&gt;as it seems&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>ember</category>
    </item>
    <item>
      <title>Advent of Code 2020 and Ruby</title>
      <dc:creator>Gonçalo Morais</dc:creator>
      <pubDate>Tue, 15 Dec 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/gnclmorais/advent-of-code-2020-and-ruby-5ck2</link>
      <guid>https://dev.to/gnclmorais/advent-of-code-2020-and-ruby-5ck2</guid>
      <description>&lt;p&gt;After a year filled with so much &lt;a href="https://www.merriam-webster.com/dictionary/sameness" rel="noopener noreferrer"&gt;&lt;em&gt;sameness&lt;/em&gt;&lt;/a&gt; and spending way too much time at home for our own sake (and others), I’m surprised that tackling the daily challenges of &lt;a href="https://adventofcode.com/2020/" rel="noopener noreferrer"&gt;Advent of Code&lt;/a&gt; have been keeping me happy and entertained.&lt;/p&gt;

&lt;p&gt;As a break from my daily work with JavaScript and to keep my skills sharp, I’ve picked Ruby for it. Its performance shouldn’t be a problem for this kind of brain teasers and its syntactic sugar is always welcomed.&lt;/p&gt;

&lt;p&gt;While trying to solve the second part of &lt;a href="https://adventofcode.com/2020/day/14" rel="noopener noreferrer"&gt;day 14&lt;/a&gt;, I found myself hogging my computer over and over again, trying to validate my algorithm. I was forced to fire up Activity Monitor and kill one of my &lt;code&gt;ruby&lt;/code&gt; processes over and over again, which would be consuming several gigabytes of RAM! What was happening??&lt;/p&gt;

&lt;p&gt;Even after finding a possible solution I was happy with, this kept happening — which meant I couldn’t get the answer to the puzzle. After stopping, reflecting a bit, and probing a few parts of the code, I had an idea — and it worked!&lt;/p&gt;

&lt;p&gt;The challenge is about reading an input file and, with the values you get, write certain numbers in memory so you can sum them all up in the end. In my case, I was using an array to emulate the program’s memory and its indexes as the memory addresses.&lt;/p&gt;

&lt;p&gt;This worked out well for a while… until I tried to write a value to the index &lt;code&gt;14694662143&lt;/code&gt;! 💥 &lt;strong&gt;This&lt;/strong&gt; was the culprit and, while I can’t find the maximum number of an array’s index in Ruby, I found a &lt;a href="https://stackoverflow.com/a/6865199" rel="noopener noreferrer"&gt;few places&lt;/a&gt; that seem to point out that I was &lt;em&gt;way&lt;/em&gt; above the realm of possibility.&lt;/p&gt;

&lt;p&gt;The fix was actually quite simple:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;instead of using an array (&lt;code&gt;[]&lt;/code&gt;) to keep the addresses and &lt;code&gt;arr.compact.reduce(:+)&lt;/code&gt; to sum all the numbers hold,&lt;/li&gt;
&lt;li&gt;I just changed it to a hash (&lt;code&gt;{}&lt;/code&gt;) which still allows me to keep the values and use &lt;code&gt;arr.values.reduce(:+)&lt;/code&gt; to sum them.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I guess Ruby tries to allocate an array as big as your index whenever you do something like this. If the index is &lt;em&gt;quite big&lt;/em&gt;, Ruby will obey you but create some headaches. A hash won’t, you’ll only allocate what you assign, nothing in between.&lt;/p&gt;

</description>
      <category>adventofcode</category>
      <category>ruby</category>
      <category>challenge</category>
    </item>
    <item>
      <title>DigitalOcean and Dokku to replace Heroku</title>
      <dc:creator>Gonçalo Morais</dc:creator>
      <pubDate>Wed, 21 Oct 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/gnclmorais/digitalocean-and-dokku-to-replace-heroku-6l1</link>
      <guid>https://dev.to/gnclmorais/digitalocean-and-dokku-to-replace-heroku-6l1</guid>
      <description>&lt;p&gt;I’ve had this on my to-do list for ages but I’ve only recently looked into it. &lt;a href="https://heroku.com" rel="noopener noreferrer"&gt;Heroku&lt;/a&gt; is a great tool to quickly deploy and set up projects but you start accumulating a hefty bill if you start moving your work out of their free plans. $7 a dyno (without counting any addons you might need, like databases) becomes unmanageable if you want to spin up multiple small projects with small traffic but without downtime (remember, Heroku only provides you a certain amount of &lt;a href="https://devcenter.heroku.com/articles/free-dyno-hours#dyno-sleeping" rel="noopener noreferrer"&gt;free hours&lt;/a&gt; per month).&lt;/p&gt;

&lt;p&gt;To go around this, I finally started playing around with &lt;a href="https://github.com/dokku/dokku#dokku" rel="noopener noreferrer"&gt;Dokku&lt;/a&gt; on &lt;a href="https://m.do.co/c/56af94308a04" rel="noopener noreferrer"&gt;DigitalOcean&lt;/a&gt;. There is even an app available &lt;a href="https://marketplace.digitalocean.com/apps/dokku" rel="noopener noreferrer"&gt;straight from their platform&lt;/a&gt;, so the support seems to be solid. The premise is simple: create a $5 droplet on DigitalOcean with Dokku installed on it (which you can do straight from &lt;a href="https://marketplace.digitalocean.com/apps/dokku" rel="noopener noreferrer"&gt;here&lt;/a&gt;) and you’ll be able to use it as your personal Heroku.&lt;/p&gt;

&lt;p&gt;The deployment process is quite similar. Here’s a sample output of what I get when I push a Ruby on Rails app to my own Dokku droplet (a few details removed/changed for privacy reasons):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git push dokku master

Enumerating objects: 5, done.
Counting objects: 100% (5/5), done.
Delta compression using up to 4 threads
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 309 bytes | 309.00 KiB/s, done.
Total 3 (delta 2), reused 0 (delta 0), pack-reused 0
-----&amp;gt; Cleaning up...
-----&amp;gt; Building dokku-test-app from herokuish...
-----&amp;gt; Adding BUILD_ENV to build environment...
-----&amp;gt; Warning: Multiple default buildpacks reported the ability to handle this app. The first buildpack in the list below will be used.
       Detected buildpacks: multi ruby nodejs
-----&amp;gt; Multipack app detected
=====&amp;gt; Downloading Buildpack: https://github.com/heroku/heroku-buildpack-ruby.git
=====&amp;gt; Detected Framework: Ruby
-----&amp;gt; Installing bundler 2.1.4
-----&amp;gt; Removing BUNDLED WITH version in the Gemfile.lock
-----&amp;gt; Compiling Ruby/Rails
-----&amp;gt; Using Ruby version: ruby-2.5.8
-----&amp;gt; Installing dependencies using bundler 2.1.4
       Running: BUNDLE_WITHOUT='development:test' BUNDLE_PATH=vendor/bundle BUNDLE_BIN=vendor/bundle/bin BUNDLE_DEPLOYMENT=1 bundle install -j4
       Using rake 12.3.2
       …
       Using webpacker 3.6.0
       Bundle complete! 29 Gemfile dependencies, 74 gems now installed.
       Gems in the groups development and test were not installed.
       Bundled gems are installed into `./vendor/bundle`
       Bundle completed (0.79s)
       Cleaning up the bundler cache.
-----&amp;gt; Installing node-v12.16.2-linux-x64
-----&amp;gt; Installing yarn-v1.22.4
-----&amp;gt; Detecting rake tasks
-----&amp;gt; Preparing app for Rails asset pipeline
       Running: rake assets:precompile
       yarn install v1.22.4
       [1/4] Resolving packages...
       [2/4] Fetching packages...
       [3/4] Linking dependencies...
       [4/4] Building fresh packages...
       Done in 87.94s.
       Webpacker is installed 🎉 🍰
       Using /tmp/build/config/webpacker.yml file for setting up webpack paths
       Compiling…
       Compiled all packs in /tmp/build/public/packs
       Asset precompilation completed (308.00s)
       Cleaning assets
       Running: rake assets:clean
-----&amp;gt; Detecting rails configuration

=====&amp;gt; Downloading Buildpack: https://github.com/heroku/heroku-buildpack-nodejs
=====&amp;gt; Detected Framework: Node.js

-----&amp;gt; Creating runtime environment

       NPM_CONFIG_LOGLEVEL=error
       USE_YARN_CACHE=true
       NODE_ENV=production
       NODE_MODULES_CACHE=true
       NODE_VERBOSE=false

-----&amp;gt; Installing binaries
       engines.node (package.json): unspecified
       engines.npm (package.json): unspecified (use default)
       engines.yarn (package.json): unspecified (use default)

       Resolving node version 12.x...
       Downloading and installing node 12.19.0...
       Using default npm version: 6.14.8
       Resolving yarn version 1.22.x...
       Downloading and installing yarn (1.22.10)
       Installed yarn 1.22.10
       ! node_modules checked into source control
       https://devcenter.heroku.com/articles/node-best-practices#only-git-the-important-bits

-----&amp;gt; Restoring cache
       - yarn cache

-----&amp;gt; Installing dependencies
       Installing node modules (yarn.lock)
       yarn install v1.22.10
       [1/4] Resolving packages...
       [2/4] Fetching packages...
       [3/4] Linking dependencies...
       [4/4] Building fresh packages...
       Done in 45.75s.

-----&amp;gt; Build

-----&amp;gt; Pruning devDependencies
       yarn install v1.22.10
       [1/4] Resolving packages...
       [2/4] Fetching packages...
       [3/4] Linking dependencies...
       [4/4] Building fresh packages...
       Done in 16.75s.

-----&amp;gt; Caching build
       - yarn cache

-----&amp;gt; Build succeeded!
       ! Unmet dependencies don't fail yarn install but may cause runtime issues
       https://github.com/npm/npm/issues/7494

       Using release configuration from last framework (Node.js).
-----&amp;gt; Discovering process types
       Procfile declares types -&amp;gt; release, web
-----&amp;gt; Releasing dokku-test-app...
-----&amp;gt; Deploying dokku-test-app...
 ! Release command declared: 'rake db:migrate 2&amp;gt;/dev/null || rake db:setup'
       D, [2020-10-24T18:04:32.244136 #13] DEBUG -- : (2.0ms) SELECT pg_try_advisory_lock(7827836239135902150)
       D, [2020-10-24T18:04:32.272390 #13] DEBUG -- : (2.9ms) SELECT "schema_migrations"."version" FROM "schema_migrations" ORDER BY "schema_migrations"."version" ASC
       D, [2020-10-24T18:04:32.281930 #13] DEBUG -- : ActiveRecord::InternalMetadata Load (0.8ms) SELECT "ar_internal_metadata".* FROM "ar_internal_metadata" WHERE "ar_internal_metadata"."key" = $1 LIMIT $2 [["key", "environment"], ["LIMIT", 1]]
       D, [2020-10-24T18:04:32.290196 #13] DEBUG -- : (0.3ms) BEGIN
       D, [2020-10-24T18:04:32.291934 #13] DEBUG -- : (0.3ms) COMMIT
       D, [2020-10-24T18:04:32.292759 #13] DEBUG -- : (0.4ms) SELECT pg_advisory_unlock(7827836239135902150)
-----&amp;gt; App Procfile file found
       DOKKU_SCALE declares scale -&amp;gt; release=0 web=1
=====&amp;gt; Processing deployment checks
       No CHECKS file found. Simple container checks will be performed.
       For more efficient zero downtime deployments, create a CHECKS file. See http://dokku.viewdocs.io/dokku/deployment/zero-downtime-deploys/ for examples
-----&amp;gt; Attempting pre-flight checks (web.1)
       Waiting for 10 seconds ...
       Default container check successful!
-----&amp;gt; Running post-deploy
-----&amp;gt; Configuring dokku-test-app.Dokku...(using built-in template)
-----&amp;gt; Creating http nginx.conf
       Reloading nginx
-----&amp;gt; Renaming containers
       Found previous container(s) (11c86d03fec9) named dokku-test-app.web.1
       Renaming container (11c86d03fec9) dokku-test-app.web.1 to dokku-test-app.web.1.1603562703
       Renaming container (154edf6916aa) inspiring_mayer to dokku-test-app.web.1
-----&amp;gt; Shutting down old containers in 60 seconds
       11c86d03fec92a9f08e4d3c1dfa33ffad428d621ed951195684ab5255a529f19
=====&amp;gt; Application deployed:
       http://dokku-test-app.Dokku

To 151.242.42.151:dokku-test-app
   92bf7df..a5b5166 master -&amp;gt; master

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

&lt;/div&gt;



&lt;p&gt;P.S.: If you are interested in trying out DigitalOcean, you can use &lt;a href="https://m.do.co/c/56af94308a04" rel="noopener noreferrer"&gt;this referral link&lt;/a&gt; and get $100 straight into your account to spin up some droplets and try it yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Troubleshooting
&lt;/h2&gt;

&lt;p&gt;Since everyone runs into a few hiccups when we’re trying something new, I’m going to keep this section as a living document of issues I’ve ran into while playing around with Dokku. Whenever I find a new issue that I’ve solved, I’ll come back to this blog post and update it.&lt;/p&gt;

&lt;h3&gt;
  
  
  virtual memory exhausted: Cannot allocate memory
&lt;/h3&gt;

&lt;p&gt;This one took me a while to find a solution for, but I believe that was just because I was not using the right terms. The &lt;a href="http://dokku.viewdocs.io/dokku~v0.11.0/getting-started/advanced-installation/#vms-with-less-than-1gb-of-memory" rel="noopener noreferrer"&gt;documentation even has a solution&lt;/a&gt; for it, it just took me a while to notice it. While installing the &lt;code&gt;sassc&lt;/code&gt; gem, my droplet was running out of memory. This seems to happen because DigitalOcean’s basic droplets only have 512MB available, which might not be enough in a few situations. The documentation provides some instructions to follow that solved my issue, by creating a bigger &lt;a href="https://en.wikipedia.org/wiki/Paging#Implementations" rel="noopener noreferrer"&gt;swap file&lt;/a&gt; (acting like extra RAM):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd /var
touch swap.img
chmod 600 swap.img

dd if=/dev/zero of=/var/swap.img bs=1024k count=1000
mkswap /var/swap.img
swapon /var/swap.img
free

echo "/var/swap.img none swap sw 0 0" &amp;gt;&amp;gt; /etc/fstab

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

&lt;/div&gt;



&lt;h2&gt;
  
  
  Further reading
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://medium.com/@herowebdev1/the-ultimate-digitalocean-dokku-deploy-guide-rails-other-environemnts-d28110f3dc86" rel="noopener noreferrer"&gt;The Ultimate Digitalocean Dokku Deploy Guide (Rails &amp;amp; other environemnts)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@dpaluy/the-ultimate-guide-to-dokku-and-ruby-on-rails-5-9ecad2dba4a3" rel="noopener noreferrer"&gt;The Ultimate Guide to Dokku and Ruby On Rails 5&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>heroku</category>
      <category>dokku</category>
      <category>deployment</category>
    </item>
    <item>
      <title>Vortexgear Cypher ⌨️</title>
      <dc:creator>Gonçalo Morais</dc:creator>
      <pubDate>Fri, 09 Oct 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/gnclmorais/vortexgear-cypher-3m3i</link>
      <guid>https://dev.to/gnclmorais/vortexgear-cypher-3m3i</guid>
      <description>&lt;p&gt;Recently, I had my introduction to mechanical keyboards. After starting with a second-hand Pok3r and trying out a Ducky One 2 SF, a Tab 75 and a Cypher, I think I found a good match.&lt;/p&gt;

&lt;h2&gt;
  
  
  Details
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="http://vortexgear.tw/vortex2_2.asp?kind=47&amp;amp;kind2=227&amp;amp;kind3=453&amp;amp;kind4=1058" rel="noopener noreferrer"&gt;Vortexgear Cypher&lt;/a&gt; (&lt;a href="https://drop.com/talk/947/physical-keyboard-layouts-explained-in-detail" rel="noopener noreferrer"&gt;65% layout&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Single spacebar (yes, because there’s a &lt;a href="http://vortexgear.tw/vortex2_2.asp?kind=47&amp;amp;kind2=227&amp;amp;kind3=454&amp;amp;kind4=1066" rel="noopener noreferrer"&gt;version with two spacebars&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://deskthority.net/wiki/Cherry_MX_Clear" rel="noopener noreferrer"&gt;Cherry MX Clear switches&lt;/a&gt; (medium stiff, tactile, non-clicky)&lt;/li&gt;
&lt;li&gt;ISO-UK layout (big return key)&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Factory layout
&lt;/h2&gt;

&lt;p&gt;&lt;a href="http://www.keyboard-layout-editor.com/##@@_c=%23373535&amp;amp;t=%23a8a8a8&amp;amp;a:7%3B&amp;amp;=Esc&amp;amp;_a:4%3B&amp;amp;=!%0A1&amp;amp;=%22%0A2&amp;amp;=%C2%A3%0A3&amp;amp;=%24%0A4%0A%0A%E2%82%AC&amp;amp;=%25%0A5&amp;amp;=%5E%0A6&amp;amp;=%2F&amp;amp;%0A7&amp;amp;=*%0A8&amp;amp;=(%0A9&amp;amp;=)%0A0&amp;amp;=%2F_%0A-&amp;amp;=+%0A%2F=&amp;amp;_w:2%3B&amp;amp;=%0A%0A%0ABackspace&amp;amp;_a:5%3B&amp;amp;=%0AHome%3B&amp;amp;@_a:4&amp;amp;w:1.5%3B&amp;amp;=%0ATab&amp;amp;=Q&amp;amp;=W&amp;amp;=E&amp;amp;=R&amp;amp;=T&amp;amp;=Y&amp;amp;=U&amp;amp;=I&amp;amp;=O&amp;amp;=P&amp;amp;=%7B%0A%5B&amp;amp;=%7D%0A%5D&amp;amp;_x:0.25&amp;amp;a:5&amp;amp;w:1.25&amp;amp;h:2&amp;amp;w2:1.5&amp;amp;h2:1&amp;amp;x2:-0.25%3B&amp;amp;=%0AEnter&amp;amp;=%0APgUp%3B&amp;amp;@_a:4&amp;amp;w:1.75%3B&amp;amp;=%0ACapsLock&amp;amp;=A&amp;amp;=S&amp;amp;=D&amp;amp;=F&amp;amp;=G&amp;amp;=H&amp;amp;=J&amp;amp;=K&amp;amp;=L&amp;amp;=%2F:%0A%2F%3B&amp;amp;=%2F@%0A'&amp;amp;=~%0A%23&amp;amp;_x:1.25&amp;amp;a:5%3B&amp;amp;=%0APgDn%3B&amp;amp;@_a:4&amp;amp;w:1.25%3B&amp;amp;=%0AShift&amp;amp;=%7C%0A%5C&amp;amp;=Z&amp;amp;=X&amp;amp;=C&amp;amp;=V&amp;amp;=B&amp;amp;=N&amp;amp;=M&amp;amp;=%3C%0A,&amp;amp;=%3E%0A.&amp;amp;=%3F%0A%2F%2F&amp;amp;_w:1.75%3B&amp;amp;=%0A%0A%0AShift&amp;amp;=%0A%0A%0A%E2%86%91&amp;amp;_a:5%3B&amp;amp;=%0AEnd%3B&amp;amp;@_a:4&amp;amp;w:1.25%3B&amp;amp;=%0ACtrl&amp;amp;_w:1.25%3B&amp;amp;=%0AWin&amp;amp;_w:1.25%3B&amp;amp;=%0AAlt&amp;amp;_a:7&amp;amp;w:6.25%3B&amp;amp;=&amp;amp;_a:4%3B&amp;amp;=%0A%0A%0AAltGr&amp;amp;=%0A%0A%0AFn&amp;amp;=%0A%0A%0APn&amp;amp;=%0A%0A%0A%E2%86%90&amp;amp;=%0A%0A%0A%E2%86%93&amp;amp;=%0A%0A%0A%E2%86%92" rel="noopener noreferrer"&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%2Fuploads%2Farticles%2Fqntytxibw3od5it4my92.png" alt="Default layout from factory"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Programming the keyboard
&lt;/h2&gt;

&lt;p&gt;A cool feature of most Vortex keyboards is the ability to program and override almost any key. This especially important on smaller keyboards, like their &lt;a href="https://mechboards.co.uk/shop/keyboards/vortex-core-40-keyboard/" rel="noopener noreferrer"&gt;Core 40%&lt;/a&gt;, but it’s still quite useful in order to tune a keyboard just for your use case.&lt;/p&gt;

&lt;p&gt;I’ve used their &lt;a href="https://mechboards.co.uk/shop/keyboards/vortex-pok3r-60-keyboard" rel="noopener noreferrer"&gt;Pok3r&lt;/a&gt; and it was almost perfect, but the fact it had no dedicated arrow keys (they were only reachable by pressing &lt;code&gt;Fn&lt;/code&gt; plus other keys) was a deal breaker to me. The Cypher model corrects that and, with a few changes, ticks all the boxes for me. Here’s my current layout (lighter grey highlighting the keys I’ve changed):&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.keyboard-layout-editor.com/##@@_c=%23373535&amp;amp;t=%23a8a8a8&amp;amp;a:7%3B&amp;amp;=Esc&amp;amp;_a:4%3B&amp;amp;=!%0A1&amp;amp;=%22%0A2&amp;amp;=%C2%A3%0A3&amp;amp;=%24%0A4%0A%0A%E2%82%AC&amp;amp;=%25%0A5&amp;amp;=%5E%0A6&amp;amp;=%2F&amp;amp;%0A7&amp;amp;=*%0A8&amp;amp;=(%0A9&amp;amp;=)%0A0&amp;amp;=%2F_%0A-&amp;amp;=+%0A%2F=&amp;amp;_c=%2360605b&amp;amp;a:0&amp;amp;w:2%3B&amp;amp;=%0A%0A%0ABackspace%0A%0ADel&amp;amp;_c=%23373535&amp;amp;a:5%3B&amp;amp;=%0AHome%3B&amp;amp;@_a:4&amp;amp;w:1.5%3B&amp;amp;=%0ATab&amp;amp;=Q&amp;amp;=W&amp;amp;=E&amp;amp;=R&amp;amp;=T&amp;amp;=Y&amp;amp;=U&amp;amp;=I&amp;amp;=O&amp;amp;=P&amp;amp;=%7B%0A%5B&amp;amp;=%7D%0A%5D&amp;amp;_x:0.25&amp;amp;a:5&amp;amp;w:1.25&amp;amp;h:2&amp;amp;w2:1.5&amp;amp;h2:1&amp;amp;x2:-0.25%3B&amp;amp;=%0AEnter&amp;amp;=%0APgUp%3B&amp;amp;@_c=%2360605b&amp;amp;a:4&amp;amp;w:1.75%3B&amp;amp;=%0ACtrlx2%20(iTerm)&amp;amp;_c=%23373535%3B&amp;amp;=A&amp;amp;=S&amp;amp;=D&amp;amp;=F&amp;amp;=G&amp;amp;=H&amp;amp;=J&amp;amp;=K&amp;amp;=L&amp;amp;=%2F:%0A%2F%3B&amp;amp;=%2F@%0A'&amp;amp;=~%0A%23&amp;amp;_x:1.25&amp;amp;a:5%3B&amp;amp;=%0APgDn%3B&amp;amp;@_a:4&amp;amp;w:1.25%3B&amp;amp;=%0AShift&amp;amp;=%7C%0A%5C&amp;amp;=Z&amp;amp;=X&amp;amp;=C&amp;amp;=V&amp;amp;=B&amp;amp;=N&amp;amp;=M&amp;amp;=%3C%0A,&amp;amp;=%3E%0A.&amp;amp;=%3F%0A%2F%2F&amp;amp;_w:1.75%3B&amp;amp;=%0A%0A%0AShift&amp;amp;=%0A%0A%0A%E2%86%91&amp;amp;_a:5%3B&amp;amp;=%0AEnd%3B&amp;amp;@_a:4&amp;amp;w:1.25%3B&amp;amp;=%0ACtrl&amp;amp;_c=%2360605b&amp;amp;w:1.25%3B&amp;amp;=%0A%E2%8C%A5&amp;amp;_w:1.25%3B&amp;amp;=%0A%E2%8C%98&amp;amp;_c=%23373535&amp;amp;a:7&amp;amp;w:6.25%3B&amp;amp;=&amp;amp;_c=%2360605b&amp;amp;a:4%3B&amp;amp;=%0A%0A%0A%60&amp;amp;_c=%23373535%3B&amp;amp;=%0A%0A%0AFn&amp;amp;=%0A%0A%0APn&amp;amp;=%0A%0A%0A%E2%86%90&amp;amp;=%0A%0A%0A%E2%86%93&amp;amp;=%0A%0A%0A%E2%86%92" rel="noopener noreferrer"&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%2Fuploads%2Farticles%2F8riahlahqm7ke5u1e7lp.png" alt="Customised layout"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Windows and Alt key functions were basically swapped because I use macOS everywhere at the moment;&lt;/li&gt;
&lt;li&gt;AltGr is pretty much useless on macOS, so I mapped it to something I use way more while writing JavaScript, a backtick (&lt;code&gt;) — otherwise accessible with&lt;/code&gt;Fn+Esc`;&lt;/li&gt;
&lt;li&gt;The Delete function is in a weird place (&lt;code&gt;Fn+'&lt;/code&gt;), so I mapped it to &lt;code&gt;Fn+Backspace&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;Finally, the CapsLock is now two Control presses! This means that pressing this key will be the same as double-tapping the Control key. This sounds odd but iTerm2 has a hotkey feature that allows you to show/hide it either with a key (I’ve used &lt;code&gt;±&lt;/code&gt; on other keyboards, but Cypher doesn’t have this key) &lt;em&gt;or&lt;/em&gt; by double-tapping a modifier key (I’ve found that Control is the one less prone to false double taps).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I won’t include in this post how to customise the keyboard, you can read that directly from the source (by checking the &lt;a href="https://duckduckgo.com/?q=vortex+cypher+manual" rel="noopener noreferrer"&gt;Cypher’s&lt;/a&gt; and &lt;a href="https://duckduckgo.com/?q=vortex+pok3r+manual" rel="noopener noreferrer"&gt;Pok3r’s&lt;/a&gt; manuals). After a few tweaks, this is my final layout (with preprogrammed media keys Vortex keyboards already include):&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.keyboard-layout-editor.com/##@@_c=%23373535&amp;amp;t=%23a8a8a8&amp;amp;a:7%3B&amp;amp;=Esc&amp;amp;_a:4%3B&amp;amp;=!%0A1&amp;amp;=%22%0A2&amp;amp;=%C2%A3%0A3&amp;amp;=%24%0A4%0A%0A%E2%82%AC&amp;amp;=%25%0A5&amp;amp;=%5E%0A6&amp;amp;=%2F&amp;amp;%0A7&amp;amp;=*%0A8&amp;amp;=(%0A9&amp;amp;=)%0A0&amp;amp;=%2F_%0A-&amp;amp;=+%0A%2F=&amp;amp;_a:0&amp;amp;w:2%3B&amp;amp;=%0A%0A%0ABackspace%0A%0ADel&amp;amp;_a:5%3B&amp;amp;=%0AHome%3B&amp;amp;@_a:4&amp;amp;w:1.5%3B&amp;amp;=%0ATab&amp;amp;=Q%0A%0A%0A%0APrev&amp;amp;=W%0A%0A%0A%0APlay&amp;amp;=E%0A%0A%0A%0ANext&amp;amp;=R&amp;amp;=T&amp;amp;=Y&amp;amp;=U&amp;amp;=I&amp;amp;=O&amp;amp;=P&amp;amp;=%7B%0A%5B&amp;amp;=%7D%0A%5D&amp;amp;_x:0.25&amp;amp;a:5&amp;amp;w:1.25&amp;amp;h:2&amp;amp;w2:1.5&amp;amp;h2:1&amp;amp;x2:-0.25%3B&amp;amp;=%0AEnter&amp;amp;=%0APgUp%3B&amp;amp;@_a:4&amp;amp;w:1.75%3B&amp;amp;=%0ACtrl%20x%202&amp;amp;=A&amp;amp;=S%0A%0A%0A%0AVol-&amp;amp;=D%0A%0A%0A%0AVol+&amp;amp;=F%0A%0A%0A%0AMute&amp;amp;=G&amp;amp;=H&amp;amp;=J&amp;amp;=K&amp;amp;=L&amp;amp;=%2F:%0A%2F%3B&amp;amp;=%2F@%0A'&amp;amp;=~%0A%23&amp;amp;_x:1.25&amp;amp;a:5%3B&amp;amp;=%0APgDn%3B&amp;amp;@_a:4&amp;amp;w:1.25%3B&amp;amp;=%0AShift&amp;amp;=%7C%0A%5C&amp;amp;=Z&amp;amp;=X&amp;amp;=C&amp;amp;=V&amp;amp;=B&amp;amp;=N&amp;amp;=M&amp;amp;=%3C%0A,&amp;amp;=%3E%0A.&amp;amp;=%3F%0A%2F%2F&amp;amp;_w:1.75%3B&amp;amp;=%0A%0A%0AShift&amp;amp;=%0A%0A%0A%E2%86%91&amp;amp;_a:5%3B&amp;amp;=%0AEnd%3B&amp;amp;@_a:4&amp;amp;w:1.25%3B&amp;amp;=%0ACtrl&amp;amp;_w:1.25%3B&amp;amp;=%0A%E2%8C%A5&amp;amp;_w:1.25%3B&amp;amp;=%0A%E2%8C%98&amp;amp;_a:7&amp;amp;w:6.25%3B&amp;amp;=&amp;amp;_a:4%3B&amp;amp;=%0A%0A%0A%60&amp;amp;=%0A%0A%0AFn&amp;amp;=%0A%0A%0APn&amp;amp;=%0A%0A%0A%E2%86%90&amp;amp;=%0A%0A%0A%E2%86%93&amp;amp;=%0A%0A%0A%E2%86%92" rel="noopener noreferrer"&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%2Fuploads%2Farticles%2Fi2n1cfthsrmbg2wlptjl.png" alt="Final layout with media keys"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>keyboards</category>
    </item>
    <item>
      <title>Simple pluralise in JavaScript</title>
      <dc:creator>Gonçalo Morais</dc:creator>
      <pubDate>Mon, 31 Aug 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/gnclmorais/simple-pluralise-in-javascript-4ano</link>
      <guid>https://dev.to/gnclmorais/simple-pluralise-in-javascript-4ano</guid>
      <description>&lt;p&gt;While working with Vue.js on top of a Ruby on Rails stack, I found myselfreplicating on the front end a few of the helpful utilities we had access to inthe back end. One of them was &lt;strong&gt;pluralise&lt;/strong&gt; , either &lt;a href="https://apidock.com/rails/ActionView/Helpers/TextHelper/pluralize" rel="noopener noreferrer"&gt;with&lt;/a&gt; or &lt;a href="https://apidock.com/rails/String/pluralize" rel="noopener noreferrer"&gt;without&lt;/a&gt;number.&lt;/p&gt;

&lt;p&gt;After finding several components with similar &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Conditional_Operator" rel="noopener noreferrer"&gt;ternaries&lt;/a&gt; trying to figureout if the singular or plural version of a word should be used, I saw a goodopportunity to extract that logic to its own piece and I ended up with this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pluralize&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;singular&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;plural&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;count&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="nx"&gt;singular&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;plural&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;singular&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;s`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="c1"&gt;// Uses:&lt;/span&gt;
&lt;span class="nf"&gt;pluralize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mouse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 1 mouse&lt;/span&gt;
&lt;span class="nf"&gt;pluralize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;house&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// 2 houses&lt;/span&gt;
&lt;span class="nf"&gt;pluralize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;house&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;// houses&lt;/span&gt;
&lt;span class="nf"&gt;pluralize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mouse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;plural&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;// 10 mice&lt;/span&gt;
&lt;span class="nf"&gt;pluralize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mouse&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;plural&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;// mice&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;In a nutshell, this is how it works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You always need to pass the number to be considered and the singular form,which will give you &lt;code&gt;&amp;lt;number&amp;gt; &amp;lt;bigger than 1 ? (singular + 's') : singular&amp;gt;&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;If the plural is irregular (i.e. it’s not just the &lt;code&gt;singular&lt;/code&gt; plus an &lt;code&gt;s&lt;/code&gt;),you can provide it;&lt;/li&gt;
&lt;li&gt;If you just need the singular/plural without the number, you can say so.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Bonus points: if you’re a bit of a minimalist and enjoy saying some keystrokes,you could set up something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// plural with number&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;plural&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;pluralize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;plural&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="c1"&gt;// plural without number&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;plural&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;pluralize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;n&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;s&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;plural&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

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

&lt;/div&gt;



</description>
      <category>javascript</category>
    </item>
    <item>
      <title>BEM and Utility Classes for a scalable CSS architecture</title>
      <dc:creator>Gonçalo Morais</dc:creator>
      <pubDate>Wed, 20 May 2020 00:00:00 +0000</pubDate>
      <link>https://dev.to/gnclmorais/bem-and-utility-classes-for-a-scalable-css-architecture-1f6o</link>
      <guid>https://dev.to/gnclmorais/bem-and-utility-classes-for-a-scalable-css-architecture-1f6o</guid>
      <description>&lt;p&gt;I’ve been trying to define my way of writing and thinking about CSS for a while now, and I think I’ve cracked it. However, I’ve found that someone else wrote it down for me!&lt;/p&gt;

&lt;p&gt;I’ve came across &lt;a href="https://css-tricks.com/building-a-scalable-css-architecture-with-bem-and-utility-classes" rel="noopener noreferrer"&gt;this article&lt;/a&gt; by &lt;a href="https://twitter.com/guerriero_se" rel="noopener noreferrer"&gt;Sebastiano Guerriero&lt;/a&gt; titled “Building a Scalable CSS Architecture With BEM and Utility Classes” that describes what I believe my current approach is. It has been evolving throughout the years but I think I finally landed on solid ground.&lt;/p&gt;

&lt;p&gt;This approach works especially well for a &lt;em&gt;component-less&lt;/em&gt; application (i.e. no React/Vue/etc. tool that allows you to &lt;em&gt;trully&lt;/em&gt; modularise components). There is global CSS everywhere but you have to put practices into place that will help you &lt;strong&gt;reduce side effects&lt;/strong&gt; and write &lt;strong&gt;refactoring-friendly&lt;/strong&gt; CSS.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use BEM to build logical components&lt;/strong&gt;
Use this mostly to describe the inner parts of your components (cards, navbars, footers, forms, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rely on utility classes for non-crucial details&lt;/strong&gt;
This means animations, variations on fonts, margins and paddings, and other small visual attributes&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is a small summary of the &lt;a href="https://css-tricks.com/building-a-scalable-css-architecture-with-bem-and-utility-classes" rel="noopener noreferrer"&gt;whole article&lt;/a&gt; (please go read it, it is worth your time), but it crystallizes my view quite well. As someone that has been working in the same fast-moving startup for almost five years, I can’t tell you how many component variations we end up doing. We don’t have time to stop all development work and spend a few weeks creating a design system. We have to do this as we build features and, to be honest, that’s like trying to change parts of a car while you’re driving on the highway going somewhere.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqkw4wmmy0tex5skvc5mq.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqkw4wmmy0tex5skvc5mq.gif" alt="I don’t want to be doing this with my code…" width="400" height="190"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In a situation like this, you learn how to &lt;strong&gt;write CSS defensively&lt;/strong&gt; and to be kinder to your future self. You start seeing the problems you can encounter when you tie things together and leave no room for a fast, clean removal of things. Using BEM for the skeleton of components and finishing them off with utility classes has allowed us to quickly iterate on modules as we reuse and polish them. When I can’t rely on tools like React, Vue, and the like, this is what I go for.&lt;/p&gt;

</description>
      <category>css</category>
      <category>bem</category>
    </item>
  </channel>
</rss>
