Today I Learned

A Hashrocket project

Ready to join Hashrocket? Find Openings here and apply today.

Ensure Ruby returns the correct value

Ruby has implicit returns for any possible block that I can think of, except ensure. There might be more, but this is the only one that I can think of now.

So in order to return something from inside the ensure block we must use the return reserved word explicitly. Check this out:

def check_this_out
  yield if block_given?
  :ok
rescue
  :error
ensure
  :ensured
end

irb()> check_this_out { "should work" }
=> :ok

irb()> check_this_out { raise "should fail" }
=> :error

As we can see even though the ensure code runs on all calls it does not return :ensured.

Here’s the code with the explicit return:

def check_this_out_explicit_ensure_return
  yield if block_given?
  :ok
rescue
  :error
ensure
  return :ensured
end

irb()> check_this_out_explicit_ensure_return { "should work" }
=> :ensured

irb()> check_this_out_explicit_ensure_return { raise "should fail" }
=> :ensured

Rename a local git branch

Ever wanted to rename a local git branch? The git branch -m command is your friend

Want to rename a branch that’s not currently checked out?

git branch -m <original_name> <new_name>

Or to rename your current local branch, you can exclude the original name in the git branch args:

git branch -m <new_name>

Return difference between Lambda and Proc in Ruby

More differences between Procs & Lambdas

If a Proc has an explicit return then that return bubbles up to where it is being used.

Let’s take a look at a Proc’s behavior:

# Explicit return
def work_it
  p = Proc.new { puts "Workout"; return }
  puts "Pre workout"
  p.call
  puts "Post workout"
end

=> work_it
Pre workout
Workout
=> nil
# No explicit return
def work_it
  p = Proc.new { puts "Workout" }
  puts "Pre workout"
  p.call
  puts "Post workout"
end

=> work_it
Pre workout
Workout
Post workout
=> nil

Now let’s look at lambdas:

# Explicit return
def work_it
  l = -> { puts "Workout"; return }
  puts "Pre workout"
  l.call
  puts "Post workout"
end

=> work_it
Pre workout
Workout
Post workout
=> nil

Now even with the explicit return in the lambda, the method is able to complete its own logic path.

Arity difference between Lambda and Proc in Ruby

Many devs think Procs & Lambas in Ruby are interchangable… and it a lot of cases they can be.

However I did come across a difference to be aware of.

Procs do not enforce arity where a Lambda will.

Let’s take a look at a Proc’s behavior:

# Argument provided
p = Proc.new { |arg| puts arg }
p.call("TEST")
TEST
=> nil
# No argument provided
p = Proc.new { |arg| puts arg }
p.call

=> nil

Now let’s look at lambdas:

# Argument provided
l = ->(arg) { puts arg }
l.call("TEST")
TEST
=> nil
# No argument provided
l = ->(arg) { puts arg }
l.call

=> wrong number of arguments (given 0, expected 1) (ArgumentError)

See how there is strict arity on a lambda where the proc will not complain.

Comparison Validate more than Numbers in Rails

As of Rails 7 we can now use comparisions in validations for more than just numbers!

Previously we could do this but only against an integer

class Procedure
  validates :appointment_count, numericality: { less_than: 5 }
end

Now we can compare against many other types. Here is a quick example validating that an end date is further in the future than a start date.

class Procedure
  validates :start_date, comparison: { greater_than: ->(_) { Date.current }
  validates :end_date, comparison: { greater_than: :start_date }
end

Cool thing is you can pass a Proc or Symbol. A symbol can represent a method on the class. The Proc can represent anything. Its argument is the instance of the class.

For more info, check out this PR.

Always declare columns for SQL query in Rails

When ignoring a column in an ActiveRecord query you’ll receive a query that declares the column names of the table explicitly versus using *.

What if you do not want to ignore a column to get this functionality? Rails 7 will introduce an ActiveRecord class attribute to do just this.

class Procedure < ApplicationRecord
  self.enumerate_columns_in_select_statements = true
end
Procedure.all

=> SELECT "procedures"."id", "procedures"."name", "procedures"."created_at", "procedures"."updated_at" FROM "procedures"

When enumerate_columns_in_select_statements is set to true, ActiveRecord SELECT queries will always include column names explicitly over using a wildcard. This change was introduced to provide consistency in query generation and avoid prepared statment issues.

Note: it can be declared at the app configuration level as well.

module MyApp
  class Application < Rails::Application
    config.active_record.enumerate_columns_in_select_statements = true
  end
end

With that change, all ActiveRecord queries will avoid the wildcard.

Ignore columns on ActiveRecord queries in Rails

ActiveRecord models have ignored_columns defined as a class attribute. This allows us to, just like it says, ignore a particular column from our queries and returning ActiveRecords.

class Procedure < ApplicationRecord
    self.ignored_columns = [:created_at, :updated_at]
end

If we perform a lookup, this is what we’ll now see.

Procedure.all

=> SELECT "procedures"."id", "procedures"."name" FROM "procedures"

Notice the subtle difference?

Without the ignored_columns declaration, we’d see a query like this:

Procedure.all

=> SELECT "procedures".* FROM "procedures"

Using NULLS FIRST / NULLS LAST ordering in Rails

Methods nulls_first & nulls_last were added to Arel in Rails 6.1 for PostgreSQL and in most other databases in Rails 7.

In an earlier TIL, I showed how to order using Arel.

Procedure.order(Procedure.arel_table[:name].desc.nulls_first)

=> SELECT "procedures".* FROM "procedures" ORDER BY "procedures"."name" DESC NULLS FIRST
Procedure.order(Procedure.arel_table[:name].desc.nulls_last)

=> SELECT "procedures".* FROM "procedures" ORDER BY "procedures"."name" DESC NULLS LAST

Here the desc method is wrapping the Arel attribute in a node so that it can be utilized in order. nulls_first/nulls_last is just wrapping the previous ordering node.

Procedure.arel_table[:name].desc.nulls_first

=> 
#<Arel::Nodes::NullsFirst:0x00007fb73ebf18f8
 @expr=
  #<Arel::Nodes::Descending:0x00007fb73ebf1920
   @expr=
    #<struct Arel::Attributes::Attribute
     relation=
      #<Arel::Table:0x00007fb735a02dc0
       @klass=Procedure(id: integer, name: string, created_at: datetime, updated_at: datetime),
       @name="procedures",
       @table_alias=nil,
       @type_caster=
        #<ActiveRecord::TypeCaster::Map:0x00007fb735a02cd0
         @klass=Procedure(id: integer, name: string, created_at: datetime, updated_at: datetime)>>,
     name="name">>>

I mention this as you need to define the order before utilizing the nulls methods.

Using Arel in ActiveRecord ORDER Queries

I utilize Arel in ActiveRecord where all the time but it never occured to me that order can also take an Arel node.

Procedure.order(Procedure.arel_table[:name].desc)

=> SELECT "procedures".* FROM "procedures" ORDER BY "procedures"."name" DESC

For such a trivial example I’d say that it’s much clearer to just use ActiveRecord in this case.

Procedure.order(name: :desc)

=> SELECT "procedures".* FROM "procedures" ORDER BY "procedures"."name" DESC

Cool piece of knowledge for possible use in a more complex ordering case.

Split a string in elixir

You can split a string in elixir with String.split

defmodule Phoenix.Repo.Migrations.CreateUsers do
  use Ecto.Migration

  def up do
    execute_sql("""
      create extension citext;

      create table users (
        id bigint generated by default as identity primary key,
        email citext unique not null,
        inserted_at timestamptz not null default now(),
        updated_at timestamptz not null default now()
      );
    """)
  end

  def down do
    execute_sql("""
      drop table users;
      drop extension citext;
    """)
  end
   

  def execute_sql(sql_statements) do
     sql_statements
     |> String.split(";")
     |> Enum.filter(fn s -> String.trim(s) != "" end)
     |> Enum.each(&execute/1)
  end
end

Replace first letter of string

You need to use runes to prevent corrupting unicode values:

package main

import (
    "fmt"
)

func replaceFirstRune(str, replacement string) string {
    return string([]rune(str)[:0]) + replacement + string([]rune(str)[1:])
}

func main() {
    name := "Hats are great!"
    name = replaceFirstRune(name, "C")
    fmt.Println(name)
}

Output:

=> Cats are great!

Just like in ruby, this doesn’t cover multi-byte unicode characters. You still need to do a unicode table lookup:

name = "👨‍👩‍👧‍👦"
name[0] = "C"
=> "C‍👩‍👧‍👦"
println(replaceFirstRune("👨‍👩‍👧‍👦", "C"))
=> "C‍👩‍👧‍👦"

You can go step more and replace the man with a woman:

println(replaceFirstRune("👨‍👩‍👧‍👦", "👩"))
=> "👩‍👩‍👧‍👦"

Elixir: Sandboxing an `iex` session

We can change the default .iex.exs file for another to be pre-loaded by iex using --dot-iex:

MIX_ENV=test iex --dot-iex .iex-sandbox.exs -S mix

This way we could create a rudimentary way to “protect” our existing database of changes on the iex session. Check this out:

# .iex-sandbox.exs
:ok = Ecto.Adapters.SQL.Sandbox.checkout(MyApp.Repo, ownership_timeout: 300_000)

This approach works for existing databases, so if the app we are working has a very difficult data setup then maybe this is the way to go. Although, making the db to hold data changes for as long as iex sessions are running it can be a bit too much in terms of db memory. Row & Table locks can definately be a problem as well.

Another important note is that we may have to call Sandbox.allow/3 if we need to call a GenServer or another process that touches the db.

The way to go

If we have a simpler data, with a nice seeds file, we’d be much better this way:

(export DB_NAME=my_app_sandbox && mix ecto.reset && iex -S mix)

Don’t forget to change our ecto config to use that ENV var.

You MUST use a ws protocol in rails💩actioncable

Rails’ actioncable library is a bit immature compared to other implementations, so there are a lot of rough edges to work around. One of those is the basic createConsumer function.

If your app is running without a DOM (nodejs), the node_module @rails/actioncable is going to fight you.

The rails guides recommend this:

createConsumer('https://ws.example.com/cable')

But that function relies on having a global document that can create <a> tags, which you won’t have in many contexts (node, react-native, etc.)

Also, why does a websocket library depend on HTML anchor tags?

You can work around this limitation by explicitly using a ws or wss protocol:

createConsumer('ws://localhost:3000/cable')

Ruby's ENV::[]= only accepts strings

You cannot set the value of an environment variable to something that is not a string

# THIS DOES NOT WORK
ENV["SKIP_AUTH"] = true
=> `[]=': no implicit conversion of true into String (TypeError)

You can, however, pass an object that implements #to_str

class User < ApplicationRecord
  def to_str
    to_global_id.to_s
  end
end

ENV["user"] = User.first
ENV["user"]
=> "gid://rails-app/User/3f565b9c-0899-49f6-ab20-aa2724235ff5"

Be careful when stubbing ENV in specs:

# ENV could never return a boolean, your tests will lie to you.
RSpec.describe "ENV" do
  before do
    stub_const("ENV", {"SKIP_AUTH" => true})
  end
end

Have OS specific implementations in golang

In go, you can create separate implementations for different operating systems by creating multiple files:

package main

import (
    "play.ground/foo"
)

func main() {
    foo.HelloComputer()
}

-- go.mod --
module play.ground

-- foo/foo_windows.go --
// +build windows

package foo

import "fmt"

func HelloComputer() {
    fmt.Println("Hello windows!")
}

-- foo/foo_linux.go --
// +build !windows

package foo

import "fmt"

func HelloComputer() {
    fmt.Println("Hello not windows!")
}

Pass keyword arguments when using send

Don’t use a hash, just pass send with a comma-separated list of keyword arguments:

class Animal < Struct.new(:name)
  def greet(name:, catch_phrase:)
    puts "Heya #{name}! What's new, #{catch_phrase}?"
  end
end

Animal.new("Rex").send(:greet, name: "Dillon", catch_phrase: "cool cat")
=> "Heya Dillon! What's new, cool cat?"

Sending with a hash will fail:

Animal.new("Rex").send(:greet, {name: "Dillon", catch_phrase: "cool cat"})
=> wrong number of arguments (given 1, expected 0; required keywords: name, catch_phrase) (ArgumentError)

Custom URL helpers in Rails

Ever want a named route for something that’s not necessarily a resource in your Rails app?

In your any of your route files you can utilize direct.

# config/routes.rb

Rails.application.routes.draw do
  get 'foo', to: 'foo#bar'

  direct :get_to_the_goog do
    "https://google.com"
  end
end

This gives a nice named route of get_to_the_goog_url!

This can be even more useful as the return value of the block has to be valid arguments that you would pass to url_for. So you can use pretty much anything you’d normally use to build a url helper.

Let’s modify this just a bit to be more useful.

# config/routes.rb

Rails.application.routes.draw do
  get 'foo', to: 'foo#bar'

  direct :get_to_the_goog, search: nil do |options|
    "https://google.com/search?q=#{options[:search]}"
  end
end

Now we can call get_to_the_goog_url(search: "TIL").

Pretty cool… just take note of a few caveats. You get access to the _path version of the named helper but even if the helper is using a hard coded string (like our example above), it will remove the domain info (rightfully so). Also you are not able to use the direct functionality inside of a namespace or scope, however it will raise an error if you do… so that’s nice.

Break up large routing files in Rails

Breaking apart a routing file could be useful if you have large a different concepts in your app with their own larger route sets.

To do this you can utilize draw

# config/routes.rb

Rails.application.routes.draw do
  get 'foo', to: 'foo#bar'

  draw(:admin)
end

This will try to load another route file at config/routes/admin.rb. That file is just another normal routing file.

# config/routes/admin.rb

namespace :admin do
  resources :users
end

How to use ActiveRecored.pluck without Arel.sql

It is too cumbersome to remember to wrap every string in a class method, so this is a shortcut:

# app/models/application_record.rb

class ApplicationRecord < ActiveRecord::Base
  module ::ActiveRecord::Sanitization::ClassMethods
    define_method(:original_disallow_raw_sql!, instance_method(:disallow_raw_sql!))

    def disallow_raw_sql!(args, permit: connection.column_name_matcher) # :nodoc:
      original_disallow_raw_sql!(args.map { |a| a.is_a?(String) ? Arel.sql(a) : a }, permit: permit)
    end
  end
end

Then you can write sql without the class methods

This can be easier to write:

User.where(payment_due: true)
  .pluck(Arel.sql("coalesce('last_billed_date', 'start_date')"))

Can now just be written as:

User.where(payment_due: true)
  .pluck("coalesce('last_billed_date', 'start_date')")

This can also be useful for .order as well

undef_method vs remove_method

Ruby’s undef_method and remove_method are both methods for removing a method from a class, but there are subtle differences between the two.

Say we have two classes that both define the method name, with one class inheriting from the other:

class Human
  def name
    "homo sapien"
  end
end

class Child < Human
  def name
    "small homo sapien"
  end
end

remove_method fully deletes a method from a particular class, but will still look for the method on parent classes or modules when called on the particular class:

child = Child.new
child.name
# => "small homo sapien"

class Child
  remove_method :name
end

child.name
# => "homo sapien"

undef_method in contrast will prevent Ruby from looking up the method on parent classes

child = Child.new
child.name
# => "small homo sapien"

class Child
  undef_method :name
end

child.name
# => raises NoMethodError
# undefined method `name' for #<Child:0x00007ffd91a007a8>

Append items to an array

Today I came across yet another way to add items to an array in ruby

I already knew about push and <<, but did you know that there’s also an append?

[1,2,3].push(4)
# => [1,2,3,4]

[1,2,3] << 4
# => [1,2,3,4]

[1,2,3].append(4)
# => [1,2,3,4]

append is ultimately just an alias for push, but always good to know!

Subtlety between Rails' #try & Ruby's #&.

Many would say that the safe navigation operator (&.) in Ruby is a drop in replacment for Rails’ #try. However I came across an interesting case where they are not the same.

thing = nil
thing&.something
> nil
thing = nil
thing.try(:something)
> nil

Seems pretty much the same right?

However what happens when the object receiving the message is not nil but does not respond to the message?

thing = Thing.new
thing&.something
> NoMethodError: undefined method 'something' for <Thing:0x0000>
thing = Thing.new
thing.try(:something)
> nil

Hopefully not something you’ll run into often, but definitely be aware of it. It makes the safe navigation operator not necessarily a drop in replacment.

Rails has helpers for uploading spec fixture files

Instead of writing your own helper method in specs:

module HelpMePlease
  def uploaded_file(path)
    Rack::Test::UploadedFile.new(Rails.root.join("spec/fixtures", path))
  end
end

Rails has a built in helper method:

require "rails_helper"

RSpec.describe HelpNeeded do
  describe "something" do
    it "sends the file" do
      post :change_avatar, params: { avatar: fixture_file_upload("spongebob.png", "image/png") }
    end
  end
end

Dynamically Render Client Side with Next.js

Next.js has a handy feature for rendering a child component client side, instead of on the server.

Consider the following example, where you have a component in src/components/Analytics.js with a default export.

import dynamic from "next/dynamic";

const DynamicAnalytics = dynamic(() => import("../components/Analytics"));

function Header(props) {
  return (
    <>
     <DynamicAnalytics />
     <OtherServerRenderedStuff {...props} />
    </>
  )
}

Named Exports

You can also use this dynamic importing feature with named exports. Consider the next example:

// src/components/Analytics.js
export function Analytics() {
  return (
    // ....
  )
}
import dynamic from "next/dynamic";

const DynamicAnalytics = dynamic(
  () => import("../components/Analytics").then(mod => mod.Analytics)
);

function Header(props) {
  return (
    <>
     <DynamicAnalytics />
     <OtherServerRenderedStuff {...props} />
    </>
  )
}

There are some gotcha’s when using dynamic, so make sure to check out the docs and get familiar with the API.
https://nextjs.org/docs/advanced-features/dynamic-import

Convert array to object with Lodash

I was trying to see if I could find a lodash method that works the same way as index_by from ruby and I found keyBy:

> const memberships = [{groupId: '1', status: "active"}, {groupId: '2', status: "inactive"}]
> keyBy(memberships, "groupId")

{ '1': { groupId: '1', status: 'active' },
  '2': { groupId: '2', status: 'inactive' } }

Funny thing is that on a previous version of lodash this method was called indexBy, same as the ruby version.

Finding Rails ActiveModel Errors

Rails allows to filter ActiveModel erros by field name and type with the where method. Check this out:

user = User.new(email: "user@test.com")
user.valid?

user.errors.where(:email).map(&:full_message)
# => [
#      "Email has already been taken",
#      "Email is too short (minimum is 20 characters)"
#    ]

user.errors.where(:email, :taken).map(&:full_message)
# => ["Email has already been taken"]

Rails delegated_type

Rails 6.1 added delegated_type for the ones who like polymorphic relations. So in addition to set a regular polymorphic relation in rails you can also call delegated_type method to inject a bunch of useful methods and scopes for you. Check this out:

class Content < ApplicationRecord
  # belongs_to :contentable, polymorphic: true
  delegated_type :contentable, types: %w[ TilPost BlogPost ]
end

class TilPost < ApplicationRecord
  has_one :content, as: :contentable
end

class BlogPost < ApplicationRecord
  has_one :content, as: :contentable
end

And this will produce helper methods like:

content.contentable_class
# => +TilPost+ or +BlogPost+
content.contentable_name
# => "til_post" or "blog_post"

Content.til_posts
# => Content.where(contentable_type: "TilPost")
content.til_post?
# => true when contentable_type == "TilPost"
content.til_post
# => returns the til_post record, when contentable_type == "TilPost", otherwise nil
content.til_post_id
# => returns contentable_id, when contentable_type == "TilPost", otherwise nil

Rails 6 `create_or_find_by`

Rails 6 comes with a new ActiveRecord method create_or_find_by.

User.create_or_find_by(first_name: 'Vinny') do |user|
  user.status = 'pending'
end
# => #<User id: 1, first_name: "Vinny", status: pending>

This method is similar to the already existing find_or_create_by method by it tries to create the model in the database first, then if an unique constraint is raised by the DB then rails rescue the error, it rollbacks the transaction and it finally finds the model.

The idea behind to use this new method instead is to avoid race condition between a find and a create calls to the db.

There’s a few downsides to this process, but one that caught my eyes was that now we’re seeing a lot of ROLLBACK messages in the rails logs, which is the expected behavior now.