Archive – Page 5

Everything You Need to Know About Destructuring in Ruby 3

Credit: Kiwihug on Unsplash

Welcome to our first article in a series all about the exciting new features in Ruby 3! Today we’re going to look how improved pattern matching and rightward assignment make it possible to “destructure” hashes and arrays in Ruby 3—much like how you’d accomplish it in, say, JavaScript—and some of the ways it goes far beyond even what you might expect. December 2021: now updated for Ruby 3.1 — see below!

First, a primer: destructuring arrays #

For the longest time Ruby has had solid destructuring support for arrays. For example:

a, b, *rest = [1, 2, 3, 4, 5]
# a == 1, b == 2, rest == [3, 4, 5]

So that’s pretty groovy. However, you haven’t been able to use a similar syntax for hashes. This doesn’t work unfortunately:

{a, b, *rest} = {a: 1, b: 2, c: 3, d: 4}
# syntax errors galore! :(

Now there’s a method for Hash called values_at which you could use to pluck keys out of a hash and return in an array which you could then destructure:

a, b = {a: 1, b: 2, c: 3}.values_at(:a, :b)

But that feels kind of clunky, y’know? Not very Ruby-like.

So let’s see what we can do now in Ruby 3!

Introducing rightward assignment #

In Ruby 3 we now have a “rightward assignment” operator. This flips the script and lets you write an expression before assigning it to a variable. So instead of x = :y, you can write :y => x. (Yay for the hashrocket resurgence!)

What’s so cool about this is the smart folks working on Ruby 3 realized that they could use the same rightward assignment operator for pattern matching as well. Pattern matching was introduced in Ruby 2.7 and lets you write conditional logic to find and extract variables from complex objects. Now we can do that in the context of assignment!

Let’s write a simple method to try this out. We’ll be bringing our A game today, so let’s call it a_game:

def a_game(hsh)
  hsh => {a:}
  puts "`a` is #{a}, of type #{a.class}"
end

Now we can pass some hashes along and see what happens!

a_game({a: 99})

# `a` is 99, of type Integer

a_game({a: "asdf"})

# `a` is asdf, of type String

But what happens when we pass a hash that doesn’t contain the “a” key?

a_game({b: "bee"})

# NoMatchingPatternError ({:b=>"bee"})

Darn, we get a runtime error. Now maybe that’s what you want if your code would break horribly with a missing hash key. But if you prefer to fail gracefully, rescue comes to the rescue. You can rescue at the method level, but more likely you’d want to rescue at the statement level. Let’s fix our method:

def a_game(hsh)
  hsh => {a:} rescue nil
  puts "`a` is #{a}, of type #{a.class}"
end

And try it again:

a_game({b: "bee"})

# `a` is , of type NilClass

Now that you have a nil value, you can write defensive code to work around the missing data.

What about all the **rest? #

Looking back at our original array destructuring example, we were able to get an array of all the values besides the first ones we pulled out as variables. Wouldn’t it be cool if we could do that with hashes too? Well now we can!

{a: 1, b: 2, c: 3, d: 4} => {a:, b:, **rest}

# a == 1, b == 2, rest == {:c=>3, :d=>4}

But wait, there’s more! Rightward assignment and pattern matching actually works with arrays as well! We can replicate our original example like so:

[1, 2, 3, 4, 5] => [a, b, *rest]

# a == 1, b == 2, rest == [3, 4, 5]

In addition, we can do some crazy stuff like pull out array slices before and after certain values:

[-1, 0, 1, 2, 3] => [*left, 1, 2, *right]

# left == [-1, 0], right == [3]

Rightward assignment within pattern matching 🤯 #

Ready to go all Inception now?!

freaky folding city GIF

You can use rightward assignment techniques within a pattern matching expression to pull out disparate values from an array. In other words, you can pull out everything up to a particular type, grab that type’s value, and then pull out everything after that.

You do this by specifying the type (class name) in the pattern and using => to assign anything of that type to the variable. You can also put types in without rightward assignment to “skip over” those and move on to the next match.

Take a gander at these examples:

[1, 2, "ha", 4, 5] => [*left, String => ha, *right]

# left == [1, 2], ha == "ha", right == [4, 5]

[8, "yo", 12, 14, 16] => [*left, String => yo, Integer, Integer => fourteen, *
right]

# left == [8], yo == "yo", fourteen == 14, right == [16]

Powerful stuff!

And the pièce de résistance: the pin operator #

What if you don’t want to hardcode a value in a pattern but have it come from somewhere else? After all, you can’t put existing variables in patterns directly:

int = 1

[-1, 0, 1, 2, 3] => [*left, int, *right]

# left == [], int == -1 …wait wut?!

But in fact you can! You just need to use the pin operator ^. Let’s try this again!

int = 1

[-1, 0, 1, 2, 3] => [*left, ^int, *right]

# left == [-1, 0], right == [2, 3]

You can even use ^ to match variables previously assigned in the same pattern. Yeah, it’s nuts. Check out this example from the Ruby docs:

jane = {school: 'high', schools: [{id: 1, level: 'middle'}, {id: 2, level: 'high'}]}

jane => {school:, schools: [*, {id:, level: ^school}]}

# id == 2

In case you didn’t follow that mind-bendy syntax, it first assigns the value of school (in this case, "high"), then it finds the hash within the schools array where level matches school. The id value is then assigned from that hash, in this case, 2.

So this is all amazingly powerful stuff. Of course you can use pattern matching in conditional logic such as case which is what all the original Ruby 2.7 examples showed, but I tend to think rightward assignment is even more useful for a wide variety of scenarios.

“Restructuring” for hashes and keyword arguments in Ruby 3.1 #

New with the release of Ruby 3.1 is the ability to use a short-hand syntax to avoid repetition in hash literals or when calling keyword arguments.

First, let’s see this in action for hashes:

a = 1
b = 2
hsh = {a:, b:}

hsh[:a] # 1
hsh[:b] # 2

What’s going on here is that {a:} is shorthand for {a: a}. For the sake of comparison, JavaScript provides the same feature this way: const a = 1; const obj = {a}.

I like {a:} because it’s a mirror image of the hash destructuring feature we discussed above. Let’s round-trip-it!

hsh1 = {xyz: 123}

hsh1 => {xyz:}

# now local variable `xyz` equals `123`

hsh2 = {xyz:}

# hsh2 now equals `{:xyz=>123}`

Better yet, this new syntax doesn’t just work for hash literals. It also works for keyword arguments when calling methods!

def say_hello(first_name:)
  puts "Hello #{first_name}!"
end

# elsewhere…

first_name = "Jared"

say_hello(first_name:)

# Hello Jared!

Prior to Ruby 3.1, you would have needed to write say_hello(first_name: first_name). Now you can DRY up your method calls!

Another goodie: the values you’re passing via a hash literal or keyword arguments don’t have to be merely local variables. They can be method calls themselves. It even works with method_missing!

class MissMe
  def print_message
    miss_you(dear:)
  end

  def miss_you(dear:)
    puts "I miss you, #{dear} :'("
  end

  def method_missing(*args)
    if args[0] == :dear
      "my dear"
    else
      super
    end
  end
end

MissMe.new.print_message

# I miss you, my dear :'(

What’s happening here is we’re instantiating a new MissMe object and calling print_message. That method in turn calls miss_you which actually prints out the message. But wait, where is dear actually being defined?! print_message certainly isn’t defining that before calling miss_me. Instead, what’s actually happening is the reference to dear in print_message is triggering method_missing. That in turn supplies the return value of "my dear".

Now this all may seem quite magical, but it would have worked virtually the same way in Ruby 3.0 and prior—only you would have had to write miss_you(dear: dear) inside of print_message. Is dear: dear any clearer? I don’t think so.

In summary, the new short-hand hash literals/keyword arguments in Ruby 3.1 feels like we’ve come full circle in making both those language features a lot more ergonomic and—dare I say it—modern.

Conclusion #

While you might not be able to take advantage of all this flexibility if you’re not yet able to upgrade your codebase to v3 of Ruby, it’s one of those features I feel you’ll keenly miss after you’ve gotten a taste of it, just like keyword arguments when they were first released. I hope you enjoyed this deep dive into rightward assignment and pattern matching! Stay tuned for further examples of rightward assignment and how they improve the readability of Ruby templates. small red gem symbolizing the Ruby language



Ruby on the Frontend? Choose Your Weapon

Credit: Meritt Thomas on Unsplash

We all know that Ruby is a great language to use for the backend of your web application, but did you know you can write Ruby code for the frontend as well?

Not only that, but there are two available options to choose from when looking to “transpile” from Ruby to Javascript. These are:

Let’s take a quick peek at each one and see what might be right for your project.

Ruby2JS #

My personal favorite, Ruby2JS was created by Sam Ruby (yep, that’s his name), and it is intended to convert Ruby-like syntax to Javascript as cleanly and “natively” as possible. This means that (most of the time) you’ll get a line-by-line, 1:1 correlation between your source code and the JS output. For example:

class MyClass
  def my_method(str)
    ret = "Nice #{str} you got there!"
    ret.upcase()
  end
end

will get converted to:

class MyClass {
  myMethod(str) {
    let ret = `Nice ${str} you got there!`;
    return ret.toUpperCase()
  }
}

There’s actually a lot going on here so let me unpack it for you:

How do you get started using Ruby2JS? It’s pretty simple: if you’re using a framework with Webpack support (Rails, Bridgetown), you can add the rb2js-loader plugin along with the ruby2js gem, write some frontend files with a .js.rb extension, and import those right into your JS bundle. It even supports source maps right out of the box so if you have any errors, you can see the original Ruby source code right in your browser’s dev inspector!

Full disclosure: I recently joined the Ruby2JS team and built the Webpack loader, so let me know if you run into any issues and I’ll be glad to help!

Opal #

The Opal project was founded by Adam Beynon in 2012 with the ambitious goal of implementing a nearly-full-featured Ruby runtime in Javascript, and since then it has grown to support an amazing number of projects, frameworks, and use cases.

There are plenty of scenarios where you can take pretty sophisticated Ruby code, port it over to Opal as-is, and it just compiles and runs either via Node or in the browser which is pretty impressive.

Because Opal implements a Ruby runtime in Javascript, it adds many additional methods to native JS objects (strings, integers, etc.) using a $ prefix for use within Opal code. Classes are also implemented via primitives defined within Opal’s runtime layer. All this means that the final JS output can sometimes look a little closer to bytecode than traditional JS scripts.

For instance, the above example compiled via Opal would result in:

/* Generated by Opal 1.0.3 */
(function(Opal) {
  var self = Opal.top, $nesting = [], nil = Opal.nil, $$$ = Opal.const_get_qualified, $$ = Opal.const_get_relative, $breaker = Opal.breaker, $slice = Opal.slice, $klass = Opal.klass;

  Opal.add_stubs(['$upcase']);
  return (function($base, $super, $parent_nesting) {
    var self = $klass($base, $super, 'MyClass');

    var $nesting = [self].concat($parent_nesting), $MyClass_my_method$1;

    return (Opal.def(self, '$my_method', $MyClass_my_method$1 = function $$my_method(str) {
      var self = this, ret = nil;

      
      ret = "" + "Nice " + (str) + " you got there!";
      return ret.$upcase();
    }, $MyClass_my_method$1.$$arity = 1), nil) && 'my_method'
  })($nesting[0], null, $nesting)
})(Opal);

Thankfully, Opal too has support for source maps so you rarely need to look at anything like the above in day-to-day development—instead, your errors and debug output will reference clean Ruby source code in the dev inspector.

One of the more well-known frameworks using Opal is Hyperstack. Built on top of both Opal and React, Hyperstack lets you write “isomorphic” code that can run on both the server and the client, and you can reason about your web app using a well-defined component architecture and Ruby DSL.

Conclusion #

As you look at the requirements for your project, you can decide whether Ruby2JS or Opal might suit your needs.

Regardless of which you choose, it’s exciting to know that we can apply our Ruby knowledge to the frontend as well as the backend for web applications large and small. It’s a great day to be a Rubyist. small red gem symbolizing the Ruby language



If Ruby Had Imports…

Credit: Maksym Kaharlytskyi on Unsplash

Here is some example code from a Rails controller in the widely-used Discourse forum software:

class BadgesController < ApplicationController
  skip_before_action :check_xhr, only: [:index, :show]
  after_action :add_noindex_header

  def index
    raise Discourse::NotFound unless SiteSetting.enable_badges

    badges = Badge.all

    if search = params[:search]
      search = search.to_s
      badges = badges.where("name ILIKE ?", "%#{search}%")
    end

    if (params[:only_listable] == "true") || !request.xhr?
      # NOTE: this is sorted client side if needed
      badges = badges.includes(:badge_grouping)
        .includes(:badge_type)
        .where(enabled: true, listable: true)
    end

    badges = badges.to_a

    user_badges = nil
    if current_user
      user_badges = Set.new(current_user.user_badges.select('distinct badge_id').pluck(:badge_id))
    end
    serialized = MultiJson.dump(serialize_data(badges, BadgeIndexSerializer, root: "badges", user_badges: user_badges, include_long_description: true))
    respond_to do |format|
      format.html do
        store_preloaded "badges", serialized
        render "default/empty"
      end
      format.json { render json: serialized }
    end
  end

  # and more actions here...
end

Now, if you’re looking at this code coming from a JavaScript/TypeScript background—or a number of other programming languages—the first thing you might immediately think is:

Where are all the import statements??

That’s right, there’s nary an import statement to be found! Where does ApplicationController come from? SiteSetting? Badge? Heck, even MultiJson? How is this all just accessible without requiring it somehow?!

Ah my friend—welcome to the wonderful world of Ruby autoloading.

How to Acquire an Instinctual Hatred of Explicit Import Statements #

Step 1: write Rails apps full-time for several years.

Step 2: go peek at the top of a file written for virtually any large NodeJS framework.

Step 3: 🤢

Look, I don’t mean to pick on poor JavaScript. When you’re trying to write performant code for eventual download to a browser where you need to keep the bundle sizes lean and mean, you want to import and export and tree-shake and chunk-split and do everything you can do to avoid megabytes of unnecessary code clogging up the wires.

But riddle me this: why do you need 20 import statements at the top of a file…in a server environment??

Excuse me, what does NodeJS need with a small bundle size?

If you would indulge me for a moment, let’s imagine a world where you had to import all of the objects and functions needed in each file in your Rails application. Revisiting the example above, it might look something like this:

import ApplicationController from "./application_controller"
import { skip_before_action, after_action, params, respond_to, format } from "@rails/actionpack"
import Discourse from "../lib/global/discourse"
import SiteSetting from "../models/site_setting"
import Badge from "../models/badge"
import MultiJson from "@intridea/multi_json"

class BadgesController < ApplicationController
  # etc...
end

And that’s just for a single controller action! 🤪

This leaves us with only one question: since your Ruby on Rails code obviously doesn’t have to import/require anything for it to work, how does it do that? How does it know how to simply autoload all these objects?

Introducing Zeitwerk #

Actually, before we dive into Zeitwerk, let’s quickly review built-in Ruby autoloading.

Ruby comes out of the box with a form of autoloading attached to Module. You can use this in any Ruby program you write:

# my_class.rb
module MyModule
  class MyClass
  end
end

# main.rb
module MyModule
  autoload :MyClass, "my_class.rb"
end

MyModule::MyClass.new # this triggers the autoload

This is handy in a pinch, but for larger applications or gems and particularly for Rails, you need something that’s broader-reaching and more easily configurable—plus supports concepts like “eager loading” and “reloading” (in development).

That’s where Zeitwerk comes in.

With Zeitwerk, you can define one or more source trees, and within that tree, as long as your Ruby constants (modules and classs) and hierarchy thereof match the file names and folder structure via a particular convention, it all just works. Magic!

Here’s an example from the readme:

lib/my_gem.rb         -> MyGem
lib/my_gem/foo.rb     -> MyGem::Foo
lib/my_gem/bar_baz.rb -> MyGem::BarBaz
lib/my_gem/woo/zoo.rb -> MyGem::Woo::Zoo

And here’s how you instantiate a Zeitwerk loader. It’s incredibly easy!

loader = Zeitwerk::Loader.new
loader.push_dir("lib")
loader.setup # ready!

Once you’ve instantiated a Zeitwerk loader, at any point in the execution of your Ruby program after that setup is complete you can call upon any class/module defined within that loader’s source tree and Zeitwerk will automatically load the class/module.

In addition, if you use the loader.eager_load method, you can load all of the code into memory at once. This is preferred in production for performance reasons: once your app first boots, it doesn’t have to load anything else later. On the other hand, in development you want to be able to reload code if it’s changed and run it without having to terminate your app and boot it up again. With the loader.reload method, Zeitwerk supports that too!

You may be surprised to hear that Zeitwerk is somewhat new to the Ruby scene (Rails used a different autoloader before it and there have been other techniques in that vein over time). What makes Zeitwerk so cool is how easy it is to integrate into any Ruby app or gem. I myself am starting to integrate it into Bridgetown now. The only caveat is you do need to be a little strict with how you structure your source files and folders and what you name within those files. But once you do that, it’s a cinch.

Still a Use for require Though #

Even with Zeitwerk on the loose, you’ll still need to use a require statement now and then to load Ruby code from a gem or some other random file you’ve pulled into your project. But the nice thing is that Ruby’s require doesn’t work the way that import does in JavaScript. It simply adds the requested file/gem to the current execution scope of your program and then it’s available everywhere proceeding from that point. So if you add require to a main or top-level file within your application codebase, there’s no need to then “import Foo from "bar"” later on in file B and “import Foo from "bar"” in file C all over again.

This does mean that you may have to fish a bit to find out where MyObscureClassName.what_the_heck_is_this actually comes from. This is likely how some of the “argh, Ruby is too magical!” sentiment out there arises. But given a choice between Ruby magic, and JS import statement soup at the top of Every. Single. Darn. File. In. The. Entire. Codebase…

…well, I believe in magic. Do you? small red gem symbolizing the Ruby language



Where Do Ruby Blocks Come From?

Credit: Xavi Cabrera on Unsplash

It’s time to get reflective…time for some deep introspection…so light a candle or two, put some Barry White on the stereo, get nice and comfortable, because we’re going to talk about Blocks.

Blocks in Ruby are powerful, and they’re used everywhere. You can pass blocks implicitly to enumerable methods like map and select simply by adding { ... } or do ... end after the method name. You can explicitly create closures (aka anonymous functions) by using proc, lambda, or ->() semantics and pass them around as method arguments or store them as variables. Yes, blocks are pretty magical.

But we’re not here to talk about how to write blocks per se or what they’re good for. We’re here to talk about where they come from.

What do you mean, where they come from? They come from programmers, silly!

Well, obviously…but I don’t mean how they originate in the minds of intrepid Rubyists everywhere — I mean where do they come from in the execution context of your Ruby program?

Oh OK. Yes, I’d like to know that too.

Awesome sauce! So let’s dive into what exactly happens when a block is first created.

Behold the Mighty Binding! #

When you write a block, you aren’t merely defining some lines of code that will get executed later. You’re also creating a binding. A binding is the execution context in which the block will eventually be executed. It consists of:

Bindings are actually instances of the Binding class, which means you can inspect the binding of any block’s Proc instance you have access to.

Wait wut?

Yep, in Ruby virtually everything is an object, even blocks (in the form of Proc instances). If you’re coming from a less dynamic language (say, Javascript), prepare to have your mind blown! Here’s an example:

abc = 123
block = proc {}
block.binding.local_variable_get(:abc) # 123

xyz = 987
block.binding.local_variable_get(:xyz) # NameError (local variable `xyz' is not defined for #<Binding>)

block.binding.local_variables # [:block, :abc, ...]

The reason the first variable (abc) was part of the binding and the second (xyz) wasn’t is because blocks inherit their parent scope at the point where the block is defined, but anything added in that scope later isn’t accessible from within the block. This is sometimes referred to as “lexical scope”.

However — and this is important to note — changes to existing variables are accessible. Check this out:

abc = 123
block = proc { puts abc }
block.binding.local_variable_get(:abc) # 123
block.call # 123

abc = 456
block.binding.local_variable_get(:abc) # 456
block.call # 456

The binding stores references to local variables by name (i.e., it doesn’t copy any variables), so when your variable was reassigned with a different value later, the block could still access the new value.

Of course, you can also reassign variables within a block and the new values are accessible outside the block:

abc = 1
proc { abc = 2 }.call

puts abc # 2

But what if you want to “hide” a local variable from inside a block — i.e., the variable isn’t included in the block scope and changes don’t propagate back up to the parent scope? Never fear! You can create what are called “block-local variables” by listing them in the block’s parameter list, preceded by a semicolon:

abc = 1
proc do |;abc|
  puts abc.nil? # true
  abc = 2
  puts abc
end.call # 2

puts abc # 1

So that’s pretty cool! But here’s where things get a little confusing: if you call binding.local_variable_get(:abc) on the proc even when specifying abc as a block-local variable, you get back the value 1, not nil. I guess that’s because the binding is telling you about the context the block has been bound to, not necessarily what the exact state of affairs will be inside the block. If you know of a way to introspect block-local variables through the Binding object, please let me know!

It is Better to Give Than to Receiver #

Another thing to take a look at is the receiver of the binding. Any method calls you make implicitly or explicitly to self, as well as any instance variables you access, will all be bound to the receiver. When you’re testing this out in IRB or in a basic Ruby script, the receiver will be the main object (unless you are inside of another object). Here’s an example:

block = proc {}
puts block.binding.receiver # main

class MyObject
  def receiver
    block = proc {}
    block.binding.receiver # return current scope's self, aka MyObject instance
  end
end

puts MyObject.new.receiver.class # MyObject

Now here’s where things get really trippy. Ruby lets you change the receiver of a block! Yep, that’s right: you can swap one receiver out for another and when you execute the block its self will be different than the originally bound receiver.

abc = 123

block = proc do
  puts abc
  puts self.xyz # explicit so you can see what's going on
end

block.call # NoMethodError (undefined method `xyz' for main:Object)

class MyObject
  def xyz
    456
  end
end

obj = MyObject.new
block.binding.receiver = obj
block.call

Run that and…oh wait, oops, that doesn’t work! 🙁 That’s because there is no receiver= method for binding like you might expect. Fortunately, there’s another way to go about things (in Ruby there almost always is!). We can use the instance_exec method of the object itself. Let’s fix the code and try again:

abc = 123

block = proc do
  puts abc
  puts self.xyz
end

block.call # still causes an error…but wait!

class MyObject
  def xyz
    456
  end
end
obj = MyObject.new

# Time to try out instance_exec!
obj.instance_exec(&block)
# output: 123
#         456

That works! 😃👍

So using instance_exec is very similar to using call, only you pass the block in as the first argument (just make sure to include the ampersand in front of the block variable). Any additional arguments will be passed to the block itself, same as any arguments you would give call. When you use instance_exec to execute the block proc, it’s then able to access the xyz method of obj — whereas before there was no xyz method available. In addition, even if you use instance_exec, the block still has access to the original local variables (abc) as part of its binding.

If you wanted to get really fancy with Ruby-fu metaprogramming, you could store the block’s bound receiver in a variable, then use instance_exec in combination with method_missing so that method calls in the context of the new object would actually end up shadowing those original receiver’s methods. Why on earth would you want to do this? Let’s just say I have a story to tell you…but we’ll save that for a later date. 😄

Summary #

So there you have it: Ruby blocks are fun and weird and can do so much, yet they ask so little of us in return. May we learn to appreciate how much work they have to do under the hood to make it all seem so easy. And now that you know more about bindings, lexical scope, and the wizard-like power of instance_exec, you too can have precise control over exactly what’s going on as you wield (and yield) procs and lambdas like a mahōtsukai. small red gem symbolizing the Ruby language



Why the Release of Ruby 3 Will Be Monumental

Credit: Johannes Groll on Unsplash

We’ve been living in the shadow of Ruby 2 for seven years now. Seven! Ruby 2 was released in 2013 (which incidentally is the same year as the initial public release of React 0.3.0!).

In that span of time, Ruby performance has improved significantly and many, many enhancements to the language have benefited a great many people and projects. We’ve seen companies using Ruby and in many cases Rails become bedrocks of developer and consumer internet infrastructure. GitHub. Shopify. Stripe. Square. AirBnB.

But there has also been some consternation along the way. Is Ruby really a top-tier programming language able to compete with the likes of Javascript, Python, PHP, Go, and beyond? Or was it just a DHH-fueled hype-cycle doomed to inevitable relative obscurity as other technologies and frameworks ascended in its wake? (I don’t actually believe anyone seriously thinks this any more, but you still see the stray head-scratcher whiz by on Hacker News.)

Now we are mere weeks away from a major new Ruby release: version 3. While Ruby 3 is an exciting update with lots of features that make it interesting both now and in the future with various point updates promising even more goodies, I think it’s the psychology of turning over from major version 2 to 3 that is most vital to the future health of the community.

Ruby 3 isn’t just a new version. It’s a new era.

What does this era represent? Let’s list a few talking points I hope we’ll start to push hard and often as Rubyists:

Ruby 3 is Fast #

No, I don’t mean Ruby 3 suddenly got a whole lot faster than Ruby 2.7. I mean that Ruby 3 is fast compared to Ruby 2. It’s unfortunate that much of the “Ruby is slow” meme has been a laggard perspective stemming from people’s experiences years ago with the language, or an old version of Rails, or Jekyll, or…the fact is it just wasn’t the zippy experience we’re pleased to enjoy today.

Do we still want even better performance? Of course! But at this point, Ruby is plenty fast as compared to many other “scripting” languages. Most of the time it’s on par with Python. It’s even on par with Javascript. (What? Don’t believe me? Check out how similar Jekyll and Eleventy perform as static site generators.) And as Nate Berskopec often reminds us, your Rails app can perform quite well with just a bit of fine-tuning, and often the typical bottlenecks lie elsewhere in the stack (database, web server, etc.)

Ruby 3 is Easy #

These days, you don’t need to wrestle with gem dependency hell or pray to the gods to get Ruby or a Ruby extension to compile. That was “old Ruby”. New Ruby is using a fancy-pants version manager like rbenv combined with Bundler 2.

It just works.

Truly, Ruby is the first thing I install on any new Mac or Linux machine I operate and getting things set up is a piece of cake. Installing Rails. Installing Bridgetown. Installing…whatever. It. Just. Works.

We also have things like Docker and WSL to make things much easier to accomplish on Windows machines if you get stuck wrestling with Win-native Ruby. Heck, you can upload your entire dev environment into the cloud now and use VSCode with remote extensions.

Are there ways Bundler and the ecosystem around Ruby versions/dependencies could be improved? No doubt. But it’s in no way any more complicated or fiddly than the world of npm/yarn, and you don’t see the angry hordes trying to burn down the barn doors over there (except maybe the Deno folks 😉).

Ruby 3 is Sleek #

Ruby isn’t the best choice for all problem domains. It just isn’t. But when it comes to “standard” web development, it often is the best choice. It really is! Spend a few days writing NestJS + TypeORM Typescript code and then come back to Rails. It’s like a breath of fresh, sweet air. And that’s not just when you’re writing controllers or models…it goes all the way up and down the stack.

Ruby just makes everything better. Less code. Less boilerplate. Less ceremony. More streamlined. More properly object-oriented. More polished and pleasurable to read and write. Certainly one could posit there are other web frameworks/languages which have much going for them as well. Laravel is popular with PHP devs, and for good reason. Django is popular with Pythonistas. But can anyone say with a straight face that, all things being equal, PHP is a “superior” programming language to Ruby? Can anyone say that Python—taken as a whole—is more suited to building a website than Ruby is?

I think not. While Ruby wasn’t originally invented as a way to supercharge web development, it found its niche in the rise of such amazing projects as Rails, Rack, Jekyll, plus great APIs by Stripe and many others. It rode much of the early wave of Web 2.0 hits, and that heritage continues to benefit us today.

Ruby 3 is Here to Stay #

Ruby 3 isn’t just another notch on the belt of recent Ruby releases. It’s Ruby 3.0. That means we can look forward to 3.1, 3.2, 3.3, and beyond. This is the beginning of a whole new era. New innovations. New patterns. Exciting ideas fusing concepts from other technologies with The Ruby Way. Fresh blood coming into the ecosystem. (Anecdotally, I’m seeing newbies plus returning old-timers jumping into Ruby-based forums and chat rooms all the time, and the pace of interesting new Ruby gems bursting onto the scene finally seems to be increasing after a few years of ho-hum incremental progress.)

The takeaway is this: Ruby 3 represents a moment when we should stand proud as Rubyists and unabashedly proclaim to the bootcamps and engineering departments of the world that we’re open and ready to do business large and small. Sure you could pick something other than Ruby with which to build the next great internet success story. But you’ll definitely be in good company if you do pick Ruby. After all, it’s more likely than not your code will be living in a repository overseen by Ruby (GitHub), you’ll be communicating with your fellow colleagues via Ruby (Basecamp & HEY), you’ll be asking for support via Ruby (Discourse forums), you’ll be researching the latest developer news and techniques via Ruby (Dev.to), and you’ll be spinning up your dev machine while wearing that l33t geek t-shirt you got from an indie vendor via Ruby (Shopify)—that is, after you paid for it via Ruby (Stripe). And when you’re exhausted from all that coding and need to unwind at a private cottage by the beach, Ruby will help you out there too (AirBnB).

Excelsior!



Gsub Blocks, Partitions, and StringScanners, Oh My!

Credit: Tara Evans on Unsplash

It should come as no surprise that Ruby gives you a lot of flexibility right out of the box when it comes to manipulating text. After all, it originated in the 90s when Perl was on the ascension, and Matz took inspiration from that language which is famous for its text processing prowess.

I’ve needed to do a fair bit of parsing work lately, and as part of that I’ve become more familiar with some of the ins and outs of using Regular Expressions to seek through text to find and possibly replace tokens. This is by no means an exhaustive resource, but it should provide you with a general idea of what’s possible in your day-to-day Ruby programming.

Gsub #

If you need to do a search and replace in one or more places throughout your string, gsub is typically the way to go. I think most Rubyists will discover this method pretty early on when learning about string manipulation.

What I didn’t know until recently is you can pass a block to gsub. For each match in the string, the block will be evaluated and the return value will be the replacement for that match. This means you can write code that will determine the replacement values conditionally based on what exactly is getting matched!

For example, if you wanted to change <div> tags to <span> tags, but only if there are no attributes, you could write something like this:

"<div>This is a string</div>" \
"<div class='centered'>This is another string</div>"
  .gsub(/(<.*?[ >])(.*?)(<\/.*?>)/) do |match|
    if $1.end_with?(" ")
      match
    else
      "<span>#{$2}</span>"
    end
  end

# <span>This is a string</span><div class='centered'>This is another string</div>

(Now this isn’t a great example because it doesn’t handle nested tags, but you get the idea…)

In case you’re not familiar with capture groups, the $1 and $2 are referencing the first capture group which is an opening tag (aka <div>) and the second capture group which is the text inside the tag.

gsub also lets you provide a hash where matches will be replaced by the values of matched keys:

"Foo is the nicest bar you'll ever meet."
  .gsub(/Foo|bar/, "Foo" => "Joe", "bar" => "guy")

# Joe is the nicest guy you'll ever meet.

I suspect the block syntax is ultimately of more value though.

Partition #

The partition method lets you divide a string into three pieces: the part of the string before a single match, the match itself, and everything that comes after that match. If you include capture groups in your regular expression, you can utilize those as well. One way you can take advantage of this type of data is by using partition to search a string for tokens, and build a new string up via a buffer as you transform the tokens.

Let’s say you want to be able to put colons around words where you’d like the word length to appear as a kind of footnote after the world. You want text :like: this to turn into text like(4) this.

Here’s how you could write it using partition, a buffer, and an until loop:

string = "This is :something: you'll :want: to try :out: for yourself."
buffer = ""

until string.empty?
  text, token, string = string.partition(/ :(.*?): /)

  buffer << text

  if token.length.positive?
    buffer << " #{$1}"
    buffer << "(#{$1.length}) "
  end
end

puts buffer

# This is something(9) you'll want(4) to try out(3) for yourself.

Now, is this something you could do with a gsub block as described previously? Yes indeed:

string = "This is :something: you'll :want: to try :out: for yourself."
string.gsub!(/ :(.*?): /) do
  " #{$1}(#{$1.length}) "
end

puts string

In fact that’s a lot simpler. However, in this example you don’t have access to any of the text before or after the token. If that’s something that’s important to you (maybe you need to process the token differently depending on what comes before it, or after it), you’ll want to use partition.

Or will you?? There is another way!

StringScanner #

Using StringScanner is like bringing a bazooka to a paintball tournament. It’s extraordinarily powerful, but it can also land you in some serious trouble—not to mention get a little mind bend-y if you’re not careful.

StringScanner is actually the name of a Ruby class in the standard library (stdlib), which you’ll need to import by adding require "strscan" to the top of your code. You use it by instantiating a scanner with a string, and then you use various methods to scan the string for patterns and advance a “pointer”.

Let’s say you want to replace “cake” with “pie” in a string, but not if the keyword is preceded by “short” or if it’s followed by “pops”. We’ll use a buffer and do string replacement like in previous examples, but because we have all the benefits of a scanner it’s pretty easy to look backwards and forwards and determine our next course of action.

require "strscan"

string = "Let them eat cake and then more shortcake and finally cake pops!"
scanner = StringScanner.new(string)
buffer = ""

until scanner.eos?
  portion = scanner.scan_until(/cake/)
  if portion.nil?
    buffer << scanner.rest
    scanner.terminate
    next
  end
  unless scanner.pre_match =~ /short$/ or scanner.check(/\s+pops/)
    buffer << portion.sub(/cake/, "pie")
  else
    buffer << portion
  end
end

puts buffer

# Let them eat pie and then more shortcake and finally cake pops!

Whoa, what’s going on here?

First, we set up an until scanner.eos? loop. This means the loop will iterate until we’ve reached the end of the string.

The scan_until method looks for a pattern and advances the current pointer to that location. (You can verify this by adding puts scanner.pointer below scan_until.) It returns the portion of the string that matches the pattern, so we can use that to perform string substitution to change “cake” to “pie”.

However, we don’t want to do the substitution if cake is preceeded immediately by “short”, so we’ll check for a regex match on everything that’s come before the portion (scanner.pre_match) to see if it ends with “short”. We also want to check if the very next part of the string is the word “pops”, so we’ll use the scanner.check method. This checks what comes immediately next in the string, but it doesn’t advance the pointer. (There’s also a check_until method which is analogous to scan_until.) By not advancing the pointer, we avoid messing up our position in the string and can continue looping normally.

The if portion.nil? block near the top of the loop handles the case where there are no more instances of “cake” in the string but there’s still more to the string we need to account for. By adding the .rest of the string to our buffer and calling scanner.terminate, we force the scanner to advance to the end of the string, in which case until scanner.eos? will evaluate true and end the loop.

This example is fairly simple because it’s only changing a single word to another word, so the substitution itself doesn’t require any fancy regex. But combine StringScanner with all of the techniques we’ve already learned (gsub blocks, even partition), and you’re able to build extremely sophisticated routines to handle nearly any kind of text processing imaginable.

Summary #

Whew, that’s a lot to take in! Today you’ve leaned that gsub is much more than just a way to say that “a” should become “b”. By supplying a block, you have precise control over the replacement strings by first inspecting each match of the source string.

In addition, the partition string method lets you divide a string into pre-match, match, and post-match components—and by doing so over and over in a loop and using a buffer, you can transform a large and complicated string section-by-section.

Finally, for the most precise control over searching text for one or more tokens and performing elaborate search-and-replace actions based on the relationships those tokens have with the rest of the text, the StringScanner object is there just waiting to unleash its full power. Not only that, your code can benefit from previous techniques in the midst of using StringScanner for maximum Ruby text processing prowess. small red gem symbolizing the Ruby language



Adding and Merging ActiveRecord Relations

Credit: Michael Dziedzic on Unsplash

An intriguing scenario arose in a project I was working on. In this application, creators can create plugin presets. Presets themselves are associated with “banks” — aka a bank has_many presets. There are also actions that can be taken against either banks or presets: users generally will bookmark them or download them. Thus these actions have a polymorphic association with both presets and banks.

What I wanted to do was package up all the actions users had performed for banks and related presets created by a particular creator. The resulting ActiveRecord query I arrived at expresses that well. Could this be refactored in a more performant way? (For example, using only one SQL query instead of three?) Very likely. But as this code is run only infrequently for reporting reasons, it’s a reasonable tradeoff between performance and readability.

Here’s the code for the ItemAction.for_creator(creator_profile) method:

class ItemAction < ApplicationRecord
  belongs_to :actionable, polymorphic: true
  enum action_type: [ :bookmark, :download, :publish ]

  def self.for_creator(creator_profile)
    banks = creator_profile.banks.published.select(:id)
    presets = Preset.where(bank: banks).select(:id)

    where(actionable: banks + presets).merge(
      ItemAction.bookmark.or(
        ItemAction.download
      )
    )
  end

  # other code, etc.
end

First, we get the list of banks. We use a published scope to limit the list to only banks which have been made available to the public. Also, we only need the id field back, not all the fields in the database table.

Second, we get the list of presets for those banks, again pulling in only the id field.

The last line of the method is where things get interesting. We want actions where the actionable association brings in both the banks and the presets we’ve loaded, so we use the + operator to concatenate both result sets together. Once we have that relation, we can use ActiveRecord’s merge functionality to pull in additional scopes that specify we only want either bookmark or download actions. We don’t want any other actions (say, publish), because those are actions taken by creators, not end users.

And that does the trick! Yay! A brief but useful example of the expressive power of adding and merging ActiveRecord relations together to get a final result.



The Archival Benefits of Static Site Generators

2020 Update: While I’m all in now on Bridgetown, a modern fork of Jekyll, I’m leaving this up since you can apply many of these same principles to Bridgetown as well.

I’ve been on a nostalgia trip lately, poring over old snapshots of various sites and blogs I worked on in the past (stretching all the way back to 1996). Thank goodness for the Wayback Machine! But it’s gotten me thinking about the impermanence of the digital artifacts we create all the time as designers, developers, and content authors.

All the work you’ve put into that app, that blog post, that video, that Instagram story…blink and it’s gone! In some cases that’s by design. Content that expires and is quickly forgotten has become desirable in certain circles, the artform being all about its “in the moment”-ness.

But in the instances where you want to preserve your content for posterity, the options become challenging. Let’s focus on blogging for the sake of this discussion. I’ve run a number of blogs over the years, and I deeply care about preserving those—at the very least for myself but also for my children and their children (etc.). But the sad truth of the matter is that I’ve “lost” almost all of them. They’re either folders of PHP spaghetti code or SSI files (Server-Side Includes…remember those?) or WordPress installations scattered across multiple ancient backup drives—some of which are in formats or using connectors can’t even use any more. Plus in many cases the content itself doesn’t even live in those folders, but rather exists in old MySQL databases which I would have to track down, load up, and possibly convert in order to access any of that content!

Bottom line: I’m essentially forced to rely on the Wayback Machine to look up my old content, but not all of the posts and domains were properly archived—and on many of the pages that do work, image links are often broken. It’s hardly an ideal scenario.

There is a Better Way: Make Your Site Static #

Thankfully, I’m now building all of my sites (including this one!) in a completely new way. I’m using Jekyll, which is a Static Site Generator. What does that mean? It means the content for the site—both the blog posts and pages as well as all of the template & layout files, Javascript code, and stylesheets—lives entirely within a simple folder hierarchy and consists solely of plain text files (other than images and other media of course). No databases to install, no weird dynamic code to run on the fly. All you have to do is run jekyll build at the command line (or in my case gulp build ; jekyll build because I process the source SCSS and JS files with Gulp) and in seconds you get a _site folder with your complete website generated and ready to deploy and view anywhere. As long as you write your content in Markdown (.md) or HTML (.html), you’re golden.

But the special one-two punch of using a Static Site Generator such as Jekyll is the fact that you can save your site and all of its content into a version controlled repository. Once your site is stored in a Git repo, you have endless options for how you want to archive and protect your data. Not only do you have every version of your site archived within the repo (so you can “go back in time” to view past iterations of the site), you can easily store the repo in multiple places at once. All of my sites are stored both locally on my computer as well as “in the cloud” in Bitbucket or GitHub, and in some cases they’re also stored on DigitalOcean servers I’ve set up with custom web apps I use to manage the content files using WYSIWYG editing tools. If my computer is busted, my sites are safe online, and if the internet completely goes down, at least I still have my local copies.

Why is this all so important for archival purposes? Here are three big reasons:

  1. You can view your site without any special software. Just fire up the most basic web server imaginable, drop your _site folder into its root location, and your site is up and running. No PHP, Ruby, Go, Python, or any other server language or framework required. There is no step three!
  2. Your content is contained within the most future-proof formats possible. Markdown files are just plain text with minimal decoration. HTML is the most well-established and widely-supported data exchange format in history. JPEG images certainly aren’t going anywhere any time soon. It’s safe to say that (unless you build your site with a bunch of crazy client-side Javascript rendering such that nothing works until all your code runs) you’ll be able to load a web browser decades from now and your site will just work.
  3. Your content is automatically backed up in multiple contexts, consistently. If all of your content is “silo’ed” in a single MySQL database somewhere on some WordPress host, and that host goes down or their backup gets corrupted, you’re toast. Years of work, gone. (And let’s not forget the fact that WordPress sites are prime targets of hacker attacks on a daily basis!) However, if your content lives within Git repositories that likely exist in multiple locations simultaneously, the likelihood you’ll completely lose your repo and all that data is vanishingly small.

The Future is Static #

A lot of web developers are using the term JAMstack these days to describe static sites build with the latest generation of tools, because the word “static” got a bad rap back in the day when new “dynamic” tools such as MovableType or WordPress were taking over the world. But there’s nothing truly static about static sites built with tools such as Jekyll, Hugo, and many others.

I can use extremely sophisticated build processes to create fantastic website designs with tons of interactivity, and I can log into admin interfaces and use WYSYWIG editors if I want to to to manage content and publish updates at the click of a button. Using Jekyll doesn’t mean you have to hand-code every blog post in raw HTML and “FTP” it somewhere like in the old days. We live in a new age where static site generators are not only slick and amazing, but are in fact paving the way for the future of modern web development.

Static is dead. Love live static!

So to sum it all up, if you want to create blogs and websites that will stand the test of time, that will still be readable ten, twenty, probably even fifty years from now, that will not get buried in a stack of hard drives somewhere or lost in some database black hole on the internet, then you need to try out Jekyll (or one of its competitors). I guarantee you: once you go JAMstack, you’ll never go back!

Newer Posts Older Posts
Skip to content