makandra tech survey - results

These are the results of the "personal tech stack survey". I've included only the most popular mentions, maybe it can help you find one or two useful tools for your own usage.

Desktop environment

pie title Desktop environment
    "Gnome" : 16
    "i3": 2
    "sway": 2
    "awesome": 1
    "bspwm": 1
    "mate": 1
    "xfce": 1

Gnome dominates (unsuprising, it's the Ubuntu default), but quite a few people use tiling window managers, most popular i3 and the mostly i3-compatible [sway](https://swaywm....

Double loading issue with Ruby default gems

Ruby includes many standard gems that are bundled into the Ruby installation. Here is an example for the gem strscan that will be displayed as default:

gem list strscan     

*** LOCAL GEMS ***

strscan (default: 3.0.1)

It is still possible to have newer version of a gem installed beside the default version:

gem install strscan  
Fetching strscan-3.0.3.gem
Building native extensions. This could take a while...
Successfully installed strscan-3.0.3
1 gem installed
gem list strscan   

*** LOC...

You are not using filter_map often enough

Somewhat regularly, you will need to filter a list down to some items and then map them to another value.

You can of course chain map and compact, or select/filter and map, but Ruby 2.7 introduced a method for this exact purpose: filter_map.

So instead of

>> [1, 2, 3, 4, 5, 6].map { |i| i * 2 if i.even? }.compact
=> [4, 8, 12]

or

>> [1, 2, 3, 4, 5, 6].select(&:even?).map { |i| i * 2 }
=> [4, 8, 12]

you can just do

>> [1,...

Capybara: Most okayest helper to download and inspect files

Testing file download links in an end-to-end test can be painful, especially with Selenium.

The attached download_helpers.rb provides a download_link method for your Capybara tests. It returns a hash describing the download's response:

details = download_link('Download report')
details[:disposition]  # => 'attachment' or 'inline'
details[:filename]     # => 'report.txt'
details[:text]         # => file content as string
details[:content_type] # => 'text/plain'

Features

Compared to [other approaches](...

JavaScript: New Features in ES2021

tl;dr

With ES2021 you now can use str.replaceAll(), Promise.any(), logical assignment operators, numeric separators and WeakRef on all major browsers except IE11.

replaceAll

JavaScript's replace(searchValue, replaceValueOrFn) by default replaces only the first match of a given String or RegExp.
When supplying a RegExp as the searchValue argument, you can specify the g ("global") modifier, but you have to remember doing that, hence using replace when you expect global replacement is prone to errors.
When supplying st...

net-ssh and openssl-3.0.0

You'll need openssl-3 or newer for servers running 22.04

Ruby version 3.1 uses by default the gem openssl-3.0.0. This can cause issues with the gem net-ssh (6.1.0). This is a known bug.

Typically this can cause an error while deploying an application with capistrano:

could not verify server signature (SSHKit::Runner::ExecuteError)

or

Ed25519::VerifyError: signature verification failed!

As temporary workaround add the following line to your Gemfile:

gem 'openssl', ...

Use DatabaseCleaner with multiple test databases

There is a way to use multiple databases in Rails.
You may have asked yourself how you're able to keep your test databases clean, if you're running multiple databases with full read and write access at the same time. This is especially useful when migrating old/existing databases into a new(er) one.

Your database.yml may look like this:

default: &default
  adapter: postgresql
  encoding: unicode
  username: <%= ENV['DATABASE_USER'] %>
  host: <%= ENV['DATABASE...

Gem development: recommended gem metadata

The gemspec for gems allows to add metadata to your gem, some of which have a special meaning and are helpful for users.
You can provide links to your Github bugtracker or changelog file that are then used on the rubygems page of your gem (in the sidebar, e.g. see gem page of consul).

Here are some keys that should be filled:

Gem::Specification.new do |s|
  s.name = 'my-gem'
  s.homepage = 'https://github.com/makandra/my-gem'

  s.metadata = {
    'source_code_uri' => s.homepage,
    'bug_tracker...

How to get information about a gem (via CLI or at runtime from Ruby)

When you need information about a gem (like version(s) or install path(s)), you can use the gem binary from the command line, or the Gem API inside a ruby process at runtime.

gem binary (in a terminal)

You can get some information about a gem by running gem info <gem name> in your terminal.

Example:

$ gem info irb

*** LOCAL GEMS ***

irb (1.4.1, 1.3.5)
    Author: Keiju ISHITSUKA
    Homepage: https://github.com/ruby/irb
    Licenses: Ruby, BSD-2-Clause
    Installed at (1.4.1): /home/arne/.rbenv/versions/3.0.3/lib/ruby/g...

Version 5 of the Ruby Redis gem removes Redis.current

Redis.current will be removed without replacement in redis-rb 5.0.
Version 4.6.0 adds deprecation warnings for Redis.current and Redis.current=:

`Redis.current=` is deprecated and will be removed in 5.0.

If your application still uses Redis.current, you can only fix it by no longer using it. Here is how.

Redis.new when you need it

You can easily instantiate a Redis client when you need it.

There is probably already a constant like REDIS_URL that you use to configure Sidekiq or similar. So just use that one.

``...

Integrating ESLint

Introduction

To ensure a consistent code style for JavaScript code, we use ESLint. The workflow is similar to integrating rubocop for Ruby code.

1. Adding the gem to an existing code base

You can add the following lines to your package.json under devDependencies:

  "devDependencies": {
    "eslint": "^8.7.0",
    "eslint-config-standard": "^16.0.3",
    "eslint-plugin-import": "^2.25.4",
    "eslint-plugin-node"...

How to add esbuild to the rails asset pipeline

This are the steps I needed to do to add esbuild to an application that used the vanilla rails asset pipeline with sprockets before.

Preparations

  1. update Sprockets to version 4
  2. add a .nvmrc with your preferred node version (and install it)
  3. add gems jsbundling-rails and foreman to your Gemfile:
    gem 'jsbundling-rails'
    group :development, :test do
      gem 'foreman'
      # ...
    end
    
  4. bundle install
  5. run bin/rails javascript:install:esbuild in a console to prepare esbuild.
  6. run yarn install...

Ensure passing Jasmine specs from your Ruby E2E tests

Jasmine is a great way to unit test your JavaScript components without writing an expensive end-to-end test for every small requirement.

After we integrated Jasmine into a Rails app we often add an E2E test that opens that Jasmine runner and expects all specs to pass. This way we see Jasmine failures in our regular test runs.

RSpec

In a [feature spec](https://relishapp.com/rspec/rspec-rails/docs/feature-spec...

RSpec: automatic creation of VCR cassettes

This RailsCast demonstrated a very convenient method to activate VCR for a spec by simply tagging it with :vcr.

For RSpec3 the code looks almost the same with a few minor changes. If you have the vcr and webmock gems installed, simply include:

# spec/support/vcr.rb
VCR.configure do |c|
  c.cassette_library_dir = Rails.root.join("spec", "vcr")
  c.hook_into :webmock
end

RSpec.configure do |c|
  c.around(:each, :vcr) do |example|
    name = example.metadata[:full_descripti...

Better numeric inputs in desktop browsers

You want to use <input type="number"> fields in your applications.
However, your desktop users may encounter some weird quirks:

  1. Aside from allowing only digits and decimal separators, an "e" is also allowed (to allow scientific notation like "1e3").
    • Non-technical users will be confused by this.
    • Your server needs to understand that syntax. If it converts only digits (e.g. to_i in Ruby) you'll end up with wrong values (like 1 instead o...

Fix REPL of better_errors page

The gem better_errors offers a detailed error page with an interactive REPL for better debugging.
I had the issue that on a few projects with Ruby 2.5.8, the REPL was not shown.

Solution

To make the REPL work properly with this Ruby version I had to update the gem binding_of_caller to at least version 0.8.0.

From the [better_errors](https://github.com/BetterE...

Ruby: You can nest regular expressions

Ruby lets you re-use existing RegExp objects by interpolating it into new patterns:

locales_pattern = /de|en|fr|es/i

html_tag_pattern = /<html lang="#{locales_pattern}">/

Any modifiers like /i or /x will be preserved within the interpolated region, which is pretty cool. So in the example above only the interpolated locales are case-insensitive, while the pattern around it (/<html .../) remains case-sensitive.

RSpec matcher to compare two HTML fragments

The RSpec matcher tests if two HTML fragments are equivalent. Equivalency means:

  • Whitespace is ignored
  • Types of attribute quotes are irrelevant
  • Attribute order is irrelevant
  • Comments are ignored

You use it like this:

html = ...
expect(html).to match_html(<<~HTML)
  <p>
    Expected content
  </p>  
HTML

You may override options from CompareXML by passing keyword arguments after the HTML string:

html = ...
expect(html).to match_html(<<~HTML, ignore_text_nodes: true)
 ...

How to extract a Ruby gem

The rubygems binary gem allows to extract a local gem with gem unpack GEMNAME. For more details see the official documentation.


This was useful for compliance checks, when it was necessary to check the license of the C-files in nokogiri.

Heads up: Byebug has problems with zeitwerk

I encountered a unlucky behavior of byebug 11.1.3 (the most recent version at time of writing) when using it with Rails 6 and it's new autoloading component, zeitwerk. There already is a issue for that, so I hope it will be fixed with a future release.

The following test succeeds:

  context 'factories' do
    let(:test_case) { FactoryBot.create(:test_case) }
    it 'are valid' do
      expect(test_case).to be_valid
    end
  end

But when I did the same in byebug the foll...

Ruby: Fixing strings with invalid encoding and converting to UTF-8

When dealing with external data sources, you may have to deal with improperly encoded strings.
While you should prefer deciding on a single encoding with the data-providing party, you can not always force that on external sources.
It gets worse when you receive data with encoding declaration that does not reliably fit the accompanying string bytes.

Here is a Ruby class that helps converting such strings to a proper encoding.
Note that it tries several approaches of changing the encoding. **This is not a silver bullet and may or may not work...

Pre-releasing an npm package

You can publish pre-release versions of an npm package.

Naming convention for pre-release versions

An npm package must use Semantic Versioning's naming convention for its version. In Semantic Versioning, the version number and pre-release identifier (like rc1) must be separated by a dash, like this:

  • 1.0.0-rc1
  • 2.3.0-alpha2
  • 3.0.0-beta3

Publishing to a pre-release tag

npm packages have multiple "current" releases, identified by "tags". The default tag is latest. It is expected to contain the la...