<?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: Abhinav Saxena</title>
    <description>The latest articles on DEV Community by Abhinav Saxena (@abhinavs).</description>
    <link>https://dev.to/abhinavs</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%2F106937%2F83d8dc25-0ecb-476a-8751-0d9d1c925746.jpeg</url>
      <title>DEV Community: Abhinav Saxena</title>
      <link>https://dev.to/abhinavs</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abhinavs"/>
    <language>en</language>
    <item>
      <title>Elixir Plugs</title>
      <dc:creator>Abhinav Saxena</dc:creator>
      <pubDate>Sat, 03 Dec 2022 06:33:21 +0000</pubDate>
      <link>https://dev.to/abhinavs/elixir-plugs-3d4n</link>
      <guid>https://dev.to/abhinavs/elixir-plugs-3d4n</guid>
      <description>&lt;p&gt;Of late, I have been writing some Elixir code to build an API layer for Soopr, a SaaS tool I am working on. This system needed a simplistic authentication mechanism and I ended up writing a custom Plug for it. This post explains what are plugs and how you can write one yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a Plug?
&lt;/h2&gt;

&lt;p&gt;In Elixir world, Plug is a bit similar to &lt;a href="https://github.com/rack/rack"&gt;Rack&lt;/a&gt; in Ruby. &lt;a href="https://hexdocs.pm/plug/readme.html"&gt;Official documentation&lt;/a&gt; describes Plug as:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A specification for composable modules between web applications&lt;/li&gt;
&lt;li&gt;An abstraction layer for connection adapters for different web servers in the Erlang VM&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Plugs can be chained together and they can be very powerful as well as can add simplicity and readability to your code.&lt;/p&gt;

&lt;p&gt;If I were to describe Plug, plugs are a mechanism through which either you can transform your request/response or you can decide to filter requests that reach to your controller.&lt;/p&gt;

&lt;p&gt;In Plug/Phoenix world, &lt;code&gt;%Plug.Conn{}&lt;/code&gt; in the connection struct that contains both request &amp;amp; response paramters and is typically called &lt;code&gt;conn&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Transformation
&lt;/h3&gt;

&lt;p&gt;It essentially means modifying &lt;code&gt;conn&lt;/code&gt; (technically, creating a copy of original &lt;code&gt;conn&lt;/code&gt;) and adding more details to it.  These plugs also need to return modified &lt;code&gt;conn&lt;/code&gt; struct so that plugs can be chained together. Some examples of plugs are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; &lt;a href="https://hexdocs.pm/plug/Plug.RequestId.html"&gt;Plug.RequestId&lt;/a&gt; - generates a unique request id for each request&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://hexdocs.pm/plug/Plug.Head.html"&gt;Plug.Head&lt;/a&gt; - converts HEAD requests to GET requests&lt;/li&gt;
&lt;li&gt; &lt;a href="https://hexdocs.pm/plug/Plug.Logger.html"&gt;Plug.Logger&lt;/a&gt; - logs requests&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Filter
&lt;/h3&gt;

&lt;p&gt;There are cases when you want to stop HTTP requests if they don’t meet your criteria. Example - they come from a blocked IP or more common example - don’t have required authentication details. In these cases you use such plugs. &lt;a href="https://hexdocs.pm/plug/Plug.BasicAuth.html"&gt;Plug.BasicAuth&lt;/a&gt; is one such plug, it provides Basic HTTP authentication.&lt;/p&gt;



&lt;h2&gt;
  
  
  How are plugs chained?
&lt;/h2&gt;

&lt;p&gt;In Phoenix world, plugs are generally added to your &lt;code&gt;endpoint.ex&lt;/code&gt; or &lt;code&gt;router.ex&lt;/code&gt; modules.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Router&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:router&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;BasicAuth&lt;/span&gt;

  &lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="ss"&gt;:api&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="ss"&gt;:accepts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"json"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; 
    &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="ss"&gt;:basic_auth&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="ss"&gt;username:&lt;/span&gt; &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetch_env!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"AUTH_USER"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="ss"&gt;password:&lt;/span&gt; &lt;span class="no"&gt;System&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;fetch_env!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"AUTH_KEY"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="s2"&gt;"/api/v1/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;pipe_through&lt;/span&gt; &lt;span class="ss"&gt;:api&lt;/span&gt;
    &lt;span class="n"&gt;get&lt;/span&gt; &lt;span class="s2"&gt;"/posts"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;PostController&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:index&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;In this example - our &lt;code&gt;api&lt;/code&gt; pipeline would accept only json requests and require basic authentication. This api pipeline is then used to define a &lt;code&gt;GET /api/v1/posts&lt;/code&gt; route.&lt;/p&gt;



&lt;h2&gt;
  
  
  How to build your own Plug?
&lt;/h2&gt;

&lt;p&gt;There are two ways in which you can write your own Plug - Function or Module.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Function Plugs
&lt;/h3&gt;

&lt;p&gt;Function plugs are generally used if your plug is simple enough and doesn’t have any costly initialisation requirement. To write a function plug, simply define a function which takes two inputs -  &lt;code&gt;conn&lt;/code&gt; and options. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://hexdocs.pm/phoenix/plug.html#function-plugs"&gt;Example&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;introspect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;puts&lt;/span&gt; &lt;span class="sd"&gt;"""
  Verb: #{inspect(conn.method)}
  Host: #{inspect(conn.host)}
  Headers: #{inspect(conn.req_headers)}
  """&lt;/span&gt;
  &lt;span class="n"&gt;conn&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Module Plugs
&lt;/h3&gt;

&lt;p&gt;Module plugs are useful when you have a bit heavy initialisation process or you need auxilary functions to keep your code readable. For a Module Plug, you need you to define following two functions inside an elixir module: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;init/1&lt;/code&gt; where you can do the initialisation bit. It takes options as input, something that you can pass when using it in router or endpoint file.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;call/2&lt;/code&gt; which is nothing but a function plug and takes exactly the same two parameters - &lt;code&gt;conn&lt;/code&gt; and &lt;code&gt;options&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;MyAppWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;BearerAuth&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;

  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;Plug&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Conn&lt;/span&gt;
  &lt;span class="n"&gt;alias&lt;/span&gt; &lt;span class="no"&gt;MyApp&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Account&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;init&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;get_bearer_auth_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="no"&gt;nil&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;unauthorized&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="ss"&gt;:error&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;unauthorized&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="n"&gt;auth_token&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;
        &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
          &lt;span class="no"&gt;Account&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;get_from_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;auth_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
          &lt;span class="n"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:current_account&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;account&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;else&lt;/span&gt;
          &lt;span class="n"&gt;conn&lt;/span&gt; &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;unauthorized&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;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;get_bearer_auth_token&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;with&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"Bearer "&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;auth_token&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;-&lt;/span&gt; &lt;span class="n"&gt;get_req_header&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"authorization"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
      &lt;span class="n"&gt;auth_token&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;
      &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="ss"&gt;:error&lt;/span&gt;
    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;defp&lt;/span&gt; &lt;span class="n"&gt;unauthorized&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;resp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Unauthorized"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;halt&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 taken from an actual plug which I wrote for Soopr (I have just changed the names). Though I didn’t have any heavy initialisation requirements, I decided to use module way of writing so that I can define a couple of private helper functions. In this example:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;init/1&lt;/code&gt; function takes options, however does nothing with it.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;call/2&lt;/code&gt; function takes conn and options as input&lt;/li&gt;
&lt;li&gt;Private function &lt;code&gt;get_brearer_auth_token/1&lt;/code&gt; takes &lt;code&gt;conn&lt;/code&gt; as input and tries finding &lt;code&gt;auth_token&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;From &lt;code&gt;auth_token&lt;/code&gt; we try finding an account. If we find the account, we add in our &lt;code&gt;conn&lt;/code&gt; so that it is accessible to downstream plugs and controller functions.&lt;/li&gt;
&lt;li&gt;In case we don’t find &lt;code&gt;auth_token&lt;/code&gt; or &lt;code&gt;account&lt;/code&gt; we respond with &lt;code&gt;401&lt;/code&gt; and halt the request, &lt;code&gt;unauthorized/1&lt;/code&gt; function takes care of that.&lt;/li&gt;
&lt;/ol&gt;



&lt;h3&gt;
  
  
  Interesting Use cases
&lt;/h3&gt;

&lt;p&gt;Here are a few interesting problems which you can possibly solve using your own custom plugs.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Firewall - block traffic from barred IP addresses or clients.&lt;/li&gt;
&lt;li&gt;Last Seen - in messenger apps, last seen is maintained separately, using a plug you can update users' last seen value.&lt;/li&gt;
&lt;li&gt;Throttle - throttle requests depending upon limits set by you or pricing plans.&lt;/li&gt;
&lt;li&gt;Circuit Breaker - in case your downstream backend systems are facing trouble, you can decide to prevent traffic from creating more trouble. &lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>elixir</category>
      <category>phoenix</category>
      <category>plug</category>
    </item>
    <item>
      <title>Scoop - a production-ready Sinatra boilerplate project</title>
      <dc:creator>Abhinav Saxena</dc:creator>
      <pubDate>Mon, 22 Nov 2021 19:29:00 +0000</pubDate>
      <link>https://dev.to/abhinavs/scoop-a-production-ready-sinatra-boilerplate-project-lio</link>
      <guid>https://dev.to/abhinavs/scoop-a-production-ready-sinatra-boilerplate-project-lio</guid>
      <description>&lt;h2&gt;
  
  
  Bootstrap Sinatra projects using Corneal, Capistrano, Puma, and Nginx
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--QvNICt4S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6pwqa0dhi2tyb3xtunox.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--QvNICt4S--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6pwqa0dhi2tyb3xtunox.png" alt="Scoop - a production-ready Sinatra boilerplate project" width="880" height="308"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="http://sinatrarb.com/"&gt;Sinatra&lt;/a&gt; is a Ruby framework that helps you quickly create web applications, APIs and microservices. Its minimalism not only attracted a lot of developers, but it also inspired &lt;a href="https://flask.palletsprojects.com/en/2.0.x/"&gt;Flask&lt;/a&gt; in Python and &lt;a href="https://expressjs.com/"&gt;Express&lt;/a&gt; in Node.js&lt;/p&gt;

&lt;p&gt;If you are developing microservices or web APIs, Sinatra is a great choice. However, bootstraping a Sinatra project that is optimized for development speed as well as is easy to deploy has become non trivial because &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Documentation is not easy to find (you mostly find Ruby on Rails specific tips)&lt;/li&gt;
&lt;li&gt;You need to write code to set up and connect DB, manage migrations etc.&lt;/li&gt;
&lt;li&gt;You need to create a right directory structure yourself and configure it accordingly&lt;/li&gt;
&lt;li&gt;You also miss a boot file that you can use with console and with custom rake tasks/scripts&lt;/li&gt;
&lt;li&gt;Setting up projects for deployment is tedious and error prone, with a lot of details to be filled in for Capistrano, Puma and Nginx.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://github.com/abhinavs/scoop"&gt;Scoop&lt;/a&gt; solves it by providing a good boilerplate that can be a starting point for your next project. It solves all the above mentioned problems and comes up with easy to edit configuration files that can help you correctly configure servers.&lt;/p&gt;

&lt;p&gt;Scoop uses&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;a href="http://thebrianemory.github.io/corneal/"&gt;Corneal&lt;/a&gt; to make scaffolding models, controllers and views&lt;/li&gt;
&lt;li&gt;ActiveRecord as database ORM&lt;/li&gt;
&lt;li&gt;Capistrano for deployment&lt;/li&gt;
&lt;li&gt;Puma as app server&lt;/li&gt;
&lt;li&gt;Nginx as a proxy server&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In addition, &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;it is also JSON API ready with JSON, CORS and JSONP support already enabled,&lt;/li&gt;
&lt;li&gt;has a RoR like console, and&lt;/li&gt;
&lt;li&gt;comes up with example script and rake task that can be used to perform tasks that load the environment&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Try Sinatra &amp;amp; Scoop by forking &lt;a href="https://github.com/abhinavs/scoop/fork"&gt;Scoop Repository&lt;/a&gt;. Scoop's &lt;a href="https://github.com/abhinavs/scoop/blob/master/README.md"&gt;README&lt;/a&gt; has detailed steps to set up development and production systems as well as the deployment process. &lt;/p&gt;

&lt;p&gt;Please let me know in comments if you would want to try it out or have any suggestions - would love to improve the project. &lt;/p&gt;

</description>
      <category>sinatra</category>
      <category>capistrano</category>
      <category>nginx</category>
      <category>boilerplate</category>
    </item>
    <item>
      <title>Cookie - a complete landing website for your next project.</title>
      <dc:creator>Abhinav Saxena</dc:creator>
      <pubDate>Tue, 27 Apr 2021 16:56:30 +0000</pubDate>
      <link>https://dev.to/abhinavs/cookie-a-complete-landing-website-for-your-next-project-465k</link>
      <guid>https://dev.to/abhinavs/cookie-a-complete-landing-website-for-your-next-project-465k</guid>
      <description>&lt;p&gt;Recently, while working on a project, I got fascinated with the idea of using a &lt;a href="https://www.abhinav.co/static-websites"&gt;static site&lt;/a&gt; as a landing website, and markdown to power additional supporting pages and blog posts. And the outcome is Cookie.&lt;/p&gt;

&lt;p&gt;You can checkout code on &lt;a href="https://github.com/abhinavs/cookie"&gt;Github&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cookie is a Jekyll and Tailwind CSS based static website that makes the whole process of creating and launching landing websites extremely easy. With its responsive, mobile-friendly pages, integrated blog, additional pages, and &lt;a href="https://www.soopr.co"&gt;Soopr&lt;/a&gt; integration, it can help you focus on building your product than landing website.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;You can see demo app deployed &lt;a href="https://cookie-demo.netlify.app/"&gt;here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cookie also uses Tailwind 2.0 which is a utility first CSS framework and makes the process of iterating on the homepage a lot easier.&lt;/p&gt;

&lt;h2&gt;
  
  
  Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Well-designed landing page&lt;/li&gt;
&lt;li&gt;Responsive and mobile-friendly&lt;/li&gt;
&lt;li&gt;Additional pages like about us, terms of service &amp;amp; privacy policy&lt;/li&gt;
&lt;li&gt;Integrated blog, write content in markdown format&lt;/li&gt;
&lt;li&gt;Easy to customize using Tailwind CSS&lt;/li&gt;
&lt;li&gt;Fast and performant website&lt;/li&gt;
&lt;li&gt;SEO optimized&lt;/li&gt;
&lt;li&gt;RSS feed&lt;/li&gt;
&lt;li&gt;Easy to deploy on platforms like Heroku, AWS, Netlify &amp;amp; Vercel. One-click deploy on Netlify is also possible&lt;/li&gt;
&lt;li&gt;Easy to integrate with headless CMS like &lt;a href="https://forestry.io/"&gt;Forestry&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://wwww.soopr.co"&gt;Soopr&lt;/a&gt; integrated - easy to customize share &amp;amp; like buttons, URL shortening, and website analytics&lt;/li&gt;
&lt;li&gt;Free &amp;amp; MIT Licensed&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;You can check out &lt;a href="https://github.com/abhinavs/cookie"&gt;Github&lt;/a&gt; or &lt;a href="https://www.abhinav.co/cookie"&gt;my blog&lt;/a&gt; for more details on customization and deployment. Do try it out for your next project.&lt;/p&gt;

&lt;h2&gt;
  
  
  Acknowledgement
&lt;/h2&gt;

&lt;p&gt;Cookie uses landing page provided by &lt;a href="https://www.creative-tim.com/learning-lab/tailwind-starter-kit/presentation"&gt;Tailwind Starter Kit&lt;/a&gt; - thanks for providing an amazing landing page under MIT License. Initial code was also inspired by &lt;a href="https://github.com/chunlea/jekyll-tailwindui"&gt;Jekyll TailwindUI&lt;/a&gt;&lt;/p&gt;

</description>
      <category>jekyll</category>
      <category>landingwebsite</category>
      <category>tailwindcss</category>
      <category>opensource</category>
    </item>
    <item>
      <title>8 useful Rails Bytes to improve your development process</title>
      <dc:creator>Abhinav Saxena</dc:creator>
      <pubDate>Tue, 01 Sep 2020 11:10:33 +0000</pubDate>
      <link>https://dev.to/abhinavs/8-useful-rails-bytes-to-improve-your-development-process-lon</link>
      <guid>https://dev.to/abhinavs/8-useful-rails-bytes-to-improve-your-development-process-lon</guid>
      <description>&lt;p&gt;&lt;a href="https://railsbytes.com/"&gt;Rails Bytes&lt;/a&gt; is a platform created by Chris Oliver aka &lt;a href="http://excid3.com/"&gt;excid3&lt;/a&gt; to share useful Ruby on Rails recipes/templates. These templates allow you to add features to both old and new apps. In the background, these templates may install a gem, run their installation steps, or edit configurations themselves. Of late, I have been using Rails Bytes for my new Rails apps, and here are a few of them (in no particular order) which I find very useful.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;HAML&lt;/strong&gt;: Add beautifully DRY, well-indented, clear markup.

I particularly enjoy writing clean markup, and I absolutely love &lt;a href="https://haml.info/"&gt;HAML&lt;/a&gt; It also helps me understand the logic and intent clearly without getting lost in HTML. This template also lets you automatically convert your &lt;code&gt;.erb&lt;/code&gt; files to &lt;code&gt;.haml&lt;/code&gt; files. You also get an option to retain your &lt;code&gt;.erb&lt;/code&gt; files in case you want to go back.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails app:template &lt;span class="nv"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'https://railsbytes.com/script/x7msKK'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Live Reload&lt;/strong&gt;: LiveReload with Webpack for views and viewcomponents.

I was recently exposed to Gatsby, and live reloading added so much to my productivity. Live Reload template tries to emulate the same for Rails. It used webpacker. There's &lt;a href="https://railsbytes.com/public/templates/x7msEo"&gt;another template&lt;/a&gt; on Rails Bytes which achieves the same using guard.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails app:template &lt;span class="nv"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'https://railsbytes.com/script/V1bs61'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;dotenv&lt;/strong&gt;: A Ruby gem to load environment variables from &lt;code&gt;.env&lt;/code&gt;.

One of the tenets of a &lt;a href="http://12factor.net/"&gt;twelve-factor app&lt;/a&gt; is storing  &lt;a href="http://12factor.net/config"&gt;configuration in the environment&lt;/a&gt;. In the development environment, this template uses &lt;a href="https://github.com/bkeepers/dotenv"&gt;dotenv-rails&lt;/a&gt; gem which loads environment variables from .env file in the development environment.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails app:template &lt;span class="nv"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'https://railsbytes.com/script/zOvsQ0'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Simple form&lt;/strong&gt;: Rails forms made easy.

Rails default forms abstract native HTML forms well, &lt;a href="https://github.com/heartcombo/simple_form"&gt;Simple form&lt;/a&gt; takes this abstraction a little further and helps you create forms easily.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails app:template &lt;span class="nv"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'https://railsbytes.com/script/VQLslK'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Recreate&lt;/strong&gt;: Rake task to drop schema and recreate the database.

I am not sure if it's only me - but because of multiple reasons, I tend to recreate my development database numerous times. Rake task added by this template makes it very easy to do.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails app:template &lt;span class="nv"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'https://railsbytes.com/script/VQLsoK'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Annotate&lt;/strong&gt;: Annotate Rails classes with schema and routes info.

Rails does magic with ActiveRecord, and while I completely love it, I also need to refer to column names very frequently. This template and &lt;a href="https://github.com/ctran/annotate_models"&gt;underlying&lt;/a&gt; gem help you achieve that.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails app:template &lt;span class="nv"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'https://railsbytes.com/script/Vqqsqg'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Strong Versions&lt;/strong&gt;: Ensure your gems are appropriately versioned.

I have seen production issues arising because of version mismatch of gems and other external libraries. Install &lt;a href="https://github.com/bobf/strong_versions"&gt;strong_versions&lt;/a&gt; gem to enforce stricter policy on your &lt;code&gt;Gemfile&lt;/code&gt; requirements.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails app:template &lt;span class="nv"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'https://railsbytes.com/script/xjNsMn'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;High Voltage&lt;/strong&gt;: Add High Voltage to manage static pages.

There are static pages in every app, and High Voltage using a good convention saves you from building unnecessary controllers for static pages. For more details check out &lt;a href="https://github.com/thoughtbot/high_voltage"&gt;High Voltage&lt;/a&gt; gem.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;rails app:template &lt;span class="nv"&gt;LOCATION&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'https://railsbytes.com/script/XbBsdZ'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(cross-post from my &lt;a href="http://www.abhinavsaxena.com/useful-railsbytes-templates/"&gt;personal blog&lt;/a&gt;)&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
