<?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>Forem: Can Olcer</title>
    <description>The latest articles on Forem by Can Olcer (@canolcer).</description>
    <link>https://forem.com/canolcer</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%2F739886%2F58b2b4d0-29b1-42d6-8941-3e27512ff634.jpg</url>
      <title>Forem: Can Olcer</title>
      <link>https://forem.com/canolcer</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/canolcer"/>
    <language>en</language>
    <item>
      <title>How to deploy your Jekyll site on Scalingo</title>
      <dc:creator>Can Olcer</dc:creator>
      <pubDate>Fri, 04 Nov 2022 18:48:46 +0000</pubDate>
      <link>https://forem.com/canolcer/how-to-deploy-your-jekyll-site-on-scalingo-2amo</link>
      <guid>https://forem.com/canolcer/how-to-deploy-your-jekyll-site-on-scalingo-2amo</guid>
      <description>&lt;p&gt;&lt;a href="https://jekyllrb.com/"&gt;Jekyll&lt;/a&gt; is a static site generator based on Ruby. &lt;a href="https://scalingo.com/"&gt;Scalingo&lt;/a&gt; is a hosting provider (similar to Heroku, Platform-as-a-Service). One of the great things about Scalingo is that they are an European company with a data center in France.&lt;/p&gt;

&lt;p&gt;At &lt;a href="https://getravioli.de"&gt;Ravioli&lt;/a&gt;, we're moving all our apps to EU-based hosting providers to ensure the privacy of customers. This is necessary because the USA has very lax privacy laws, and it's easy for the US government to access customer data that is residing on data centers owned or operated by US legal entities. So, even if you host your app on AWS's Frankfurt server, you compromise your users' privacy.&lt;/p&gt;

&lt;p&gt;If you're still reading this after my privacy detour, thanks! Let's get to the juicy part.&lt;/p&gt;

&lt;p&gt;Scalingo doesn't offer a native way to serve static applications such as Jekyll or Hugo. Rather, &lt;a href="https://doc.scalingo.com/platform/app/static-files-hosting"&gt;they recommend&lt;/a&gt; that you run an Express server that serves the static assets.&lt;/p&gt;

&lt;p&gt;Sounds easy enough. Why do we need a wordy blog post for this?&lt;/p&gt;

&lt;p&gt;Because there are two gotchas which make deploying your Jekyll site not that straight-forward. The good news is that I have the solution right here for you.&lt;/p&gt;

&lt;p&gt;Like Heroku, Scalingo works with buildpacks. Buildpacks tell Scalingo which environment to install before deploying and running your app. We are basically running an Express app that just serves the static files generated by Jekyll. Therefore, we need to choose the &lt;code&gt;nodejs&lt;/code&gt; buildpack.&lt;/p&gt;

&lt;p&gt;When you do that, you'll quickly realize that your build fails. The reason is simple: The &lt;code&gt;nodejs&lt;/code&gt; build pack doesn't include Ruby, but before deploying we need Ruby to generate the static files for our website. The Jekyll command for that is &lt;code&gt;bundle exec jekyll build&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Of course, we could run the command before and check in the static build output (usually the &lt;code&gt;_site&lt;/code&gt; folder) to our version control. But this would be a major pain in the ass.&lt;/p&gt;

&lt;p&gt;We need a buildpack that somehow contains Node and Ruby. It's our lucky day! Scalingo offers &lt;a href="https://doc.scalingo.com/platform/deployment/buildpacks/multi"&gt;Multi Buildpacks&lt;/a&gt;. To combine multiple build packs, add a file called &lt;code&gt;.buildpacks&lt;/code&gt; to your root with this content:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;https://github.com/Scalingo/ruby-buildpack
https://github.com/Scalingo/nodejs-buildpack.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The order is important. The &lt;code&gt;nodejs&lt;/code&gt; buildpack needs to come last, because we're deploying a Node application.&lt;/p&gt;

&lt;p&gt;Our &lt;code&gt;package.json&lt;/code&gt; should look 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="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scripts&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;start&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;node server.js&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;build&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;bundle exec jekyll build&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;dependencies&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="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;express&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;^4.18.2&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;nodejs&lt;/code&gt; buildpack runs the &lt;code&gt;build&lt;/code&gt; task before deploying. That's where we build our Jekyll static files. And then it uses the &lt;code&gt;start&lt;/code&gt; task to run the Express server.&lt;/p&gt;

&lt;p&gt;Speaking of Express, our second gotcha comes to mind. In &lt;a href="https://doc.scalingo.com/platform/app/static-files-hosting"&gt;their documentation&lt;/a&gt;, Scalingo suggests this setup for &lt;code&gt;server.js&lt;/code&gt;:&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;var&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&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;express&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;directory&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="p"&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;STATIC_DIR&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dist&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;port&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;PORT&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Listening on&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;port&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;With Jekyll, this works ok for the index page, but not for any other page. That's because it expects the &lt;code&gt;.html&lt;/code&gt; extension. So, &lt;code&gt;/about&lt;/code&gt; won't work, but &lt;code&gt;/about.html&lt;/code&gt; will. But that's a bit too 1995 for me. Fortunately, a simple modification to &lt;code&gt;server.js&lt;/code&gt; fixes this problem:&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="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;express&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;static&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;directory&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;extensions&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;html&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it. Using an Express server and leveraging Scalingo's Multi Buildpacks, you should be able to deploy any static website on Scalingo.&lt;/p&gt;

&lt;p&gt;This static website that you're reading is generated with Hugo and ironically still runs on Netlify. I'll make sure to migrate it over to Scalingo one of these days.&lt;/p&gt;

</description>
      <category>jekyll</category>
      <category>staticwebapps</category>
      <category>tutorial</category>
      <category>ruby</category>
    </item>
    <item>
      <title>A better CSV import</title>
      <dc:creator>Can Olcer</dc:creator>
      <pubDate>Sat, 07 May 2022 10:48:41 +0000</pubDate>
      <link>https://forem.com/canolcer/a-better-csv-import-264k</link>
      <guid>https://forem.com/canolcer/a-better-csv-import-264k</guid>
      <description>&lt;p&gt;If you ever had to import data from a CSV file in a web app, there's a high chance that your experience wasn't great. You need to prepare a file with the required columns, export to .csv and make sure to get the delimiter right. (Ironically, even though CSV stands for &lt;em&gt;comma&lt;/em&gt; separated values, the delimiter can be a different character like a semicolon or tab). The fun doesn't stop there. If there are any errors in your data, such as wrong formatting or missing values, the app will tell you to fix it before continuing. Now, you need to go back to Excel or Google Sheets and find the erroneous cells. Rinse and repeat.&lt;/p&gt;

&lt;p&gt;When building the bulk location import feature for &lt;a href="https://mapzy.io"&gt;Mapzy&lt;/a&gt;, our open-source and self-hostable store finder, we wanted to provide a better import experience for our users. We wanted to spare them the pain of dealing with exporting files and choosing delimiters, and we wanted to make it simple for them to fix any import errors.&lt;/p&gt;

&lt;p&gt;In an innovation that can only be likened to the invention of the first iPhone, we came up with the following: In the import screen, we provide an embedded spreadsheet that already has the correct headers filled out. Instead of choosing a file from their computer, users just copy and paste their CSV data into this spreadsheet and press the big red button. If there are any errors, we simply highlight the relevant cells in the spreadsheet. This way, users can easily fix them right then and there without needing to fiddle with a separate file and go through the whole process again.&lt;/p&gt;

&lt;p&gt;Ladies and gentlemen, without further ado, I present to you - the Mapzy location importer:&lt;/p&gt;


  
  Your browser does not support the video tag.


&lt;p&gt;If there are errors, we handle them like this:&lt;/p&gt;


  
  Your browser does not support the video tag.


&lt;p&gt;For those who are interested in the technical background, more details follow.&lt;/p&gt;

&lt;p&gt;For the spreadsheet, we use the excellent &lt;a href="https://github.com/jspreadsheet/ce"&gt;Jspreadsheet&lt;/a&gt; library. If there are no errors, the import happens in two steps because importing a lot of locations requires a lot of calls to the Mapbox geocoding API (geocoding is converting an address to longitude and latitude), which is rate limited.&lt;/p&gt;

&lt;p&gt;In the first step, we only validate if the information is all there and correctly formatted. For example, if a location name is missing, we highlight that error in the spreadsheet. If everything looks good, we save the locations to the database using the awesome &lt;a href="https://github.com/zdennis/activerecord-import"&gt;Activerecord-Import&lt;/a&gt; library, but skip calling the geocoding API. Rather, we mark these locations as "to be geocoded".&lt;/p&gt;

&lt;p&gt;In the second step, we then start a background job (with Sidekiq and Redis) that goes through the database and checks all locations that are related to the given map and geocodes their addresses if necessary. If a geocoding attempt is not successful, we later ask the user to correct the address from their Mapzy dashboard.&lt;/p&gt;

&lt;p&gt;If you're interested in the code, have a look at &lt;a href="https://github.com/mapzy/mapzy/blob/main/app/controllers/dashboard/location_imports_controller.rb"&gt;location_imports_controller.rb&lt;/a&gt;, &lt;a href="https://github.com/mapzy/mapzy/blob/main/app/models/location_import.rb"&gt;location_import.rb&lt;/a&gt;, and &lt;a href="https://github.com/mapzy/mapzy/blob/main/app/workers/batch_geocode_worker.rb"&gt;batch_geocode_worker.rb&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;An added technical bonus of this approach is that we don't need to deal with mapping columns from a CSV file to our data models, which can be a whole can of worms in itself.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>rails</category>
      <category>webdev</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Fellow devs: Stop building on the blockchain</title>
      <dc:creator>Can Olcer</dc:creator>
      <pubDate>Mon, 07 Mar 2022 17:22:07 +0000</pubDate>
      <link>https://forem.com/canolcer/fellowdevs-stop-building-on-the-blockchain-m13</link>
      <guid>https://forem.com/canolcer/fellowdevs-stop-building-on-the-blockchain-m13</guid>
      <description>&lt;p&gt;This is an open letter to all my fellow software developers, indie hackers and entrepreneurs: Stop building things on blockchain technologies.&lt;/p&gt;

&lt;p&gt;This is a complex topic, and I will only touch on a few topics. If you remember one thing, let it be this: The growing popularity of cryptocurrencies makes the rich richer, promotes ultra-libertarian views and enables scams and ransomware. It does not lead to decentralization and empowerment of the little man.&lt;/p&gt;

&lt;p&gt;By building on the blockchain, you are not a revolutionary for a better, more equitable world. You are merely a pawn in this selfish game of chess of the rich.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why does it make the rich richer?
&lt;/h2&gt;

&lt;p&gt;Cryptos are increasingly used by venture capitalists to create returns quicker than ever before. Simply said, they invest in new crypto startups that then list their crypto on a “reputable” exchange like Coinbase (which is of course also owned by VCs). Then, they create demand for the crypto and sell their initial holdings extremely high. After a while, the gullible fools who thought they are going to the moon realize that they were left holding the bag.&lt;br&gt;
There is an &lt;a href="https://startupsandecon.substack.com/p/you-dont-own-web3-a-coinbase-curse"&gt;interesting analysis by Fais Kahn&lt;/a&gt; where he looks at the data and concludes exactly this.&lt;/p&gt;

&lt;p&gt;By building and promoting blockchain, you become part of this hype cycle. Even if you personally don’t pump and dump or have bad intentions, every bit of activity in this space ultimately benefits the Mr. Andreessen and his buddies.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why blockchain doesn’t lead to decentralization
&lt;/h2&gt;

&lt;p&gt;Decentralization is just a pipe dream sold to you by institutions trying to enrich themselves. Blockchain does not lead to decentralization. It's obvious if you look at major cryptos: a small number of people hold a large amount of the crypto wealth, and a few mining pools control the majority of the mining activity.&lt;/p&gt;

&lt;p&gt;Why is this happening? The answer is economies of scale. Nvidia lead engineer David Rosenthal makes this point in his 2014 blog post &lt;a href="https://blog.dshr.org/2014/10/economies-of-scale-in-peer-to-peer.html"&gt;Economies of Scale in Peer-to-Peer Networks&lt;/a&gt;. If you have some time, go have a read. But the main point is this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Since then I've become convinced that this problem is indeed fundamental. The simplistic version of the problem is this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The income to a participant in a P2P network of this kind should be linear in their contribution of resources to the network.&lt;/li&gt;
&lt;li&gt;The costs a participant incurs by contributing resources to the network will be less than linear in their resource contribution, because of the economies of scale.&lt;/li&gt;
&lt;li&gt;Thus the proportional profit margin a participant obtains will increase with increasing resource contribution.&lt;/li&gt;
&lt;li&gt;Thus the effects described in Brian Arthur's Increasing Returns and Path Dependence in the Economy will apply, and the network will be dominated by a few, perhaps just one, large participant.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;And this is the same exact principle that applies to blockchain, too. Not only to blockchain, by the way.  The internet is built on decentralized protocols like SMTP and HTTP. However, most people use Gmail for their email and most web services are hosted on AWS, Microsoft Azure or Google Cloud.&lt;/p&gt;

&lt;h2&gt;
  
  
  But don't we need all this experimentation to find a game-changing use case for blockchain?
&lt;/h2&gt;

&lt;p&gt;There will be no real use cases for blockchain. It’s true that it sometimes take a while until interesting use cases emerge for new technologies. But, this technology has been around as long as the iPhone, and look at the use cases that emerged for the iPhone. In fact, the iPhone (like the internet) had real use cases from the get go. Technologies may not be widespread or cost-effective at the beginning, but if it's a good technology, it already provides a real value add to a small group of people in its early days.&lt;/p&gt;

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

&lt;p&gt;All that blockchain is, is an intriguing intellectual exercise, that proved to be not viable in the real world. It does not solve the trust problem in a practical way.&lt;/p&gt;

&lt;p&gt;All projects and apps that build on blockchains, be it DeFi or NFT, keep promoting cryptocurrencies, making the initial creators richer and the people that "get in" later are left holding the bag.&lt;/p&gt;

&lt;p&gt;I'll say it again to make it stick: By building and promoting apps and projects based on the blockchain, you become a pawn in this selfish game of chess of the rich. I’m not denying that there might be a few blockchain projects that do real good with the money raised, but they are the absolute minority and do so by promoting an incredibly destructive technology. The end does not justify the means.&lt;/p&gt;

&lt;p&gt;My plea is: Don’t do it. Don’t be tempted by the devil to join this get-rich-quick scheme. Go back to building products and services that are not zero-sum games but add real value to the lives of people without fucking everything else up.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Recommended further reading:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://blog.dshr.org/2022/02/ee380-talk.html"&gt;David Rosenthal (EE380 Talk)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.dshr.org/2018/01/it-isnt-about-technology.html"&gt;David Rosenthal (It Isn't About The Technology)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.stephendiehl.com/blog.html"&gt;Stephen Diehl&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=YQ_xWvX1n9g"&gt;Dan Olson aka Folding Ideas (Video: Line Goes Up – The Problem With NFTs)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.usenix.org/publications/loginonline/web3-fraud"&gt;Nicholas Weaver (The Web3 Fraud)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://web3isgoinggreat.com"&gt;Web3 is going just great&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/life-itself/web3"&gt;Collection of web3 resources&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>blockchain</category>
    </item>
    <item>
      <title>Active Record Callbacks shouldn't modify other records</title>
      <dc:creator>Can Olcer</dc:creator>
      <pubDate>Sat, 18 Dec 2021 10:42:46 +0000</pubDate>
      <link>https://forem.com/canolcer/active-record-callbacks-shouldnt-modify-other-records-45e2</link>
      <guid>https://forem.com/canolcer/active-record-callbacks-shouldnt-modify-other-records-45e2</guid>
      <description>&lt;p&gt;In Ruby on Rails, After Record Callbacks allow you to hook into the lifecycle of Active Record objects. Commonly used callbacks are &lt;code&gt;after_create&lt;/code&gt; or &lt;code&gt;before_validation&lt;/code&gt;. In this post, I want to highlight a bad practice that is sometimes used in callbacks.&lt;/p&gt;

&lt;p&gt;Let's say we have a &lt;code&gt;User&lt;/code&gt; model that has an associated &lt;code&gt;Account&lt;/code&gt;. The idea is that every user also has an account, which we use to specifiy more details like billing information.&lt;/p&gt;

&lt;p&gt;Now, since every time we create a new &lt;code&gt;User&lt;/code&gt;, we also want to create a new &lt;code&gt;Account&lt;/code&gt;, we might be tempted to do something like this in our &lt;code&gt;User&lt;/code&gt; model:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;has_one&lt;/span&gt; &lt;span class="ss"&gt;:account&lt;/span&gt;
  &lt;span class="n"&gt;after_create&lt;/span&gt; &lt;span class="ss"&gt;:create_account&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_account&lt;/span&gt;
    &lt;span class="no"&gt;Account&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="ss"&gt;user: &lt;/span&gt;&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is almost never a good idea. Creating or updating one model should never touch another model.&lt;/p&gt;

&lt;p&gt;The main reason is that it tangles up concerns (remember: we like separation of concerns) and is not very explicit. A colleague working with you on this codebase might not expect that this side effect will happen. After all, in other places in your code where you create your user, you only do &lt;code&gt;User.create&lt;/code&gt; and it magically also creates an &lt;code&gt;Account&lt;/code&gt; for this user.&lt;/p&gt;

&lt;p&gt;Another reason is that, while most of the time you want to create an associated &lt;code&gt;Account&lt;/code&gt;, maybe there will be times where you don't.&lt;/p&gt;

&lt;p&gt;A further reason is testing. If you use factories to set up your test data, creating a &lt;code&gt;User&lt;/code&gt; factory instance will not automatically create an &lt;code&gt;Account&lt;/code&gt; factory instance, but it will create an &lt;code&gt;Account&lt;/code&gt; record in the test database. Things will get messy from there.&lt;/p&gt;

&lt;p&gt;Instead of using a callback, I recommend just creating the Account separately or wrapping these two commands into a method that lives in the &lt;code&gt;User&lt;/code&gt; model, like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;ApplicationRecord&lt;/span&gt;
  &lt;span class="n"&gt;has_one&lt;/span&gt; &lt;span class="ss"&gt;:account&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create_with_account&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;User&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="n"&gt;attributes&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="no"&gt;Account&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="ss"&gt;user: &lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>rails</category>
      <category>ruby</category>
      <category>webdev</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Setting up Rubocop correctly with Gitpod and VSCode Ruby</title>
      <dc:creator>Can Olcer</dc:creator>
      <pubDate>Fri, 29 Oct 2021 14:15:42 +0000</pubDate>
      <link>https://forem.com/canolcer/setting-up-rubocop-correctly-with-gitpod-and-vscode-ruby-28e2</link>
      <guid>https://forem.com/canolcer/setting-up-rubocop-correctly-with-gitpod-and-vscode-ruby-28e2</guid>
      <description>&lt;p&gt;I've recently come across &lt;a href="https://gitpod.io/"&gt;Gitpod&lt;/a&gt;. Gitpod provides cloud-based development environments. They're similar to &lt;a href="https://github.com/features/codespaces"&gt;GitHub Codespaces&lt;/a&gt;, but in my opinion offer a better experience. Setting up an environment is simpler and they are all about ephemerality. The idea is that each environment just lives for the duration of the task you're working on, and then you throw it away. Like Codespaces, Gitpod uses the Visual Studio Code editor, which you can access with your browser.&lt;/p&gt;

&lt;p&gt;I'm slowly adding Gitpod to all of my projects, because the development experience is much better than doing it on my local machine.&lt;/p&gt;

&lt;p&gt;For &lt;a href="https://github.com/shafy/fugu/"&gt;one of my Rails projects&lt;/a&gt;, I had some issues getting the Rubocop linter to work. Here's a short set up guide to so that others can avoid the problem I had.&lt;/p&gt;

&lt;p&gt;To configure your environment to work with Gitpod, you need to add a file called &lt;code&gt;.gitpod.yml&lt;/code&gt; to the root of your project. In my case it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;image:
  file: .gitpod.Dockerfile
ports:
  # Rails server
  - port: 3000
    onOpen: ignore
  # PostgreSQL server
  - port: 5432
    onOpen: ignore
tasks:
  - init: &amp;gt;
      bundle install &amp;amp;&amp;amp;
      rails db:setup
github:
  prebuilds:
    branches: false
    pullRequests: true
vscode:
  extensions:
    - rebornix.ruby
    - wingrunr21.vscode-ruby
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can see that it contains simple instructions that Gitpod uses to set up your cloud-based development environment. Importantly, it needs to know which Docker image it will build from. While you can specify a pre-built image, in this example, I reference another file in my project called &lt;code&gt;.gitpod.Dockerfile&lt;/code&gt; that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FROM gitpod/workspace-postgres
USER gitpod
# Install the Ruby version specified in '.ruby-version'
COPY --chown=gitpod:gitpod .ruby-version /tmp
RUN echo "rvm_gems_path=/home/gitpod/.rvm" &amp;gt; ~/.rvmrc
RUN bash -lc "rvm reinstall ruby-$(cat /tmp/.ruby-version) \
              &amp;amp;&amp;amp; rvm use ruby-$(cat /tmp/.ruby-version) --default \
              &amp;amp;&amp;amp; gem install rails \
                 rubocop rubocop-performance rubocop-rails rubocop-rspec"
RUN echo "rvm_gems_path=/workspace/.rvm" &amp;gt; ~/.rvmrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a default Gitpod Docker image that works well with Rails and Postgres. Gitpod first runs the commands in the Dockerfile, and then the commands in the &lt;code&gt;tasks:&lt;/code&gt; key in &lt;code&gt;.gitpod.yml&lt;/code&gt;. And here is the tricky part: If you only install Rubocop with the &lt;code&gt;bundle install&lt;/code&gt; command after the Docker image has been set up, &lt;a href="https://marketplace.visualstudio.com/items?itemName=rebornix.Ruby"&gt;VSCode's Ruby extension&lt;/a&gt; (which we use for linting) won't have access to the command. Therefore, as you can see in the above Dockerfile, you need to install &lt;code&gt;rubocop&lt;/code&gt; in the same path as you install Ruby.&lt;/p&gt;

&lt;p&gt;That's it! If you haven't tried Gitpod or Github Codespaces, I really recommend giving it a spin. For me, this is the next level of abstraction in programming that will increase developer productivity by a significant factor.&lt;/p&gt;

</description>
      <category>vscode</category>
      <category>devops</category>
      <category>rails</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Jekyll and Tailwind: How to speed up build time</title>
      <dc:creator>Can Olcer</dc:creator>
      <pubDate>Fri, 29 Oct 2021 14:13:12 +0000</pubDate>
      <link>https://forem.com/canolcer/jekyll-and-tailwind-how-to-speed-up-build-time-1gk8</link>
      <guid>https://forem.com/canolcer/jekyll-and-tailwind-how-to-speed-up-build-time-1gk8</guid>
      <description>&lt;p&gt;I've been trying out Jekyll for a &lt;a href="https://github.com/mapzy/mapzy"&gt;new side project's&lt;/a&gt; static website and of course added my favorite CSS framework, Tailwind, into the mix. However, after adding Tailwind with PostCSS I began to see very slow build times. Generating the site went from less than a second to more than 30 seconds. Waiting this long to see every change you do makes working with Jekyll impossible.&lt;/p&gt;

&lt;p&gt;The problem arises because Jekyll regenerates all of the SCSS for every change, even if you use the &lt;code&gt;--incremental&lt;/code&gt; flag and don't touch your SCSS. The Tailwind files include a lot of CSS classes, and therefore this takes forever (at least on my 2016 MacBook).&lt;/p&gt;

&lt;p&gt;There are different ways to avoid this and build your own fancy asset pipeline, but I wanted to stick to a simple asset pipeline with PostCSS.&lt;/p&gt;

&lt;p&gt;The idea is to take advantage of some neat config options in Jekyll and of the fact that you usually don't change your Tailwind CSS. We'll make Jekyll generate the site with Tailwind once in the beginning of our project, and after that tell Jekyll to not bother with the Tailwind CSS files.&lt;/p&gt;

&lt;p&gt;First, add Tailwind with PostCSS to your project (you can follow &lt;a href="https://mdoliwa.com/articles/how-to-setup-jekyll-with-tailwind-css"&gt;this&lt;/a&gt; or &lt;a href="https://stevenwestmoreland.com/2021/01/using-tailwind-css-with-jekyll.html"&gt;this tutorial&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;Second, in your &lt;code&gt;_config.yml&lt;/code&gt; file, add the following to your &lt;code&gt;exclude&lt;/code&gt; and &lt;code&gt;keep_files&lt;/code&gt; settings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;exclude:
  # other stuff you're excluding
  - assets/css/tailwind.scss
keep_files:
  # other stuff you're keeping
  - assets/css/tailwind.css
  - assets/css/tailwind.css.map
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're telling Jekyll to ignore &lt;code&gt;tailwind.scss&lt;/code&gt; and to not delete &lt;code&gt;tailwind.css&lt;/code&gt; and &lt;code&gt;tailwind.css.map&lt;/code&gt; when building the site. Why not delete the files if we ignored them in the first place? You'll see. Make sure that you only &lt;code&gt;@import&lt;/code&gt; the Tailwind stuff in &lt;code&gt;tailwind.scss&lt;/code&gt; and keep the rest of your CSS in other files. My &lt;code&gt;tailwind.scss&lt;/code&gt; looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;---
---
@import "tailwindcss/base";
@import "tailwindcss/components";
@import "tailwindcss/utilities";
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Third, we add a second config file called &lt;code&gt;_config_tailwind.yml&lt;/code&gt; that looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;include:
  - assets/css/tailwind.scss
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's it.&lt;/p&gt;

&lt;p&gt;The magic happens here: Whenever you want Jekyll to build Tailwind, you need to tell it to include both config files. You definitely need to do this once at the start of the project, and then everytime you change something in Tailwind. To build with both configs, run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jekyll build --config _config.yml,_config_tailwind.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After that, you can just build or serve Jekyll normally with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;jekyll serve --incremental
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;keep_files&lt;/code&gt; option is necessary, because otherwise Jekyll will delete our &lt;code&gt;taiwind.scss&lt;/code&gt; file from the generated site.&lt;/p&gt;

&lt;p&gt;Bonus: To make your live easier, add the initial build command to your &lt;code&gt;package.json&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"scripts": {
  "build-tailwind": "jekyll build --config _config.yml,_config_tailwind.yml"
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you can just go &lt;code&gt;yarn run build-tailwind&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A word of caution: Once you add PostCSS to handle your asset pipeline, &lt;a href="https://jekyllrb.com/docs/assets/#sassscss"&gt;Jekyll's standard way of importing SASS partials&lt;/a&gt; from the &lt;code&gt;_sass/&lt;/code&gt; folder stops working. Instead, you need to import with the full path, like so:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# assets/css/main.scss
@import '/_sass/header.scss'
# this default Jekyll way will fail
@import 'header'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>tailwindcss</category>
    </item>
    <item>
      <title>Rails: Prevent users from logging out after each deployment</title>
      <dc:creator>Can Olcer</dc:creator>
      <pubDate>Fri, 29 Oct 2021 14:08:33 +0000</pubDate>
      <link>https://forem.com/canolcer/rails-prevent-users-from-logging-out-after-each-deployment-3ag9</link>
      <guid>https://forem.com/canolcer/rails-prevent-users-from-logging-out-after-each-deployment-3ag9</guid>
      <description>&lt;p&gt;Here's a quick one, and it may be obvious to some of you but I didn't know about it. I noticed that my Rails app (&lt;a href="https://fugu.lol"&gt;Fugu&lt;/a&gt;) kept logging out all users after every deployment.&lt;/p&gt;

&lt;p&gt;First, I thought it's an issue with Devise, but it turns out that it's related to a variabled called &lt;code&gt;secret_key_base&lt;/code&gt; that Rails uses to sign and encrypt cookies (among other things).&lt;/p&gt;

&lt;p&gt;For production, there are multiple places to define &lt;code&gt;secret_key_base&lt;/code&gt;. A glance at the &lt;a href="https://github.com/rails/rails/blob/9f980664fc1bc1fb9845e17d2c5a9ab710156303/railties/lib/rails/application.rb#L411-L419"&gt;Rails soure code&lt;/a&gt; shows that Rails looks for it in &lt;code&gt;ENV["SECRET_KEY_BASE"]&lt;/code&gt;, &lt;code&gt;credentials.secret_key_base&lt;/code&gt;, or &lt;code&gt;secrets.secret_key_base&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In my case, I hadn't set up any credentials or secrets, nor was I providing an environment variable.&lt;/p&gt;

&lt;p&gt;Digital Ocean (and, &lt;a href="https://blog.heroku.com/container_ready_rails_5#secret_key_base"&gt;as it looks, Heroku&lt;/a&gt;) automatically sets the &lt;code&gt;SECRET_KEY_BASE&lt;/code&gt; environment variable for you, and it changes with every deployment. And this was the problem. After each deployment, my Rails app couldn't decrypt the existing session cookies anymore beause &lt;code&gt;secret_key_base&lt;/code&gt; had a different value, and my users needed to log in again.&lt;/p&gt;

&lt;p&gt;To solve the problem, just provide a &lt;code&gt;SECRET_KEY_BASE&lt;/code&gt; environment variable in your production server. The simplest way to generate it is to run &lt;code&gt;rake secret&lt;/code&gt; in your terminal (make sure you're in a Rails project folder).&lt;/p&gt;

</description>
      <category>rails</category>
      <category>programming</category>
      <category>devops</category>
    </item>
  </channel>
</rss>
