<?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: Alex Musayev</title>
    <description>The latest articles on Forem by Alex Musayev (@dreikanter).</description>
    <link>https://forem.com/dreikanter</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%2F95387%2F64a1a622-08de-4c22-a8fc-2682fc34e703.jpeg</url>
      <title>Forem: Alex Musayev</title>
      <link>https://forem.com/dreikanter</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dreikanter"/>
    <language>en</language>
    <item>
      <title>Sane Defaults</title>
      <dc:creator>Alex Musayev</dc:creator>
      <pubDate>Fri, 28 Feb 2020 22:32:57 +0000</pubDate>
      <link>https://forem.com/amplifr/sane-defaults-25pe</link>
      <guid>https://forem.com/amplifr/sane-defaults-25pe</guid>
      <description>&lt;p&gt;&lt;strong&gt;This post explains one of the practices we use to improve web service configuration management. As an example, we will use a Ruby on Rails application running on the Kubernetes environment. However, the general concept of sane defaults is technology-agnostic and entirely applicable to different programming languages and frameworks.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine medium to large scale web service maintained by a team of 5 engineers. The production environment constitutes a distributed system of Rails servers, auxiliary microservices, and data storage. Everything is running on a Kubernetes cluster. As usual, besides the production, there is also a staging configuration — very similar, but not identical. It is smaller and cheaper. And there are also local development environments typically containing a minimal task-specific subset of services.&lt;/p&gt;

&lt;p&gt;To maintain multi-environment Rails application, we need it to be configurable. Most common approach here is to follow &lt;a href="https://12factor.net"&gt;The Twelve-Factor App&lt;/a&gt; methodology and store configuration in the system environment variables.&lt;/p&gt;

&lt;p&gt;Say, we use &lt;code&gt;dotenv&lt;/code&gt; gem to set some arbitrary configuration parameters for development environment:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# .env
MAIN_DOMAIN=app.local
REDIS_URL=redis://127.0.0.1:6379/0
AVATAR_SIZE=100
IMGPROXY_URL=https://imgproxy.local
SECRETS_IMGPROXY_KEY=key
SECRETS_IMGPROXY_SALT=salt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now when we can address configuration values like so: &lt;code&gt;ENV.fetch('MAIN_DOMAIN')&lt;/code&gt;. Dead-straight, cheap and easy? Sort of. Let's take a closer look at the potential shortcomings related to environment variables-based configuration in a mature Rails project.&lt;/p&gt;

&lt;h1&gt;
  
  
  Downsides of using environment variables
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;Configuration bloat.&lt;/strong&gt; A more extensive project configuration may include hundreds of parameters. This fact itself is healthy — big configuration is better than hardcoding magic values all over the codebase, anyway. But you need to expect the config to require some gardening to prevent duplication, obsoletion, and other potential issues.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Each change in the configuration require manual effort.&lt;/strong&gt; If your project uses &lt;code&gt;dotenv&lt;/code&gt; on the development environment, each team member will have to maintain an up to date copy of the &lt;code&gt;.env&lt;/code&gt; file with all the necessary configuration variables.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;.env&lt;/code&gt; file may include individual settings — like a personal S3 bucket name you use for testing — and not supposed to be committed to the version control system. Therefore, it is not possible to automate config updates propagation amongst the team members.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Application startup errors.&lt;/strong&gt; Typically it is a good practice to check mandatory configuration parameters during the application initialization stage and raise an error if something is missing (note &lt;code&gt;#fetch&lt;/code&gt; method call in the above example). I will skip a detailed explanation here, assuming you already familiar with the &lt;a href="https://en.wikipedia.org/wiki/Fail-fast"&gt;fail-fast&lt;/a&gt; principle in system design. But in the practical context of this article, this validation means each configuration change may cause a hiccup in every team member's working process. Nobody likes it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fragile tests.&lt;/strong&gt; Sometimes you need to use your configuration during testing, directly or indirectly. That means your test suite execution result depends on the system it is running on. A successful test run on a developer's machine does not guarantee the same result on CI, even in a containerized environment. And the reasons for the failure could be much less evident than the wrong &lt;code&gt;DATABASE_URL&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Sane defaults
&lt;/h1&gt;

&lt;p&gt;Our experience of maintaining configuration for larger web services shows that a significant part of configuration parameters don't change too often. Based on this observation, it is possible to mitigate most of the issues listed above by complementing configuration parameters with default values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="c1"&gt;# const/defaults.rb&lt;/span&gt;

&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;Defaults&lt;/span&gt;
  &lt;span class="no"&gt;AVATAR_SIZE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;
  &lt;span class="no"&gt;MAIN_DOMAIN&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'amplifr.local'&lt;/span&gt;
  &lt;span class="no"&gt;IMGPROXY_MAX_SRC_RESOLUTION&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;9000&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Rails autoload mechanism will make &lt;code&gt;Defaults&lt;/code&gt; module resolvable from everywhere in your project, making it possible to use the constants as a safe fallback each time you need the corresponding configuration parameter:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'AVATAR_SIZE'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;Defaults&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;AVATAR_SIZE&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Default constants make it safe to remove related environment variables from &lt;code&gt;.env&lt;/code&gt;, or &lt;code&gt;Dockerfile&lt;/code&gt;, or&lt;code&gt;docker-compose.yml&lt;/code&gt;, or whatever you use to keep your configuration. Configuration changes will automatically propagate over the version control system, preventing some of the application startup errors. And from now on, it is easy to keep an environment-agnostic test suite (just keep using &lt;code&gt;Defaults::*&lt;/code&gt; values instead of the &lt;code&gt;ENV&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;Furthermore, I'd like to highlight that sane defaults plays well with the Twelve-Factor App methodology since environment variables always have higher priority over the constants.&lt;/p&gt;

&lt;p&gt;There are some limitations, though: not every configuration parameter could have a default. Each default value should match the following conditions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A sane default supposes to remain identical for all code execution environments, including Rails environments, individual developer machines, server instances, CI, etc.&lt;/li&gt;
&lt;li&gt;Default values should never be secret. In other words, defaults do not apply to API keys or other configuration parameters that you don't want to keep in the repository.&lt;/li&gt;
&lt;li&gt;A default does not change often or ever.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Making it easy to use
&lt;/h1&gt;

&lt;p&gt;We solved the issues of the environment variables-based configuration, but &lt;del&gt;at what cost?&lt;/del&gt; accessing configuration params with &lt;code&gt;fetch&lt;/code&gt; calls is bulky and cumbersome:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;ENV&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'AVATAR_SIZE'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Defaults&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;AVATAR_SIZE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Also, it would be a bad idea to rely on the developer to remember about the defaults each time they use a related configuration. And what about required parameter validation during the application initialization?&lt;/p&gt;

&lt;p&gt;Luckily it is easy to solve all of these problems with &lt;a href="https://github.com/rubyconfig/config"&gt;config&lt;/a&gt; gem. It allows to define structured configuration with YAML files, similar to Rails' &lt;code&gt;database.yml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;# config/settings.yml&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;main_domain&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV.fetch('MAIN_DOMAIN') { Defaults::MAIN_DOMAIN } %&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;redis_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV.fetch('REDIS_URL') { Defaults::REDIS_URL } %&amp;gt;&lt;/span&gt;
&lt;span class="na"&gt;avatar_size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV.fetch('AVATAR_SIZE') { Defaults::AVATAR_SIZE } %&amp;gt;&lt;/span&gt;

&lt;span class="na"&gt;imgproxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;base_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV.fetch('IMGPROXY_URL') %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV.fetch('SECRETS_IMGPROXY_KEY') %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;salt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV.fetch('SECRETS_IMGPROXY_SALT') %&amp;gt;&lt;/span&gt;
  &lt;span class="na"&gt;max_src_resolution&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= ENV.fetch('IMGPROXY_MAX_SRC_RESOLUTION') { Defaults::IMGPROXY_MAX_SRC_RESOLUTION } %&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Being added to your &lt;code&gt;Gemfile&lt;/code&gt;, &lt;code&gt;config&lt;/code&gt; gem builds a globally available settings object during Rails application startup, providing a set of concise accessors to the configuration params, like &lt;code&gt;Settings.main_domain&lt;/code&gt; or &lt;code&gt;Settings.imgproxy.base_url&lt;/code&gt;.&lt;/p&gt;

&lt;h1&gt;
  
  
  Summary
&lt;/h1&gt;

&lt;p&gt;Here is a list of benefits you gain from complementing environment-based configuration with sane defaults:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prevent configuration bloat.&lt;/li&gt;
&lt;li&gt;Automate shared configs propagation over developer machines.&lt;/li&gt;
&lt;li&gt;Prevent startup errors caused by the lack of shared configuration params.&lt;/li&gt;
&lt;li&gt;More stable tests.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>configuration</category>
      <category>practices</category>
      <category>rails</category>
      <category>kubernetes</category>
    </item>
    <item>
      <title>Bundling Custom Assets with Sprockets</title>
      <dc:creator>Alex Musayev</dc:creator>
      <pubDate>Fri, 28 Feb 2020 16:08:05 +0000</pubDate>
      <link>https://forem.com/dreikanter/bundling-custom-assets-with-sprockets-171k</link>
      <guid>https://forem.com/dreikanter/bundling-custom-assets-with-sprockets-171k</guid>
      <description>&lt;p&gt;&lt;strong&gt;This article explains how to process custom asset files with Rails Asset Pipeline by customizing Sprockets configuration.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let's consider an example case of Rails application that uses client-side &lt;a href="https://mustache.github.io"&gt;Mustache&lt;/a&gt; templates rendering. What would be an efficient way to access the templates from JavaScript?&lt;/p&gt;

&lt;p&gt;There are several approaches here. The basic one is to nest the templates directly into the HTML view code. Like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/template"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"post-template"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;h2&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/h2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="p"&gt;}}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;But this is far from ideal:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Most probably, there will be more templates, and you will need to share some of them between different views. Therefore it makes sense to keep them organized, instead of scattering templates all over the HTML.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Nesting a template is an unnecessary manual operation. It is better to have templates available from JavaScript right away, instead of relying on the fact that you won't forget to add one.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Sometimes nesting a template within &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; element breaks syntax highlighting, therefore keeping templates in individual &lt;code&gt;*.mustache&lt;/code&gt; files usually works better. However, this depends on your text editor configuration.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To solve these issues, let's make Rails Asset Pipeline generate a JSON object within the JavaScript bundle, and populate this object from templates directory at &lt;code&gt;app/assets/templates&lt;/code&gt;. For the consumer, it will look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;Templates&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;post&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;&amp;lt;h2&amp;gt;{{ title }}&amp;lt;/h2&amp;gt;&amp;lt;p&amp;gt;{{ body }}&amp;lt;/p&amp;gt;&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;comment&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This way, the templates will be preloaded with the shared bundle, and remain available from every view that is using it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 1. Register new MIME type
&lt;/h2&gt;

&lt;p&gt;In a typical Rails application, custom Sprockets configuration should be defined under &lt;code&gt;config.assets.configure&lt;/code&gt; block within the &lt;code&gt;Application&lt;/code&gt; class. So the examples below are based on the assumption that a block like this already exists in your &lt;code&gt;config/application.rb&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="k"&gt;module&lt;/span&gt; &lt;span class="nn"&gt;MyApplication&lt;/span&gt;
  &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Application&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Application&lt;/span&gt;
    &lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;configure&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;

      &lt;span class="sr"&gt;//&lt;/span&gt; &lt;span class="no"&gt;Custom&lt;/span&gt; &lt;span class="no"&gt;Sprockets&lt;/span&gt; &lt;span class="n"&gt;configuration&lt;/span&gt; &lt;span class="n"&gt;should&lt;/span&gt; &lt;span class="n"&gt;go&lt;/span&gt; &lt;span class="n"&gt;here&lt;/span&gt;

    &lt;span class="k"&gt;end&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;MIME type registration is straightforward. Just define an association between a new type, and the list of file extensions you plan to process:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register_mime_type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'text/mustache'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;extensions: &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'.mustache'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 2. Register new assets transformer
&lt;/h2&gt;

&lt;p&gt;The most important part here is the callback. It is a callable object that converts asset file content from &lt;code&gt;source_type&lt;/code&gt; to something of &lt;code&gt;target_type&lt;/code&gt;. The callback should return a hash with processed content in &lt;code&gt;:data&lt;/code&gt; key:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="n"&gt;source_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'text/mustache'&lt;/span&gt;
&lt;span class="n"&gt;target_type&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'application/javascript'&lt;/span&gt;

&lt;span class="n"&gt;callback&lt;/span&gt; &lt;span class="o"&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="n"&gt;input&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="ss"&gt;data: &lt;/span&gt;&lt;span class="s1"&gt;'// HELLO ${input[:name]}'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;register_transformer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;target_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Lambda expression could be unsuitable for real-life situations. A service class, defined somewhere outside Rails configuration, will be a better choice. But for now, let's finish the boilerplate first. A real transformer example will follow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step 3. Enable custom file type processing
&lt;/h2&gt;

&lt;p&gt;To achieve that, add a regular expression to the precompile array in the assets initializer (&lt;code&gt;config/initializers/assets.rb&lt;/code&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Rails&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;application&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;assets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;precompile&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sr"&gt;/\.(?:mustache)\z/&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;h2&gt;
  
  
  Step 4. Bundle transformed assets
&lt;/h2&gt;

&lt;p&gt;After the new transformer is registered, both &lt;a href="https://guides.rubyonrails.org/asset_pipeline.html#manifest-files-and-directives"&gt;require and require_tree&lt;/a&gt; directives will play well with the custom assets type.&lt;/p&gt;

&lt;p&gt;If you keep Mustache templates under &lt;code&gt;app/assets/templates&lt;/code&gt;, adding this line to &lt;code&gt;app/assets/javascripts/application.js&lt;/code&gt; will inject transformer callback output for each &lt;code&gt;*.mustache&lt;/code&gt; file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;//= require_tree ../templates&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Dummy lambda-transformer from the previous step will replace this line with a list of commented file names like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// HELLO template_file_name&lt;/span&gt;
&lt;span class="c1"&gt;// HELLO another_template_file_name&lt;/span&gt;
&lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To see it working, don't forget to restart Rails server and make sure to purge the assets cache.&lt;/p&gt;

&lt;p&gt;And here is an actual transformer example, that generates JavaScript object from Mustache template files:&lt;br&gt;
&lt;/p&gt;

&lt;div class="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;MustacheTransformer&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;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="ss"&gt;:data&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'window.Templates'&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="ss"&gt;data: &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; = &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; || {}; &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;['&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'] = &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&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;Besides &lt;code&gt;:data&lt;/code&gt;, the &lt;code&gt;input&lt;/code&gt; object contains several other extension fields, that could be valuable during content transformation. Check out the official reference for the &lt;a href="https://github.com/rails/sprockets/blob/master/guides/extending_sprockets.md#extension-keys"&gt;full list&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The case of Mustache templates elaborated in this article is just a concrete example of extending Sprockets. Besides templates, it is possible to bundle any other assets with arbitrary transformation logic that suit your needs.&lt;/p&gt;

&lt;p&gt;Peace ✌️&lt;/p&gt;

&lt;p&gt;References:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://gorails.com/forum/extend-sprockets-to-bundle-mustache-templates"&gt;Original discussion on GoRails forum&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/rails/sprockets/blob/master/guides/extending_sprockets.md"&gt;Extending Sprockets, official guide&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.rubydoc.info/github/rails/sprockets/Sprockets%2FTransformers%3Aregister_transformer"&gt;Sprockets::Transformers API reference&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://guides.rubyonrails.org/asset_pipeline.html#precompiling-assets"&gt;Precompiling assets with Rails Assets Pipeline&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Complete_list_of_MIME_types"&gt;Complete list of MIME types&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/leshill/handlebars_assets"&gt;handlebars_assets gem&lt;/a&gt;: a ready-to-use replacement for particular case of embedding templates into Rails assets bundle.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ruby</category>
      <category>rails</category>
      <category>javascript</category>
      <category>mustache</category>
    </item>
    <item>
      <title>You probably don't need a version manager for Ruby</title>
      <dc:creator>Alex Musayev</dc:creator>
      <pubDate>Fri, 28 Feb 2020 15:57:30 +0000</pubDate>
      <link>https://forem.com/dreikanter/you-probably-don-t-need-a-version-manager-for-ruby-4lld</link>
      <guid>https://forem.com/dreikanter/you-probably-don-t-need-a-version-manager-for-ruby-4lld</guid>
      <description>&lt;p&gt;This manual explains how to install a recent stable version of Ruby on Linux, without using &lt;a href="https://github.com/rbenv/rbenv" rel="noopener noreferrer"&gt;rbenv&lt;/a&gt; or alternative version managers. My goal was to find a fast and reliable approach for provisioning expendable virtual machines for Rails development environment.&lt;/p&gt;

&lt;p&gt;Basic assumptions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Installation speed matters.&lt;/li&gt;
&lt;li&gt;Only one version of Ruby is required system-wide.&lt;/li&gt;
&lt;li&gt;We're using Ubuntu, or any other Debian-based Linux distribution.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Step 1. Install prebuilt Ruby from Brightbox
&lt;/h2&gt;

&lt;p&gt;Notice the &lt;code&gt;dev&lt;/code&gt; package that should be installed in addition to the primary one. It is required to build native extensions for Ruby gems.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;software-properties-common
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-add-repository ppa:brightbox/ruby-ng
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get update
&lt;span class="nb"&gt;sudo &lt;/span&gt;apt-get &lt;span class="nb"&gt;install &lt;/span&gt;ruby2.6 ruby2.6-dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Brightbox manual proposes a tool called &lt;a href="https://www.brightbox.com/docs/ruby/ubuntu/#switching-the-default-ruby-version" rel="noopener noreferrer"&gt;ruby-switch&lt;/a&gt; that can help to switch between multiple Rubies on the same system. Since there will be only one of them, this step is unnecessary.&lt;/p&gt;
&lt;h2&gt;
  
  
  Step 2. Make gem work without sudo
&lt;/h2&gt;

&lt;p&gt;By default, &lt;code&gt;gem&lt;/code&gt; will try to install new gems to the system folder (e.g. &lt;code&gt;/var/lib/gems/2.6.0&lt;/code&gt;), which is no good. Ruby version managers override this path with something under user home directory. But the same operation could be done manually. To permanently set target directory to the user home, and  these lines to your &lt;code&gt;~/.bashrc&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;GEM_HOME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.gem/ruby/2.6.0
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;PATH&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;/.gem/ruby/2.6.0/bin:&lt;span class="nv"&gt;$PATH&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Here is the most important thing you need to know about Ruby version managers to understand what exactly they do in system configuration:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"RubyGems' default local repository can be overridden with the &lt;code&gt;GEM_PATH&lt;/code&gt; and &lt;code&gt;GEM_HOME&lt;/code&gt; environment variables. GEM_HOME sets the default repository to install into. &lt;code&gt;GEM_PATH&lt;/code&gt; allows multiple local repositories to be searched for gems" — &lt;a href="http://guides.rubygems.org/command-reference/" rel="noopener noreferrer"&gt;rubygems.org&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  Step 3. Run a quick ad-hoc test
&lt;/h2&gt;

&lt;p&gt;Log into the shell, and execute these commands to ensure &lt;code&gt;gem&lt;/code&gt; and &lt;code&gt;ruby&lt;/code&gt; binaries are available, gem home path is configured correctly, and native extensions could be built:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd
&lt;/span&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;bundler rails
rails new testapp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Or just execute &lt;code&gt;gem env&lt;/code&gt; to see the paths without installing anything.&lt;/p&gt;

&lt;p&gt;Here is a full &lt;code&gt;Vagrantfile&lt;/code&gt; to provision new Linux VM with Ruby development environment: &lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/dreikanter" rel="noopener noreferrer"&gt;
        dreikanter
      &lt;/a&gt; / &lt;a href="https://github.com/dreikanter/vagrant-rails" rel="noopener noreferrer"&gt;
        vagrant-rails
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Vagrant configuration for Rails development environment
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Vagrant configuration for Rails development environment&lt;/h1&gt;

&lt;/div&gt;

&lt;p&gt;Will install:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ubuntu Server (19.04 LTS)&lt;/li&gt;
&lt;li&gt;Ruby (via &lt;a href="https://github.com/postmodern/ruby-install" rel="noopener noreferrer"&gt;ruby-install&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;PostgreSQL&lt;/li&gt;
&lt;li&gt;Redis&lt;/li&gt;
&lt;li&gt;ElasticSearch&lt;/li&gt;
&lt;li&gt;NodeJS&lt;/li&gt;
&lt;li&gt;Yarn&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Set up:&lt;/p&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;brew cask install virtualbox vagrant
vagrant up&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Rails app setup after first log in (assuming Vagrant file is in you project root):&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c1"&gt;cd&lt;/span&gt; /app
bundle install
yarn install
bundle &lt;span class="pl-c1"&gt;exec&lt;/span&gt; rails db:drop db:create db:migrate db:seed --trace&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Running Rails app server:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;rails s&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Running &lt;a href="https://github.com/webpack/webpack-dev-server" rel="noopener noreferrer"&gt;webpack-dev-server&lt;/a&gt;:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;bin/webpack-dev-server&lt;/pre&gt;

&lt;/div&gt;
&lt;/div&gt;

  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/dreikanter/vagrant-rails" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;p&gt;Peace ✌️&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>linux</category>
      <category>vagrant</category>
      <category>provisioning</category>
    </item>
  </channel>
</rss>
