#546 — April 1, 2021

Read on the Web

😄 Hi folks! There's a time and a place for April Fools' jokes — today is certainly the right time (someone might want to tell Volkswagen that) but this isn't the place, so fingers crossed there aren't any stories that catch me out this week... :-) Also, Jemma is a back with a tip of the week at the end of this issue, so be sure to enjoy that.
__
Peter Cooper, editor

Ruby Weekly

A Rubyist's Walk Along the C-side: Hello World! — The first in a coming eight part series that takes an accessible tour through the often thorny world of implementing a Ruby extension in C. This introductory post gives you the quickest way to creating a barebones extension.

Peter Zhu

Rails 5.2.5, 6.0.3.6 and 6.1.3.1 Released — Why are these releases significant? They fix the Marcel/MimeMagic dependency issue around licenses that burned super hot last week.

George Claghorn

Long Term Support for Ruby on Rails 4.2, 3.2 and 2.3 - with Support for Ruby 2.7 — Rails LTS provides extended support for old versions of Ruby on Rails. Drop-in gem replacements are available for Rails 4.2, Rails 3.2 and Rails 2.3. You can even run your old Rails app with modern Ruby versions.

Rails LTS sponsor

An Interview with David Heinemeier Hansson — While it does cover Rails’ beginnings, there’s also thoughts on open source and DHH’s campaign against the big tech monopolies and the work on Basecamp’s latest project, the Hey.com email system.

Ben Rometsch

Rails 7 to Allow Run Queries on a Background Thread Poolload_async will let ActiveRecord schedule queries to run asynchronously on a thread pool in a reasonably transparent way. This should open up some nice performance optimizations though potentially some bugs too.

Swaathi Kakarla

Quick Bits

  • The always popular Euruko European Ruby conference is back (online this year, due to COVID) and if you want to speak, their call for speakers is open until April 18.
  • JRuby 9.2.17.0 has been released. It improves Sorbet support.
  • RubyMine 2021.1 beta 4 is out and includes JetBrains' new Code with Me collaborative development feature — the final release is now just around the corner.
  • @RubyCards is a Twitter account aiming to help you learn and remember Ruby concepts by way of flashcards.

💻 Jobs

Senior Backend Engineer — As part of adidas, we face the challenge of building a top-class backend infrastructure for our fitness apps every day.

Runtastic

Senior Software Engineer (US - Remote-Friendly) — Snapdocs is going through a hyper-growth phase, looking for empathetic Senior Software Engineers as we scale our product and team.

Snapdocs

Find Ruby Jobs with Hired — Take 5 minutes to build your free profile & start getting interviews for your next job. Companies on Hired are actively hiring right now.

Hired

📘 Articles & Tutorials

Using CockroachDB with Rails — CockroachDB is a scalable, distributed SQL database that boasts being, if set up correctly, almost impossible to take down. Want to use it with Rails? Here’s how.

Ali Ibrahim

Building a Ruby Web App with and without Rails' Libraries — It’s actually an interesting challenge to try and build a Ruby webapp with as few libraries as possible. Sorry, no Sinatra allowed here.. what about going right with the socket library? 😆 This is a bit of a mixed bag, but you’ll probably pick up something.

Maple Ong (Shopify)

Which Is Fastest? ERB vs. HAML vs. Slim — Comparing the speed of different templating approaches took me back to the late 00’s a bit, but it’s nice to still see people running the numbers. They’re all pretty close nowadays.

Diogo Souza

JetBrains RubyMine: A Cross-Platform Ruby & Rails IDE — The 2021.1 release is just around the corner. Learn about the new features for Ruby 3 and RBS support.

JetBrains sponsor

Lazy-Loading Content with Turbo Frames and Skeleton Loader — A solid and concise example of using Turbo Frames to dynamically load content into part of a page.

Matt Swanson

Anything I Want With Sequel and Postgres — Janko demonstrates how the Sequel gem supports advanced features of Postgres, allowing them to bulk import CSV files into a partitioned set of tables that required some obscure PG knowledge.

Janko Marohnić

Timecop vs Rails TimeHelpers — Did you know that Rails has its own TimeHelpers a la Timecop? As Matt demonstrates, you probably won’t be replacing Timecop in your Rails app in the near future (pun intended).

Matt Bearman

Why Write Rails View Tests — Writing integration tests to cover the branches in your views can be time consuming so view tests provide an alternative, says the author.

Nikola Đuza

How I Code Without Service Objects — If you feel like service objects have grown beyond their utility, then Jason’s post will sing to you while showing you how he uses plain-old OOP to get things done.

Jason Swett

Verifying JWTs with JWKS in Ruby — Some best practices around fetching and caching the key set and mitigating other security concerns with JWTs.

Unathin Chonco

🛠 Code and Tools

Marcel 1.0: Find the MIME Type of Files from Their Contents and/or Filename — If this project sounds familiar, it’s because it was at the center of the MimeMagic/GPL story last week – it now uses a Apache-licensed dataset instead and has dropped the MimeMagic dependency. It’s used by Rails but you can use it just as well from any Ruby code of your own for detecting filetypes.

Ruby on Rails Project

http-clients-benchmark: Benchmarks of Well-known Ruby HTTP Clients — The creator of HTTPX made this repository to keep his “assumptions honest” around how his gem performs against its competition.

Honeyryderchuck

Complete Peace of Mind Rails Hosting

OpsCare by reinteractive sponsor

SimpleDiscussion 1.3: A Simple, Extensible Rails Forum — We first linked this a year ago but it’s just had its first update in a while. I like the feel of the forums it creates.

Chris Oliver

pwned: A Ruby Wrapper for the Pwned Passwords API — Check if a password has been found in any of the huge data breaches.

Phil Nash

devise-two-factor: Barebones Two-Factor (TOTP) Authentication with Devise

Tinfoil Security

Torch.rb 0.6.0: Deep Learning for Ruby — A way to use Torch, an open source machine learning framework (more commonly associated with Python.)

Andrew Kane

💡 Tip of the Week

Effective use of Array#bsearch

Binary search is a commonly taught algorithm for finding an element of a sorted array in O(log(n)) time where n is the size of the array. In the context of an array, it takes a sorted array, makes a comparison to a middle element, and then based on that comparison recurses in either the right or left 'half' of the array until it either finds an element where a comparison returns true, or has no more elements to look over.

Ruby has an Array#bsearch method to return an element selected by a binary search. Its functionality can be a little tricky to grasp at first. Take a look at this example:

["tip", "of", "the", "week"].sort.bsearch { _1 == "of" }
=> nil

Initially, this result may be counterintuitive. Let's dig into why this happens, so we can effectively use Array#bsearch going forward!

There are assumptions Array#bsearch makes about the array it's searching, and the condition on which it's searching:

  • The array is sorted.
  • There are two types of comparisons we can make with the condition against elements of the array:
    • If boolean: all false evaluations precede all true evaluations.
    • If integer: all positive evaluations precede all zero evaluations which precede all negative evaluations.

It doesn't check these assumptions, however, and can't guarantee results when these assumptions aren't met. So if we look again at our example from above, we see it meets the first assumption: the array is sorted. But on the second assumption, it has boolean results which do not all evaluate to false before true. Let's take a closer look:

sorted_array = ["tip", "of", "the", "week"].sort
=> ["of", "the", "tip", "week"]

# This violates the second assumption because `false`
# evaluations don't always precede `true` evaluations
sorted_array.map { _1 == "of" }
=> [true, false, false, false]

# There are a few options for how we could fix this:

# If we reverse the array, it'll work
sorted_array.reverse.map { _1 == "of" }
=> [false, false, false, true]

# Tada!
sorted_array.reverse.bsearch { _1 == "of" }
=> "of"

# Or we can use the <=> operator which returns 1,0,-1
# Note we need "of" <=> _1 to hold the ordering property we seek
sorted_array.map { "of" <=> _1 }
=> [0, -1, -1, -1]

# Tada!
sorted_array.bsearch { "of" <=> _1 }
=> "of"

And we can see that as long as both of the assumptions hold, we can rely on Array#bsearch to efficiently and effectively search an array.

An added bonus, if we're just looking for the index of an element selected by binary search, we can use Array#bsearch_index with the same block as above:

sorted_array.bsearch_index { "of" <=> _1 }
=> 0

This week’s tip was written by Jemma Issroff.