Today I Learned

A Hashrocket project

187 posts about #ruby

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

Generate A Signed JWT Token

The jwt gem is a Ruby library for encoding and decoding JWT tokens. You can create a signed JWT with the #encode method by specifying a secret and a hash algorithm.

payload = { id: 1, email: 'user@example.com' }
secret = Rails.application.credentials.secret_key_base

token = JWT.encode(payload, secret, 'HS256')

This will create a JWT token that contains some JWT headers, application data, and an encrypted secret that signs the data. This can be passed to and from your client app as a way of identifying and authenticating a user.

See the jwt-ruby docs or jwt.io for more details.

Compare Dates in Neo4j.rb

Neo4j.rb stores dates as timestamps so you will have to convert your date object into a timestamp.

To convert a date object into a timestamp first convert to utc time and then to integer:

Date.current.to_time(:utc).to_i

And in your cypher query you are safe to use the comparison operators:

where('post.published_at <= ?', Date.current.to_time(:utc).to_i)

Scripting With RVM

Because of how RVM works under the hood, you have to do a couple things to get it to work in a script.

First, you need to ensure that your script is using bash instead of sh, so add this to the top of your scripts:

#!/bin/bash

You’ll then want to make sure that RVM is sourced. Their docs recommend sourcing in a script like this:

# Load RVM into a shell session *as a function*
if [[ -s "$HOME/.rvm/scripts/rvm" ]] ; then

  # First try to load from a user install
  source "$HOME/.rvm/scripts/rvm"

elif [[ -s "/usr/local/rvm/scripts/rvm" ]] ; then

  # Then try to load from a root install
  source "/usr/local/rvm/scripts/rvm"

else

  printf "ERROR: An RVM installation was not found.\n"

fi

After that, you can utilize any of the capabilities of RVM in your script as you’d like.

Ruby #try Operator

In Rails, there’s a #try method that will attempt to call a method on an object. If the caller is not nil, it will return the result of the “tried” method. If the caller is nil, try will return nil:

chicken.try(:farm) # => "delicious" (assuming chicken is an object whose #farm function returns "delicious")

# otherwise

chicken.try(:farm) => nil

Ruby 2.3.0 introduced the Safe Navigation Operator &. that is a shorthand for the try method.

chicken&.farm # => "delicious" (assuming chicken is an object whose #farm function returns "delicious")

# otherwise

chicken&.farm => nil

(Shouts out to Dorian)

Chaining `then` in Ruby 2.6

Ruby 2.5 introduced a method yield_self on an object. The idea is simple, just pass self as the argument to a block.

"something".yield_self {|str| str + "!"}
          .yield_self {|str| str + "!"}
# "something!!"

It yields self and returns the result of the block, unlike tap which yields self and returns self.

yield_self feels technical and wordy. Ruby 2.6 introduces an alias for yield_self, then. then feels like something that could mimic the pipe operator (|>) in Elixir. Data transformations can be just a series of thens.

"something"
.then {|str| str.chars.map {|x| x.ord + 1 }}
.then {|ords| ords.map {|x| x.chr }}
.then {|chars| chars.join } 
.then {|str| str + "!" }
.then {|str| str + "!" }
# tpnfuijoh!!

In some cases where a data transformation takes multiple steps, this coding style might make your could cleaner and more intentional.

I look forward to seeing then in your code!

Ruby 2.6 introduces endless ranges `(1..)`

This syntax is basically a substitute for either (1..Float::INFINITY) or [1, 2, 3][1..-1].

One of the best use cases I’ve seen is from this April 2018 post:

case 2022
when(2030..)
  :mysterious_future
when(2020..)
  :twenties
when(2010..)
  :nowish
else
  :ancient_past
end
#=> :twenties

This reads a lot cleaner than it would with 2010...Float::INFINITY.

Another cool use is a short with_index using zip.

[1, 2, 3].zip(1..) { |a, index| puts "#{a}, #{index}"}
# 1 1
# 2 2
# 3 3

And additionally, you get a bit of safety that you didn’t get with (1..Float::INFINITY):

(1..Float::INFINITY).to_a
# endless loop
(1..).to_a
# RangeError (cannot convert endless range to an array)

Which is a much better outcome for your program!

The shovel `<<` now composes procs in 2.6

The new Ruby 2.6 release definitely has some… uh… gems.

The >> and << operators can now compose procs.

irb> a = ->(){1}
=> #<Proc:0x00007f937a0be158@(irb):33 (lambda)>
irb> b = ->(x){ x + 2}
=> #<Proc:0x00007f937b01fac0@(irb):34 (lambda)>
irb> c = a >> b
=> #<Proc:0x00007f937a148c40 (lambda)>
irb> c.call()
3

Composing procs yes, but really anything that’s callable (implements the call method).

irb> def d; 4; end
:d
irb> e = method(:d) >> b
=> #<Proc:0x00007f937a86b3b0 (lambda)>
irb> e.call()
=> 6

This is cool! I hope I start seeing code taking more advantage of procs, I can see some interesting styles coming from this.

Ruby knows itself

An identity function according to wikipedia is defined as:

a function that always returns the same value that was used as its argument.

This morning I learned that Ruby added an identity function for objects in 2.2, #itself!

> [1, 2, 3].map(&:itself)
[1, 2, 3]

Off the top of my head I can’t think of a good example, but it does come up now and again. The identity function is definitely something to keep in mind!

Using BCrypt To Create And Check Hashed Passwords

The BCrypt library is used under the hood by gems like Devise in order to work with passwords securely. You can use it to salt and hash a plain text password. You can also use it to check whether an encrypted password matches some input password.

> include BCrypt
=> Object

> encrypted_pass = Password.create('password')
=> "$2a$10$te3Y8wdSXf8/gWDeSP5z9eut7alThnuTvq1SvgQyJ1C57F.qit1uq"

> Password.new(encrypted_pass) == "not_my_pass"
=> false

> Password.new(encrypted_pass) == "password"
=> true

The Password.create method will salt and hash the given password. The resulting encrypted password, if it is an instance of Password, can be directly compared to a string. For good measure, in case the encrypted password is a string, you can wrap it in a call to Password.new to ensure you are working with a Password instance.

Generate Ruby Version and Gemset Files With RVM

RVM, the ruby version manager, is a fairly flexible tool that supports a number of workflows. The rvm utility can be used to generate both a .ruby-version file and a .ruby-gemset file for a given project.

$ rvm --ruby-version use 2.5.3@my_project

This will generate a .ruby-version file in your current project directory that points RVM to the 2.5.3 version of Ruby. It will also create a .ruby-gemset file that RVM will use for managing this project’s gemset.

Spreading nil Into a Ruby Array

Spreading nil into an array doesn’t add an entry into a Ruby array:

[*nil] # => []
a = [1, 2, 3]
[*nil, *a] # => [1, 2, 3]

One might expect this to insert nil in the array, but this isn’t the case. This feature could be useful when passing dynamically assigned, nullable items into an array.

def foo(arg)
  [*arg]
end

nullable_var = params[:foo]
foo(nullable_var).each { |x| act_on(x) } # => [] if nullable_var is nil

Find a Capybara node with regex

My pair and I faced a situation yesterday where we had to find a node with two separated pieces of text.

<section>
    <div>Something</div>
    <div>Some Other Text</div>
    <div>Apple</div>
</section>
<section>
  <div>Different</div>
  <div>Some Other Text</div>
  <div>Apple</div>
</section>
<section>
  <div>Something</div>
  <div>Some Other Text</div>
  <div>Orange</div>
</section>

We wanted to find a node with text “Something” and text “Orange” in our Capybara test.

The find method has a text option that you can pass a regex to.

find("section", text: /Something.*Orange/)

And initially this wasn’t working because of a line break and so we had to use the multiline flag (m).

find("section", text: /Something.*Orange/m)

Yanking a Ruby Gem

Yanking (unpublishing) a Ruby gem is something that has to happen from time to time. Maybe your library has a known vulnerability. Maybe you pushed a gem by mistake. Sometimes the best code is no code.

Here’s how you permanently yank a gem published to Rubygems:

$ gem yank <gemname> -v <version>

What are the side effects? If somebody was locked to your exact version, they would not be able to download that package again and would have to upgrade or downgrade. Consider that when deciding whether to yank.

Policy change about gem yank / CLI reference

Install `capybara-webkit`

Turns out capybara-webkit’s has a dependency on qtwebkit which has been removed from the qt library package. You’ll have the pleasure of finding this out when you run into this grand error:

Project ERROR: Unknown module(s) in QT: webkitwidgets

To get around this, you can install a previous version of qt using brew via:

brew install qt@5.5

Now reload your terminal and ensure that you can run qmake. Once that is all good, go ahead and reinstall/bundle capybara-webkit.

Don’t let this stump you for longer than it did me!

Mocking Requests With Partial URIs Using Regex

Generally when mocking out requests with the webmock gem, we specify full request URIs like so:

stub_request(:post, 'http://localhost:4000/api/posts')

We may not want to specify the entire URI though. For instance, the host may change or be configurable. The stub_request method allows us to use regex.

stub_request(:post, %r|/api/posts|)

Using the %r regex literal syntax, we are able to avoid escaping all of the / characters in our URI.

h/t Brian Dunn

`nil.&to_s` evaluates to false

ruby 2.3 introducted the Safe Naviation Operator &. intended to make nil checks less verbose, simulating the functionality of the Rails introduced try method.

This helps to prevent unwanted NoMethodErrors

> nil.do_something
NoMethodError (undefined method `do_something' for nil:NilClass)
> nil&.do_something
nil

What happens when you type the & on the right side of the .?

> nil.&do_something
NameError (undefined local variable or method `do_something' for main:Object)

Normally you’d get an error, because do_something isn’t defined, but what if you are calling a method available on everything like to_s?

> nil.&to_s
false

This breaks expectations. This statement is equivalent to nil & to_s.

> nil & to_s
false

Ruby defines & as a method on nil.

> nil.method(:&)
#<Method: NilClass#&>

This method in the documentation is defined as:

And—Returns false. obj is always evaluated as it is the argument to a method call—there is no short-circuit evaluation in this case.

So with nil.&to_s you’re calling the & operator as a method and passing the result of to_s called on the main object, but it doesn’t matter because nil.& always evaluates to false.

Create Listing Of All Middleman Pages

Middleman is a handy tool for quickly throwing together a bunch of static pages with layout and templating help at the ready. Once you have a handful of pages up and running, you’ll probably want a way to quickly navigate to them. You can add a quick listing of all the pages with a couple helpers provided by Middleman.

<ul>
  <% sitemap.resources.each do |resource| %>
    <li><%= link_to(resource.path, resource.path) %></li>
  <% end %>
</ul>

The sitemap.resources variable will contain a list of all the resources that get processed and served by the Middleman app. The link_to helper makes it easy to turn those into links.

Because resources includes images and other assets, you may want to filter down to just html files which could look something like the following:

<ul>
  <% sitemap.resources
       .select { |resource| resource.path =~ /html$/ }
       .each do |resource|
  %>
    <li><%= link_to(resource.path, resource.path) %></li>
  <% end %>
</ul>

Just add the snippet to whatever page you’d like the page listing to appear on.

Mutations with the graphql-client Ruby gem

The graphql-client is a really good library to consume Graphql APIs in Ruby.

You can execute a mutation the same way as if it was a regular query passing the variables you want to use:

require 'graphql/client'
require 'graphql/client/http'

module Api
  HTTP = GraphQL::Client::HTTP.new(ENV['GRAPHQL_API_URL'])
  Schema = GraphQL::Client.load_schema(HTTP)
  Client = GraphQL::Client.new(schema: Schema, execute: HTTP)
end

CreateCityMutation = Api::Client.parse(<<~'GRAPHQL')   
  mutation($name: String) {
    createCity(name: $name) {
      id
    }
  }
GRAPHQL

variables = {name: 'Jacksonville'}
result = Api::Client.query(CreateCityMutation, variables: variables)
puts result.data.create_city.id

Remove indentation from heredocs

Ruby heredocs are a convenient way to include a block of text in Ruby source. Unfortunately, if the text is indented properly with the rest of the code the actual string is also indented. This is often undesired. However, it can be fixed by using the squiggly heredoc.

lorem = <<~STR
  Lorem ipsum dolor sit amet,
  consectetur adipiscing elit,
  sed do eiusmod
STR

lorem #=> => "Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit,\nsed do eiusmod\n"

The squiggly heredoc automatically removes any indentation that is applied to all lines.

Azure key discovery provides modulus and exponent

To verify a JWT coming from Azure, you must provide the ruby JWT package a public key. That public key must be OpenSSL::PKey::RSA value.

The Azure key discovery url provides this public key definition:

{
  "keys": [
    {
      "kid": "X5eXk4xyojNFum1kl2Ytv8dlNP4-c57dO6QGTVBwaNk",
      "nbf": 1493763266,
      "use": "sig",
      "kty": "RSA",
      "e": "AQAB",
      "n": "tVKUtcx_n9rt5afY_2WFNvU6PlFMggCatsZ3l4RjKxH0jgdLq6CScb0P3ZGXYbPzXvmmLiWZizpb-h0qup5jznOvOr-Dhw9908584BSgC83YacjWNqEK3urxhyE2jWjwRm2N95WGgb5mzE5XmZIvkvyXnn7X8dvgFPF5QwIngGsDG8LyHuJWlaDhr_EPLMW4wHvH0zZCuRMARIJmmqiMy3VD4ftq4nS5s8vJL0pVSrkuNojtokp84AtkADCDU_BUhrc2sIgfnvZ03koCQRoZmWiHu86SuJZYkDFstVTVSR0hiXudFlfQ2rOhPlpObmku68lXw-7V-P7jwrQRFfQVXw"
    }
  ]
}

First, I tried using the n value to create a ruby public key:

> OpenSSL::PKey::RSA.new(key['n'])
OpenSSL::PKey::RSAError: Neither PUB key nor PRIV key: nested asn1 error

So n is not the public key? WTF is n. Via the wikipedia page for RSA.

Alice transmits her public key (n, e) to Bob via a reliable, but not necessarily secret, route

n is the modulus. e is the exponent (in this case AQAB), but the ruby library requires a pem. We must convert the modulus and exponent to a pem. Luckily there is a gem for that:

gem install rsa-pem-from-mod-exp

Convert to a pem then to a pubkey:

require 'rsa_pem'

pem = RsaPem.from(key['n'], key['e'])

public_key = OpenSSL::PKey::RSA.new(pem)

And verify your token

decoded_token = JWT.decode token, public_key, true, { :algorithm => 'RS256' }

Parse JSON into an OpenStruct

When you parse json in ruby it is placed into a hash, and you have to access the values with hash syntax:

parsed_data = JSON.parse('{"color": "blue"}')
puts parsed_data["color"] # prints 'blue'

But instead of a hash you can choose to parse it into an OpenStruct by using the object_class option. Then you can use ruby’s method syntax to access the data.

parsed_data = JSON.parse('{"color": "blue"}', object_class: OpenStruct)
puts parsed_data.color # prints 'blue'

H/T Dillon Hafer

Generate Absolute File Path In Ruby

Pass a string to File.expand_path to generate the path to that file or directory. Relative paths will reference your current working directory, and paths prepended with ~ will use the owner’s home directory.

File.expand_path('example.rb')
=> "/Users/avogel/My/Working/Directory/example.rb"

File.expand_path('~/example.rb')
=> "/Users/avogel/example.rb"

Limiting Mock API Restarts with Rerun

On a recent project, we built a Sinatra mock API. A feature we added early on was hot code reloading via the rerun gem. Every time a file in the API changes, Sinatra automatically restarts.

It’s a nice system but not perfect, because the server restarts on changes to any file ending in .rb, .html, .md, and number of other extensions. This captures the README, integration tests, and other undesirables, slowing everything down for no particular reason.

rerun’s --dir flag scopes it to just files in the given directory. We chose the src/ directory, which includes the API, mock database, and helpers.

$ rerun --dir src "ruby src/mock_api.rb"

Setting Struct properties with an index

It’s rare that I get a chance to use structs but yesterday while parsing some xml (!!!) I wrote an algorythm where it would be handy to set values of a struct with an incrementing number rather than a symbol.

Low and Behold! Ruby Structs allow you to set attributes with either the name of the property or the ordinal with which it was declared.

2.5.0 :001 > Apple = Struct.new(:color, :size)
 => Apple
2.5.0 :002 > apple = Apple.new
 => #<struct Apple color=nil, size=nil>
2.5.0 :003 > apple[0] = 'red'
 => "red"
2.5.0 :004 > apple[1] = 'huge'
 => "huge"
2.5.0 :005 > apple
 => #<struct Apple color="red", size="huge">

Structs are a great data structure that I’m going to try to use more in my daily programming.

Log rotation in ruby

Jake wrote a great til about linux log rotation last year. Ruby also has a log rotation alternative built into the stdlib.

Generally, ruby logging looks like this:

require 'logger'
logger = Logger.new('app.log')
logger.info('starting log')

You can also pass a ‘daily’, ‘weekly’ or ‘monthly’ to the logger:

logger = Logger.new('week.log', 'weekly')

If I use this log today, Tue Jan 9, and next week on Wed Jan 17, then ls will show two log files:

week.log
week.log.20180113

20180113 is Saturday the last day of the previous logging period, a new log is started every Sunday. NOTE: no files are deleted with this style of logging, you may potentially run out of space.

You can also choose to log with the number of files to keep and the maximum size of each file.

logger = Logger.new('size.log', 4, 1024000)

In this case after producing more than 4 MB of logging information ls will show you.

size.log
size.log.1
size.log.2
size.log.3

Compared to linux’s logrotate this is missing features, compression for example, but for adding log rotation to your logs quickly and simply it works great.

Documentation

Partial post body matching with webmock

When using webmock to stub out a post, you can specify that the post has a specific body. When dealing with web apis, that body is json and you want to compare the body to a ruby hash, from the webmock README, that looks like this:

stub_request(:post, "www.example.com").
  with(body: {a: '1', b: 'five'})

In the above example, the whole json body has to be represented in the hash. Webmock provides a way to just examine a portion of the json/hash with the hash_including method. This is not the rspec hash_including this is a webmock specific function using the WebMock::Matchers::HashIncludingMatcher class.

stub_request(:post, "www.example.com").
  with(body: hash_including({a: '1'}))

The rest of keyword arguments 🍕

Sometimes you pass some extra keyword arguments to a method that doesn’t handle them and you get the error ArgumentError: unknown keywords: ....

You can just use ** as the last argument to ignore the rest of the keyword arguments:

def make_pizza(cheese:, sauce:, **)
  puts "Making pizza with #{cheese} cheese and #{sauce} sauce"
end

make_pizza(cheese: 'muzzarella', sauce: 'tomato', chocolate: 'white', syrup: 'mapple')

=> Making pizza with muzzarella cheese and tomato sauce

You can also give it a name to group them:

def make_pizza(cheese:, sauce:, **rest)
  puts "Making pizza with #{cheese} cheese and #{sauce} sauce"
  rest.each {|k, v| puts "#{v.capitalize} #{k} is not good for you"}
end

make_pizza(cheese: 'muzzarella', sauce: 'tomato', chocolate: 'white', syrup: 'mapple')

=> Making pizza with muzzarella cheese and tomato sauce
=> White chocolate is not good for you
=> Mapple syrup is not good for you

Slow Down, Sinatra

Right now we’re using Sinatra to mock an API. An important consideration for our frontend experience: what happens when the API takes a long time to respond? How do the pages look when they are waiting on a slow, or absent, server?

Sinatra’s support of before blocks allow us to gauge this:

before do
  sleep(3)
end

get 'some/endpoint' do
  200
end

get 'some/other/endpoint' do
  404
end

With this block, every mocked endpoint will wait three seconds before responding, giving us plenty of time to consider what, in development, would otherwise be an instantaneous delay.

Remove namespaces for easier Xpath queries

Xpath can get strange when namespaces are involved

> doc = Nokogiri::XML(<<-XML)
  <a xmlns='http://www.example.com/xhtml'>
    <b>
      <c></c>
    </b>
  </a>
  XML
> doc.xpath("/a")
[] # returns empty array
> doc.xpath("/*[name()='a']")
# returns the first node

The [name()='a'] isn’t really clear and will become less clear as the query looks for elements deeper in the document. When the namespace doesn’t provide any value, for instance by helping to avoid collisions, or helping to validate the given xml document, then removing the namespace is entirely acceptable.

In Nokogiri you can remove namespaces from the document with, remove_namespaces!.

> doc.remove_namespaces!
> doc.xpath('/a')
[<Node a>]

Now traversing the document with xpath will be significantly more straightforward.

Ruby Binding Class

Ruby’s Binding class allows you to access classes, variables, and methods outside of the current scope.

class Foo
    def bar
        my_var = 20
        binding()
    end
end

Normally, if you made reference to my_var outside of that method, you’d get an error. Binding objects allow you to do this:

my_foo = Foo.new

foo_bar = my_foo.bar
puts foo_bar.eval('my_var')

# 20

Stacking HEREDOCS

I ran across this in a co-workers code yesterday. It made my head hurt for a second. Let’s say you need a a function that takes 4 lines as an argument and outputs each of those lines to stdout.

def print_stanza(line1, line2, line3, line4)
  puts line1, line2, line3, line4
end

print_stanza(<<-LINEA, <<-LINEB, <<-LINEC, <<-LINED)
  You can get all hung up
LINEA
  in a prickle-ly perch.
LINEB
  And your gang will fly on.
LINEC
  You'll be left in a Lurch.
LINED

The second HEREDOC starts where this first one ends, the third HEREDOC starts where the second one ends, etc. Its all valid Ruby and starts to make sense if you look at it long enough.

Most editors I’ve seen haven’t been able to highlight the syntax correctly though. Sorta leaves you in a Lurch.

H/T Brian Dunn

Use dotenv In A Non-Rails Project

Up to now I’ve only used dotenv in a Rails context. It can just as easily be used in a plain old Ruby project.

Install the non-Rails version of the gem.

$ gem install dotenv

Then add the following lines wherever you want dotenv included and loaded. In my case, I want it pulled in as part of my RSpec setup in spec_helper.rb.

require 'dotenv'
Dotenv.load

Your environment variables declared in .env are now accessible via fetches against the ENV object.

ENV.fetch('my_env_var')

`github` as source block in Gemfile

source blocks in a Ruby Gemfile help group gems together that come from the same source. In addition, the Gemfile supports a github block for multiple gems that are coming from the same github repository. In my specific case, there are two gemspecs in the Brian Dunn’s flatware repo.

github 'briandunn/flatware', branch: master do
  gem 'flatware-rspec'
  gem 'flatware-cucumber'
end

With this example, only one change is needed to change the branch that both of those gems will come from.

H/T Brian Dunn

Ruby srand returns the previous seed

srand is a method on Kernel that seeds the pseudo random number generator. It takes a new seed as an argument or calls Random.new_seed if you don’t pass an argument. What’s interesting about it is that it returns the old seed. This has the effect of return a new large random number every time you call srand.

2.4.1 :007 > srand
 => 94673047677259675797540834050294260538
2.4.1 :008 > srand
 => 314698890309676898144014783014808654061
2.4.1 :009 > srand
 => 102609070680693453063563677087702518073
2.4.1 :010 > srand
 => 81598494819438432908893265364593292061

Which can come in handy if you’re playing some Ruby golf and need to generate a huge random number in as few characters as possible.

H/T Dillon Hafer