Today I Learned

A Hashrocket project

143 posts about #ruby

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

Capturing stderr when shelling out in ruby

All of the usual methods of shelling out in ruby (backticks, system, etc.) don’t capture stderr, but there’s an oddly named library open3 that can help with that.

require 'open3'

result_or_err, process_status = Open3.capture2e('cat abc')
puts result_or_err
# 'cat: abc: No such file or directory'
puts process_status
# <Process::Status: pid 6729 exit 1>]

In this case cat abc errors out because there is no abc file, so the command writes to stderr. If there was an abc file, result_or_err would contain the contents of the abc file.

source

Non-ActiveRecord objects in FactoryGirl

Creating non-ActiveRecord objects with FactoryGirl is possible. Classically, a constructor is used to set all the data attributes of an object.

class ParsedString
  attr_reader :abc, :def
  def initialize(string)
    @abc, @def = string.split(?|)
  end
end

In the above class the attributes are set in the constructor by passing in a string that gets split into two parts on \|. Lets use instantiate_with to determine how the object gets instantiated in FactoryGirl.

factory :parsed_string do
  initial_value "123|456"

  initialize_with { new(initial_value) }
end

And then in the test we can FactoryGirl.build, like:

ps = FactoryGirl.build(:parsed_string)
expect(ps.abc).to eq 123
expect(ps.def).to eq 456

Pass name of function as a block

In Ruby it is possible to pass a function name instead of a block to a function expecting a block using &method in the last argument.

For example:

def blocky_fun(foo)
  bar = "hello #{foo}"
  yield(bar)
end

blocky_fun('blocky', &method(:puts))

The result of the above would be that the puts function will be called with “hello blocky” as the first argument.

Expect page to have a link... an exact link

Sometimes, while writing integration tests, we expect there to not be a link on a page, for instance if our page looked like this:

<a href="#create_jetpack">Create Jetpack</a>

And we want to make sure there is no general create link so we expect this:

expect(page).to_not have_link('Create')

But the expectation fails! By default capybara will match if the substring is anywhere in the content.

To avoid this problem use the exact flag like:

expect(page).to_not have_link('Create', exact: true)

Ruby Object Reference in blocks

Usually when I want to set the value of a variable by yielding to a block, I will explicity set it

  x = 'something'
  x = yield(x)
  #....

Turns out ruby will treat different types of objects different when yielded to a block (remember everything is an object). If you are yielding an Integer, or String for (a contrived) example:


def do_stuff_by_val
  do_block_stuff_by_val do |name|
    name += 'rocket'
  end
end

def do_block_stuff_by_val
  company_name = 'hash'
  yield(company_name)
  puts company_name #prints 'hash', not 'hashrocket'
end

If we wanted the company name to change to “hashrocket”, we would need to explicitly set the variable to the result of yielding:

company_name = yield(company_name)

Other types of objects do not behave this way (hash or array). If we yield a hash and set a key, the hash in the calling method will be updated:


def do_stuff_by_object_reference
  do_block_stuff_by_object_reference do |company|
    company[:name] = 'hashrocket'
  end
end

def do_block_stuff_by_object_reference
  company = {}
  yield(company)
  puts company # { name: 'hashrocket'}
end

Resetting the bundler --path

When running bundler install you can pass an option --path <dir>:

bundle install --path ./gems

Which will specify where to install the gems. This is a one time option. Everytime you invoke bundle from here on in you’ll see that its using the directory you specificed with the —path variable as the gem directory.

Bundler keeps track of this option in the .bundler/config file in the folder from where bundler was initially run. That file looks like this:

---
BUNDLE_PATH: "./gems"
BUNDLE_DISABLE_SHARED_GEMS: "true"

To reset the gems path back to the default, remove the BUNDLE_PATH line from the bundle/config file.f

Reinstall all rubies with rvm

When compiling ruby the executable is attached to various C system libs like libreadline. If libreadline is updated then each ruby on the system attached to the old libreadline must be re-compiled. If you are using rvm then this can be done with one command:

rvm reinstall all --force

The --force flag will skip any question the process asks.

Be careful, if you are working on a machine that has installed many rubies and has had many projects for those rubies this could take an extraordinarily long time.

Webmock assertion that an http request was made

Webmock is a library that helps you manager the http requests that are made during a test. Its not advisable to hit production systems with test-generated requests and with webmock you can stub those requests. Sometimes to ensure functionality of your system you might want to assert that the request was made.

stub_request(:get, 'http://www.google.com')
assert_requested(:get, 'http://www.google.com')

The stub_request method also returns a variable that can be passed to the assert function.

get_google = stub_request(:get, 'http://www.google.com')
assert_requested get_google

Ins And Outs Of Pry

When executing commands during a Pry session, you’ll see an incrementing number for each prompt as you enter each statement. These numbers can be used to look up the inputs and outputs of each statement executed during the session. The statements and their results are made available in the array-like _in_ and _out_ objects.

[1] pry(main)> :one
=> :one
[2] pry(main)> 1 + 1
=> 2
[3] pry(main)> ["t", "h", "r", "e", "e"].join
=> "three"
[4] pry(main)> _in_.to_a
=> [nil, ":one\n", "1 + 1\n", "[\"t\", \"h\", \"r\", \"e\", \"e\"].join\n"]
[5] pry(main)> _out_.to_a
=> [nil, :one, 2, "three", [nil, ":one\n", "1 + 1\n", "[\"t\", \"h\", \"r\", \"e\", \"e\"].join\n"]]
[6] pry(main)> _out_[2]
=> 2
[7] pry(main)> _in_[2]
=> "1 + 1\n"

source

Rails/PG Statement Timeout 🐘⏰

By default, Rails does not set a timeout on database statements. For example, this will run for a full day, even if your ruby process goes away.

ActiveRecord::Base.connection.execute(<<~SQL)
  select pg_sleep(86400);
SQL

The good news is that Rails provides a way to prevent this from happening from the database.yml with the statement_timeout variable.

default: &default
  adapter: postgresql
  ...
  variables:
    statement_timeout: 5000

I’m starting to think that this should always be set low. And then explicitly set higher on a per-query basis, when one is expecting something to take a long time.

Chaining Multiple RSpec Change Matchers

It can be handy to use RSpec’s change matchers to determine if some method or process creates a new record.

expect{ Registration.create(attrs) }.to change{ User.count }.by(1)

But what if we are testing a method that creates a couple different records in the system?

RSpec allows us to chain together change matchers with and. Consider this additional contrived example.

expect {
  Project.generate(attrs)
}.to change{ Project.count }.by(1).and \
     change{ User.count }.by(1)

In addition to keeping our tests tight and concise, this approach gives some pretty nice output on failure.

If we were just beginning our implementation with a failing test, we’d see a multi-part failure like the following.

Failure/Error:
  expect {
    Project.generate(attrs)
  }.to change{ Project.count }.by(1).and \
       change{ User.count }.by(1)

     expected result to have changed by 1, but was changed by 0

  ...and:

     expected result to have changed by 1, but was changed by 0

Rails on ruby 2.4: Silence Fixnum/Bignum warnings

Wanna use the latest ruby, but don’t really need to be perpetually reminded that rails hasn’t caught up with this latest ruby design change? Change the top of your config/application.rb thusly:

require_relative 'boot'

verbose = $VERBOSE
$VERBOSE = nil
require 'rails/all'
require 'active_support/core_ext/numeric/conversions'
require 'active_job/arguments'
$VERBOSE = verbose
Bundler.require(*Rails.groups)

module MyApp
  class Application < Rails::Application
  end
end

Yes, this will also suppress any other ruby warnings from the loading of rails code. ¯\_(ツ)_/¯

Rerun Only Failures With RSpec

After running a big test suite, I may have a bunch of output on the screen including the results of a couple test failures. I like to bring the context of the test failures front and center and make sure they are consistent test failures (not flickering failures). Instead of copying and pasting each failure, I can rerun rspec in a way that executes only the test cases that failed.

$ rspec --only-failures

This feature requires that you set a file for RSpec to persist some state between runs. Do this in the spec/spec_helper.rb file. For example:

RSpec.configure do |config|
  config.example_status_persistence_file_path = "spec/examples.txt"
end

See more details here.

h/t Brian Dunn

Use A Case Statement As A Cond Statement

Many languages come with a feature that usually takes the name cond statement. It is essentially another way of writing an if-elsif-else statement. The first conditional in the cond statement to evaluate to true will then have its block evaluated.

Ruby doesn’t have a cond statement, but it does have a case statement. By using a case statement with no arguments, we get a cond statement. If we exclude arguments and then put arbitrary conditional statements after the when keywords, we get a construct that acts like a cond statement. Check out the following example:

some_string = "What"

case
when some_string.downcase == some_string
  puts "The string is all lowercase."
when some_string.upcase == some_string
  puts "The string is all uppercase."
else
  puts "The string is mixed case."
end

#=> The string is mixed case.

source

Ruby Arguments Can Reference Other Arguments

I love the dark corners of Ruby. Today we discovered this one by basically guessing that it might work:

def foos(foo, bar = foo.upcase + "!")
  puts foo
  puts bar
end
2.3.1 :001 > foos('foo')
foo
FOO!
 => nil

That’s right; bar references another argument, foo. And we can also call a method on it. And concatenate it with a string, because, why not? The ‘principle of least surprise’ strikes again.

h/t Brian Dunn

Ruby $!

This week I enjoyed using Ruby’s built-in global variables, including $!. $! refers to the last error that was raised, and is useful in lots of different contexts such as Rake tasks.

Here it is in action:

:001 > begin; raise NameError; rescue; puts "The error we raised was #$!."; end
The error we raised was NameError.

And a list of these global variables:

https://github.com/ruby/ruby/blob/trunk/lib/English.rb

For a slightly less bare-metal implementation, require this library and utitlize the friendly names:

:001 > require 'english'
 => true
:002 > begin; raise NameError; rescue; puts "The error we raised was #$ERROR_INFO."; end
The error we raised was NameError.

The case of meridian in Ruby's strftime

When formatting dates using strftime, you can include AM/PM with %p:

puts Time.now.strftime("%H:%M:%S %p")
# 20:40:52 PM

I didn’t want PM capitalized, so I did this:

puts Time.now.strftime("%H:%M:%S %p").downcase
# 20:40:52 pm

But I found that you can also just use %P:

puts Time.now.strftime("%H:%M:%S %P")
# 20:40:52 pm

Yes that’s right, when you want lowercase AM/PM, use uppercase P. When you want upppercase, use lower. It couldn’t be more straightforward folks.

Using Ruby Hash in `gsub`

Ruby String#gsub method also accepts a hash as second argument. This is perfect for some situations like mapping a phone-word to phone number:

phonewords = {
  'a' => 2, 'b' => 2, 'c' => 2,
  'd' => 3, 'e' => 3, 'f' => 3,
  'g' => 4, 'h' => 4, 'i' => 4,
  'j' => 5, 'k' => 5, 'l' => 5,
  'm' => 6, 'n' => 6, 'o' => 6,
  'p' => 7, 'q' => 7, 'r' => 7, 's' => 7,
  't' => 8, 'u' => 8, 'v' => 8,
  'w' => 9, 'x' => 9, 'y' => 9, 'z' => 9,
}
phone = "1-800-map-gsub"
puts phone.gsub(/[a-z]/, phonewords)
# => 1-800-627-4782

h/t @joshuadavey

Expecting change with RSpec #rails #testing #rspec

Usually when I try to test if a value has changed after a method has been called I will assert the initial value as one expectation followed by the action that changes it, and finally assert the value has changed.

For example this test will check if a user’s bad login attempts are incremented when the user.record_bad_login! method is called:

describe '#record_bad_login!' do
  let(:user) { FactoryGirl.create(:user) }

  it 'increments the bad login attempts count' do
    expect(user.failed_login_attempts).to eq(0)
    user.record_bad_login!
    expect(user.failed_login_attempts).to eq(1)
  end
end

RSpec provides us with a more straight forward way to “oneline” this type of test while making it more declarative:

describe '#record_bad_login!' do
  let(:user) { FactoryGirl.create(:user) }

  it 'increments the bad login attempts count' do
    expect { user.record_bad_login! }.to change { user.failed_login_attempts }.from(0).to(1)
  end
end

Read more here: https://www.relishapp.com/rspec/rspec-expectations/v/2-0/docs/matchers/expect-change

Or Operator Precedence

What’s the difference between || and or in Ruby?

Let’s look at an example to find out. First, let’s start with some boolean variables:

> a, b = false, true
=> [false, true]

Now, let’s try the different or operators:

> a || b
=> true
> a or b
=> true

Cool, they seem to work as expected.

Finally, let’s capture the result in a variable:

> c = a or b
=> true
> c
=> false

But why is c false and not true? Operator precedence. The assignment operator (=) takes precedence over the or operator causing c to be assigned to the value of a (false) before or’d with b.

source

Ruby XOR operator

The ^ acts is a boolean XOR operator in Ruby when the arguments are boolean. It wants only one true value in an expression in order to be considered true:

true ^ false ^ false
# => true

true ^ false ^ true
# => false

Let’s look at 5 trues:

true ^ true ^ true ^ true ^ true 
# => true

How is this true? Each ^ is evaluated one at a time. Since true ^ true is not exclusive, it is false. So after the first evaluation, we have:

false ^ true ^ true ^ true

# false ^ true is true

true ^ true ^ true

#true and true is false
false ^ true
# true

Using this same logic we can see why true ^ true ^ true ^ true is false.

Return `MatchData` with `$~`

Check out this Ruby regex:

2.1.0 :001 > "totally gourmet" =~ /totally/
 => 0

There’s a match at index zero. But what is the thing that actually matched? Enter $~:

 2.1.0 :002 > $~
  => #<MatchData "totally">

$~ stores the result of the last regex, and can be called later on. This is equivalent to the match method:

2.1.0 :003 > /totally/.match('totally gourmet')
 => #<MatchData "totally">

h/t Brian Dunn

Dealing with exit signals on Ruby

When working on CLIs it’s useful to treat if the application exists. Image you have a long application and even if the user hits a <Ctrl+c> to kill the process you still want to display the results so far.

There is a Ruby Kernel method at_exit that runs the block passed to it if the current process exists. Here is an example:

# print_at_exit.rb
at_exit { puts "Come back later!" }
puts "sleeping for 5 secs"
sleep 5

If you run this file and wait it until it finishes you’ll get:

ruby print_at_exit.rb                                                                                                                {130}
#=> sleeping for 5 secs
#=> 
#=> Come back later!

But now, if you run it and hit <Ctrl+c> for finishing the process earlier you’ll get:

ruby print_at_exit.rb
#=> sleeping for 5 secs
^C
#=> Come back later!
#=> print_at_exit.rb:6:in `sleep': Interrupt
#=>         from print_at_exit.rb:6:in `<main>'

h/t @nikkypx

Ruby defined? method evaluation

Today I learned that Ruby Object method defined? evaluates if the argument is anything recognizable, not just if the variable are set. Here are some examples:

undefined_foo #=> NameError: undefined local variable or method `undefined_foo'
defined? undefined_foo #=> nil

defined? nil #=> "nil"

defined? "foo" #=> "expression"
defined? [] #=> "expression"
defined? def x; end #=> "expression"
defined? class Foo; end #=> "expression"

defined? @foo = :bar #=> "assignment"

@foo = :bar
defined? @foo #=> "instance-variable"

foo = :bar
defined? foo #=> "local-variable"

def foo; end
defined? foo() #=> "method"

class Foo; end
defined? Foo #=> "constant"

h/t @VidalEkechukwu

Ruby's `__LINE__`

Debugging a tough problem, with terse, unhelpful error messages? Sometimes puts driven development can only take you so far.

A corner of Ruby I sometimes forget about are the double-underscore methods on Object. Today I learned a new one: __LINE__.

Here it is in the console:

2.1.0 :001 > puts __LINE__
1
 => nil
2.1.0 :002 > puts __LINE__
2
 => nil
2.1.0 :003 > puts __LINE__
3
 => nil

Try putting this above and below code you think might be causing an issue.

A new tool for my debugging toolbox. 🔨

Documentation

h/t Brian Dunn

Bundle in parallel using full CPU powa!!! #rails

Don’t you wish there was a faster way to install your bundled gems for a project? Especially when cloning an existing Rails application from Github?

![more powa](https://i.imgur.com/HFgXC3H.png)

It turns out that since Bundler v1.5, Bundler supports Parallel Install.

To run bundler with parallel install use the --jobs option or -j for short.

Assuming your computer has 4 cores you can try

$ bundle install --jobs 4
$ # or
$ bundle install -j4

Finally if you want to set bundler to always use parallel install you can run this command:

bundle config --global jobs 4

The bundler team has seen speedups of 40-60%. That’s amazing!

h/t Micah Cooper && bundler documentation

Ruby Method arity

Today I learned that Method#arity does not consider blocks.

Also it returns negative with splat arguments.

Finally, all keyword arguments counts 1.

class Foo
  def one; end
  def two(a, b); end
  def three(a, *b); end
  def four(a, &b); end
  def five(a, b:, c:); end
end

puts Foo.instance_method(:one).arity
# => 0
puts Foo.instance_method(:two).arity
# => 2
puts Foo.instance_method(:three).arity
# => -2
puts Foo.instance_method(:four).arity
# => 1
puts Foo.instance_method(:five).arity
# => 2

h/t @higgaion

Require Entire Gemfile In Pry Session

Want to experiment in a pry session with some of the gems in your project’s Gemfile? You can quickly require all the gems for your project using Bundler’s #require method.

Just require bundler itself and then execute Bundler.require. Everything will be loaded in.

> require 'bundler'
=> true
> Bundler.require
=> [Gem::Dependency.new("devise", Gem::Requirement.new([">= 0"]), :runtime),
 Gem::Dependency.new("rails", Gem::Requirement.new(["= 4.2.5"]), :runtime),
 Gem::Dependency.new("pg", Gem::Requirement.new(["~> 0.15"]), :runtime),
...

String Interpolation With Instance Variables

When using regular variables with string interpolation in Ruby, they must be wrapped in curly braces (e.g. "This is a #{variable}"). With instance variables (and class and global variables) you can just use the octothorp followed directly by the variable.

Here is an example of this in action:

class Person
  def initialize(name)
    @name = name
  end

  def whoami
    puts "I am #@name"
  end
end

bob = Person.new("bob")
#=> #<Person:0x007fdaf3291618 @name="bob">

bob.whoami
# I am bob

This is a handy shortcut, but may affect readability and/or result in an interpolation error at some point. Your mileage may vary.

h/t Josh Davey

Edit Previous Parts Of The Pry Buffer History

Each line of Ruby you enter into a Pry session is recorded with a number in the buffer history. Pry keeps this buffer history so that you can recall parts of it for editing and subsequent execution.

If you use the edit command by itself, Pry will open the previous Ruby statement in your default editor. But what if you want to edit a statement from a while back? Or even a series of statements?

Use the --in flag with edit either specifying a single record in the buffer history or a range of records.

$ pry
[1] pry(main)> puts "Hello"
Hello
=> nil
[2] pry(main)> puts "World"
World
=> nil
[3] pry(main)> puts "People"
People
=> nil
[4] pry(main)> edit --in 1..2
Hello
World
=> nil