Today I Learned

A Hashrocket project

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

235 posts about #ruby

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

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.

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

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)

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!

Parse a Query String in Ruby

If you ever need to parse a query string in Ruby - or Rails, Rack has a convenient utility to do just that. parse_nested_query will parse from a string to a hash:

Rack::Utils.parse_nested_query("&sort_dir=asc&sort_by=date_created&filter_by=lead")

=>  {"sort_dir"=>"asc", "sort_by"=>"date_created", "filter_by"=>"lead"}

You can also go the opposite way with build_nested_query and generate a query string:

Rack::Utils.build_nested_query({"sort_dir"=>"asc", "sort_by"=>"date_created", "filter_by"=>"lead"})

=>  "sort_dir=asc&sort_by=date_created&filter_by=lead"

https://www.rubydoc.info/gems/rack/Rack/Utils

Set JSON.parse returned object and array classes

By default, the Ruby JSON.parse method returns a ruby Hash for any json object, and a ruby Array for any json array.

However, you can customize the returned object classes using the object_class and array_class options:

source = JSON.dump({ wibble: "wubble", data: [1,2,3] })

result = JSON.parse(
  source, 
  object_class: OpenStruct,
  array_class: Set
)
# => #<OpenStruct wibble="wubble", data=#<Set: {1, 2, 3}>>

result.data # => #<Set: {1, 2, 3}>
result.wibble # => "wubble"

rspec should receive thrice

rspec has a #thrice method for testing receive counts:

describe Account do
  context "when opened" do
    it "logger#account_opened was called once" do
      logger = double("logger")
      account = Account.new
      account.logger = logger

      logger.should_receive(:account_opened).thrice

      account.open
      account.open
      account.open
    end
  end
end

Replace multiple characters in ruby strings

Ruby String#tr allows you to replace characters or patterns in strings:

irb(main):001:0> "I love coffee".tr("love", "😍")
=> "I 😍😍😍😍 c😍ff😍😍"

Compare with #gsub:

irb(main):001:0> "I love coffee".gsub("love", "😍")
=> "I 😍 coffee"

If your pattern arg to gsub is only one character consider using #tr, but beware of multi-length from_str arg to #tr

How to conditionally add a value in array literals

I really like declarative things. It’s probably why I like React, and really dig functional programming approaches. So when I’m writing Ruby, sometimes I find myself wanting to delcare some array of values to use in a map, reduce, or each. The problem is, sometimes I only want a particular value give a condition. One pattern that I’ve started to try on for size is

def things
  [something, (something_else if reasons)]
end

Then, you can use it like so:

things.compact.map(&:cool_method)

The conditional will evaluate and leave a nil in the array, which isn’t my favorite. However, I’ve found that this is a very useful pattern for simplifying certain methods.

Ruby Itself

Today I stumbled across a neat Ruby object, itself. itself returns the receiver, itself.

string = "my string"
string.itself.object_id == string.object_id #=> true

What’s a use case for this object? I used it to divide an array into arrays of matching integers.

 > [1,99,99,1,2,3,2,5].group_by(&:itself)
 => {1=>[1, 1], 99=>[99, 99], 2=>[2, 2], 3=>[3], 5=>[5]}

Ruby Delete Prefix

Today I was working with a Ruby method that deletes the prefix from a string, turning --code into code. While there are several ways to get this done, a handy Ruby feature I discovered today is delete_prefix:

> "--code".delete_prefix("--")
=> "code"

Intention-revealing and very Ruby.

What Is the Rails Notes Command?

While reading through the Rails 6 changelog, I noticed an entry for a rails command called notes. Having never seen this before, I took a quick look at the Rails Guides.

The command rails notes will return a list of all instances of the following annotations in your codebase - FIXME, OPTIMIZE, and TODO.

You can optionally search for your own custom annotations with the --annotations (-a) flag:

rails notes -a NOTE
app/controllers/admin/blog_posts_controller.rb:
  * [10] [NOTE] Only return the last 10 blog posts

README.md:
  * [ 1] [NOTE] Set the following env variables

There’s also a way to register your own custom annotations for use with the default runner

config.annotations.register_tags("DEPRECATEME", "TESTME")

Rails Guides - Rails Notes

There's a "whereami" alias in Pry

Since version 0.10.0, the pry gem has shipped with a built in alias for whereami: the extremely convenient @

Other useful aliases can be found using the help command:

[1] pry(main)> help

Aliases
  !!!                Alias for `exit-program`
  !!@                Alias for `exit-all`
  $                  Alias for `show-source`
  ?                  Alias for `show-doc`
  @                  Alias for `whereami`
  clipit             Alias for `gist --clip`
  file-mode          Alias for `shell-mode`
  history            Alias for `hist`
  quit               Alias for `exit`
  quit-program       Alias for `exit-program`
  reload-method      Alias for `reload-code`
  show-method        Alias for `show-source`

Check for members in Ruby

Ruby’s Enumerable class has a member? method that returns a boolean.

For arrays, the method checks if the supplied value is included (similar to ['a'].include?('a')):

[:a, :b].member?(:b) # => true
[:a, :b].member?(:c) # => false

For hashes, the method checks if the supplied value is included in the keys for the hash:

{ a: 'b' }.member?(:a) # => true
{ a: 'b' }.member?(:c) # => false

Count the number of objects in memory

Let’s say you’re having memory issues in your Ruby app and you want to get a feel for how many objects are instantied. You can use ObjectSpace!

require 'objspace'
ObjectSpace.each_object(Object).count
# in my typical rails app: 394683

Everything is a child of Object (for the most part), so you have about 394683 objects in memory! That’s a lot! You can narrow this down if you like to just String.

ObjectSpace.each_object(String).count
# in my typical rails app: 295261

But let’s check something more interesting, like TZInfo::CountryTimezone:

ObjectSpace.each_object(TZInfo::CountryTimezone).count
# in my typical rails app: 348

Neat!

Safe Navigation on nil gotcha

tldr; Beware, both ActiveSupport’s try and Ruby’s safe navigation operator &. will always return nil when operating on a nil.

For example, I would have expected either to return a response like so:

> nil.try(:to_s) => ""
> nil&.to_s => ""

However this is not the case:

> nil.try(:to_s) => nil
> nil&.to_s => nil

Default Hash values are super easy in Ruby

Want a list?

list_default = Hash.new([])

That’s pretty darn easy.

Edit: Thanks to @kaokun on twitter for catching this and correcting me. This will return the same array each time (having flashbacks to the first time I used mutable keyword args in python). To get a different array, you need need to pass a block with the behavior:

list_default = Hash.new { |hash, key| hash[key] = [] }

Just because your language hides pointers and references doesn’t mean you can ignore them I guess.

Rounding Time in Increments

I Recently needed to round down a time in certain increments. This is not something I’ve ever been asked to do previously so it was an interesting problem to figure out. The resulting formula is simple:

TIME_MIN - (TIME_MIN % INCREMENT)

This will give you the previous incremental minute. ex: Given a 5 minute increment @ 3:12, the result would be 10.

time = Time.now
new_min = time.min - (time.min % 5)

I happen to be solving this in a project with ActiveSupport so there was access to Time#change so I finished this up with:

time.change(min: new_min)

Proc Composition Operator in Ruby #functionalruby

Function composition is really nice, and having a clean way to do it is important. Ruby 2.6 introduced a really neat way of handling it with procs:

add_self = ->x { x + x }
mult_self = ->x { x * x }
add_and_mult = add_self << mult_self
add_and_mult[3] # 18

If you want the application to be reversed:

add_self = ->x { x + x }
mult_self = ->x { x * x }
add_and_mult = add_self >> mult_self
add_and_mult[3] # 36

This can be made into a way of building up pipelines for single argument procs by declaring them in an Array and using #inject with an identity proc:

calculations = [
->x { x / 3 },
->x { x + 5 },
->x { x * 2 }
]
pipeline = calculations.inject(->x {x}) do |built, func|
build << func
end
pipeline[3] # 8

This can be super cool for things like ActiveRecord objects where you want to chain certain operations without necessarily making them explicit methods via scopes or similar.

Do float division with `fdiv`

Usually in ruby, when I need to perform floating point division I call .to_f on either the numerator or denominator, like this:

1 / 5.to_f
# 0.2

While many people might read this as floating point division instead of integer division, reading it that way gets trickier when those numbers are variables and the .to_f happens somewhere else.

You can be more explicit about the operation you are performing with .fdiv.

1.fdiv(5)
# 0.2

This works with floats as well, so that you can use fdiv in any context.

1.to_f.fdiv(5.to_f)
# 0.2

Ruby 2.7 Enumerable#tally

Have you ever wanted to get back a count of how often objects showed up within an array? If so, you’ve likely written (or copy-pasta’d) some variation of the code below:

list = %w(red red red blue blue fish blue blue)

list.each_with_object(Hash.new(0)) { |v, h| h[v] += 1 }          # => {"red"=>3, "blue"=>4, "fish"=>1}
list.each_with_object({}) { |v, h| h[v] ? h[v] += 1 : h[v] = 1 } # => {"red"=>3, "blue"=>4, "fish"=>1}
list.group_by { |v| v }.map { |k, v| [k, v.size] }.to_h          # => {"red"=>3, "blue"=>4, "fish"=>1}

Lucky for us, Ruby 2.7 introduces a new convenience method in Enumerable#tally, so our above code reduces to:

list = %w(red red red blue blue fish blue blue)
list.tally # => {"red"=>3, "blue"=>4, "fish"=>1}

Huzzah!

You can read the feature discussion here!

No More Destructuring Objs in Func Args in Ruby2.7

Ruby has supported destructuring of objects into keyword arguments for a while in the Ruby 2.* series of releases, but now you’ll be getting a warning if you try this:

The 2.6 version

> def foo(a:, b:); puts [a, b]; end
> foo({a: 2, b: 2})
1
2

The 2.7 version

> def foo(a:, b:); puts [a, b]; end
> foo({a: 2, b: 2})
warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call

This warning will turn into an error in Ruby 3.0.

And if you add ** to the call like it tells you to:

> def foo(a:, b:); puts [a, b]; end
> foo(**{a: 2, b: 2})

OK Everything is cool. You can’t put ** if your keyword arguments are in a lambda which is being passed to map(&myLambda) though.

In this case, you’ll have to rewrite your code, so do that or you’ll be version locked!

H/T Brian Dunn

Read more here.

So you heard about Ruby 2.7 Pattern Matching?

Ruby has an experimental feature “Pattern Matching” introduced in this latest release, 2.7.

When I think pattern matching I think function arguments but for Ruby this is not the case. Instead, pattern matching is all about the case statement.

When I open up a 2.7 irb repl I can do this:

irb(main):001:0> case [1, 2]; in [1, x]; puts "x: #{x}"; end
(irb):5: warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!
x: 2
=> nil

Yep, it’s experimental. Seems like you shouldn’t use it in production code 😡.

There it is! Check out a slide deck with more info here

Class.new with Superclass

Today I encountered the following syntax (observed in Ruby 2.7):

SpecialError = Class.new(StandardError)

What’s going on here?

According to the Class docs, Class.new takes a superclass as an argument, creating an anonymous unnamed class. So this could be written as:

class SpecialError < StandardError
end

We’d lose the anonymity of the SpecialError class, but in the first example, we are assigning it to a Pascal-cased variable name anyway.

I think the first syntax would make sense when the code looks just like this example: a simple class that inherits some behavior, where the name is needed only in the scope where the class is declared.

Ruby Array#sort_by

Let’s say we have the following list of very interesting items.

items = [
  { name: "dogfood", price: 4.99 },
  { name: "catfood", price: 5.99 },
  { name: "batfood", price: 4.99 }
]

Our client wants the list sorted by price descending, and we should break ties in price by then sorting by item name descending. So with our example list, the result should be:

items = [
  { name: "batfood", price: 4.99 },
  { name: "dogfood", price: 4.99 },
  { name: "catfood", price: 5.99 }
]

Here’s a method that does the job for our client:

def sort_by_price_and_name(items)
  items
    .group_by { |i| i[:price] }
    .map { |_k,v| v.sort { |a, b| a[:name] <=> b[:name] } }
    .flatten
end

It’s a bit unwieldy and inelegant. It turns out there is a perfect method for this in Ruby:

def sort_by_price_and_name(items)
  items.sort_by { |i| [i[:price], i[:name]] }
end

Ruby’s Enumerable#sort_by works in this case where we sort by all the fields in ascending order. If we wanted to sort by price ascending, then name descending, sort_by would no longer be the tool we’d want to use.

Hat tip to @joshcheek for the knowledge.

Multiline HAML, One Way or Another 🥞

HAML uses meaningful whitespace identation, so in general, multiline code doesn’t fly. The prevailing wisdom is that ‘it’s better to put that Ruby code into a helper’ rather than support multiline blocks. But what if I really want to?

Here’s an example of a multiline HAML block using pipes.

= link_to(           |
  'someplace',       |
  some_path,         |
  class: 'someclass'

The pipe character preceded by whitespace signifies a multiline block. Make sure you don’t end your final line with a pipe; this can break your templating.

Ruby yield as keyword args default

Today I learned that you can use yield as a default value for a keyword arg.

def foo(bar: yield)
  "Received => #{bar}"
end

Then you can call this method using the keyword syntax:

foo(bar: "Hello world!")
#=> "Received => Hello world!"

or by using the block syntax:

foo do
  "Hello world!"
end
#=> "Received => Hello world!"

I am not sure why I’d use such flexible syntax for a single method, but we have to know what’s possibly in Ruby. Anyway, just a sanity check here, is the block evaluated if we pass the arg?:

foo(bar: "Hello") do
  puts "Block was evaluated!!!"
  "world!"
end
#=> "Received => Hello"

Cool, so ruby does not evaluate the block if this keyword is passed into, so we are cool.

How to setup VS Code for Ruby development

After some trial and error with the various extensions available for Ruby, I’ve found the following combination to work well:

Ruby for debugging, syntax highlighting and linting. I use these VS Code User Settings:

"ruby.useLanguageServer": true,
"ruby.lint": {
  "ruby": true
}

Solargraph for intellisense and autocomplete.

endwise for wisely adding end to code structures. (Inspired by tpope’s endwise.vim)

Prettier and plugin-ruby for formatting.

Prettier plugin support is on the way, but for now we have to do this

Bonus for Rails: PostgreSQL for writing queries within the editor.

Create Named Structs With Struct.new

I often see Struct used to create some one-off anonymous data structure like so:

> person = Struct.new(:name, :age)
=> #<Class:0x007fc6c89112e8>
> person.new("Alice", 33)
=> #<struct name="Alice", age=33>

This will often get the job done, but on its own the resulting data structure doesn’t tell us as much as it could.

We can say more with a named struct:

Struct.new("Person", :name, :age)
=> Struct::Person
> Struct::Person.new("Bob", 24)
=> #<struct Struct::Person name="Bob", age=24>

When the first argument is a string that can be converted to a constant, then we’ll get a named struct that is subclassed under Struct.

We can also assign the struct initialization to a constant to do a similar thing:

> Person = Struct.new(:name, :age)
=> Person
> Person.new("Jerry", 45)
=> #<struct Person name="Jerry", age=45>

source