Today I Learned

35 posts about #ruby

How not to check files with RuboCop

While using rubocop on the project it came out that a lot of fixes had to be done, not only those connected with my task.

So I came up with the idea to list and fix only these files that I have changed.

Two of them were:

- bin/start-dev
- config/config.yaml

Calling the command directly on those files:

rubocop bin/start-dev
rubocop config/config.yaml

forced rubocop to check them and throw offenses that were not supposed to appear despite the fact, these files were not included as files to be checked in:


So by default rubocop ignores such files.

Persisting custom methods in ruby ​​interpreter

To enhance your interpreter you may add some custom method or functions to your .irbrc or .pryrc files that usually can be found in your home directory ~/.irbrc, ~/.pryrc

How example ~/.pryrc file may look like:

def v_trace { |x| x.include?(Rails.root.to_s) }

class Object
  def m?(method_name)
    self.methods.grep /#{method_name}/

After saving the above changes, run pry command and take advantage of new features


Extracting substring using regexp

If you want to grab some part of the string using a regular expression there is a high chance that you are using match or scan method

pry(main)> '$$$ Tony Stark $$$'.scan(/\w+ \w+/)[0] # -> Tony Stark

pry(main)> '$$$ Tony Stark $$$'.match(/(\w+ \w+)/)[1] # -> Tony Stark

next time try this

pry(main)> '$$$ Tony Stark $$$'[/(\w+ \w+)/] # -> Tony Stark

More advanced examples

pry(main)> '$$$ Tony Stark $$$'[/\${3}\s(\w+\s\w+)\s\${3}/, 1] # -> Tony Stark

pry(main)> '$$$ Tony Stark $$$'[/\${3}\s(?<name>\w+\s\w+)\s\${3}/, :name] # -> Tony Stark

as always visit to validate your regexp

One liner for recursively transforming to OpenStruct

hash = {
  name: "Bob",
  school: {
    name: "RSpec school",
    level: "secondary",
    region: {
      countryCode: "pl",
      region: "Bielsko"

JSON.parse(hash.to_json, object_class: OpenStruct)
=> #<OpenStruct name="Bob", school=#<OpenStruct name="RSpec school", level="secondary", region=#<OpenStruct countryCode="pl", region="Bielsko">>>

and here’s benchmark if you’re interested

require 'benchmark'
require 'json'

def build_nested_hash
    name: 'Bob',
    school: {
      name: 'RSpec school',
      level: 'secondary',
      region: {
        countryCode: 'pl',
        region: 'Bielsko'

n = 100_000 do |benchmark|'OpenStruct') do
    n.times do
      JSON.parse(build_nested_hash.to_json, object_class: OpenStruct)
  end'Hash') do
    n.times do
 $ ruby benchmark.rb
               user     system      total        real
OpenStruct  4.118626   0.010050   4.128676 (  4.129240)
Hash        0.986823   0.002627   0.989450 (  0.989538)

As you can see it’s quite slower than parsing to hash, but still rather acceptable

tested on ruby 2.6.5(M1)

Creating array without parentheses

One of the unique features in ruby is that you don’t need to use parentheses(most of the time). For example, you can skip them when you are defining or invoking a method.

What I didn’t know was that you don’t need brackets while creating a new array

pry(main)> new_array = 1, 2, 3 # => [1, 2, 3]

More examples:

pry(main)> new_array = 1, second = 2, third = 3 # => new_array = [1, 2, 3]; second = 2; third = 3
pry(main)> new_array = first = 1, second = 2, third = 3 # => new_array = [1, 2, 3]; first = 1; second = 2; third = 3

Aggregating failures in RSpec

Sometimes our examples have multiple independent expectations. In such cases, the default behavior of RSpec to abort on the first failure may not be ideal.

Consider the following example:

it do

  expect(response.status).to eq(200)
  expect(response.body).to eq('[{"name":"Johny"}]')

If our API returns a wrong status, RSpec will print the following output:

1) Users GET /api/v1/users example at ./spec/requests/api/v1/users_spec.rb:9
   Failure/Error: expect(response.status).to eq(200)
     expected: 200
          got: 201

While this gives us feedback on the response’s status being wrong, it entirely skips the assertion on the response’s body, even though having both results could make debugging easier.

RSpec has a neat solution to this: Aggregating Failures.
To use it, you can either tag the whole example with :aggregate_failures:

it 'does something', :aggregate_failures do

Or you can just wrap your assertions in an aggregate_failures block:

it do

  aggregate_failures do
    expect(response.status).to eq(200)
    expect(response.body).to eq('[{"name":"Johny"}]')

This will change RSpec’s default behavior and will group both expectations:

1) Users GET /api/v1/users example at ./spec/requests/api/v1/users_spec.rb:9
   Got 2 failures from failure aggregation block.

   1.1) Failure/Error: expect(response.status).to eq(200)
          expected: 200
               got: 201

   1.2) Failure/Error: expect(response.body).to eq('[{"name":"Johny"}]')
          expected: "[{\"name\":\"Johny\"}]"
               got: "[{\"name\":\"Jane\"}]"

More info: documentation

How *not* to use heredoc in Ruby

Sometimes you inherit a codebase that’s so bad, it confuses your IDE. Let’s start with this beauty:

   LEFT JOIN industries
   ON = users.industry_id
 not(industries: { id: 1 })

What’s your first thought when you see something like that? If you’re convinced it’s a broken code then feel free to play with it your Rails project, just substituting model / table names for something more appropriate in your case.

The above “query” works due to the same reason as the code below:

p <<SQL.gsub(/\s+/, " ").strip
SELECT * FROM     users

After I got my head around this unintuitive syntax, as an exercise I came with a following brainfart, which you may treat as a dessert:

bullet_time = <<""::b
 this works as well
 But FFS, don’t do this

p bullet_time

Form objects with Active Admin

In this example, I’m using form object’s implementation from Selleo/pattern, but the solution shouldn’t differ much for other popular implementations.

If you want to use a form object class with the Active Admin, there are a few things you need to take care of:

  • Overwrite controller’s build_new_resource and find_resource methods.
    ActiveAdmin uses build_new_resource to build a new resource for new and create actions, and find_resource for retrieving resource in edit, update and show.
    We don’t want to use our form object in show, so we check action_name in find_resource.
contoller do
  def build_new_resource
  def find_resource
    return if[edit update])
  • Permit all params
    We don’t want to duplicate our form’s responsibility with Rails’ Strong Parameters.
controller do
  before_action -> { params.permit! }
  • Either overwrite the default form, or extend it with the names of your attributes, as it won’t be able to infer them automatically from the form object’s class.
form do |f|

Repeating all of that for every page where you want to use a form object can get tedious, so here’s a dynamic concern which you can use to include those changes to your page quickly:

module UsesFormObject

  def with_class(form_object_class) do
      define_singleton_method(:included) do |activeadmin_dsl|
        activeadmin_dsl.instance_eval do
          form do |f|

          controller do
            before_action -> { params.permit! }


            define_method(:build_new_resource) do

            define_method(:find_resource) do
              return if[edit update])


You can use it like that:

ActiveAdmin.register Order do
  include UsesFormObject.with_class(OrderForm)

Nested assignments inside array assignment in Ruby

In Ruby, an assignment returns its value.

> a = 1
=> 1

You can slightly abuse it to nest variable assignments inside an array assignment:

a = [
  b = 1,
  c = 2

> a
=> [1, 2]
> b
=> 1
> c
=> 2

You could even go a step further to nest variable assignments inside array assignments nested inside yet another array assignment - although it seems to be losing readability at this point:

a = [
  *b = [
    c = 1,
    d = 2
  *e = [
    f = 3,
    g = 4

> a
=> [1, 2, 3, 4]
> b
=> [1, 2]
> c
=> 1

This may be useful in tests, where you have multiple variables, which you need to use both individually and collectively, and you want to have descriptive variable names:

triggers = [
  trigger_with_conditions_fulfilled = create(:trigger),
  trigger_with_conditions_not_fulfilled = create(:trigger)

allow(trigger_with_conditions_fulfilled).to receive(:conditions_fulfilled?).and_return(true)
allow(trigger_with_conditions_not_fulfilled).to receive(:conditions_fulfilled?).and_return(false)

Dependent types in Ruby with dry-rb

Dry-rb supports dependent types (i.e., it allows you to match types based on the value):

class HasA < Dry::Struct
  attribute :a, Types::Symbol

class HasB < Dry::Struct
  attribute :b, Types::Symbol

class ShortText < Dry::Struct
  attribute :text, Types::String.constrained(max_size: 3)

class LongText < Dry::Struct
  attribute :text, Types::String.constrained(min_size: 4)

class OurStruct < Dry::Struct
  attribute :a_or_b, Types::Array.of(A | B)
  attribute :texts, Types::Array.of(ShortText | LongText)

> struct =
>   a_or_b: [{ a: :abc } , { b: :def }],
>   texts: [{ text: 'abc' } , { text: 'defgh' }],
> )
=> #<OurStruct
=> #  a_or_b=[#<A a=:abc>, #<B b=:def>]
=> #  texts=[#<ShortText text="abc">, #<LongText text="defgh">]
=> #>

You can use it to, e.g., parse data and then attach specific behavior to it:

module Types
  EventData = Types::Hash.schema(name: Types::String, value: Types::Integer)

class GroupCondition < Dry::Struct
  attribute :event, Types::Value(:group_formed)
  attribute :data, Types::EventData

  def applicable?
    Group.exists?(name: data.fetch(:name)) && data.fetch(:value) >= 123

class InvitationCondition < Dry::Struct
  attribute :event, Types::Value(:invitation_sent)
  attribute :data, Types::EventData

  def applicable?
    Invitation.exists?(value: data.fetch(:value))

class ConditionsSet < Dry::Struct
  attribute :conditions, Types::Array.of(GroupCondition | InvitationCondition)

> set =
>   conditions: [
>     { event: :invitation_sent, data: { name: 'abc', value: 123 } },
>     { event: :group_formed, data: { name: 'def', value: 456 } }
>   ]
> )
=> #<ConditionsSet
=> #  conditions=[
=> #    <InvitationCondition event=:invitation_sent data={:name=>"abc", :value=>123}>,
=> #    <GroupCondition event=:group_formed data={:name=>"def", :value=>456}>
=> #  ]
=> #>
> set.conditions.all?(&:applicable?)
=> true

Ruby's "Class < OtherClass"


You can use ruby’s “greater than” and “less than” operators for checking class inheritance.

Foo < Bar
# returns true if Foo is included in Bar's ancestors
# returns false if Foo is a Bar's descendant
# otherwise returns nil

Source: Selleo/pattern - lib/patterns/query.rb

Real life examples:

User < ActiveRecord::Base # => true
ActiveRecord::Base < User # => false
TrueClass < String # => nil

How to inline specify gem version in the shell

The problem:

I’ve multiple versions of rails gem and I’d like to generate new app but in different version than the default one

> gem list | grep rails
rails (5.2.4, 5.2.3,

Typing just rails new my_app would generate rails app with version 5.2.4

The solution:

It turns out, that it’s possible to specify gem version like this:

rails _4.2.11.1_ new my_app

It works with bundler, rspec and probably any other gem callable from shell as well

bundle _1.17.2_ install
rspec _3.8.0_

Tested on zsh + asdf and zsh + rvm

SQLite3::RangeException: column index out of range

If you encounter the error:

SQLite3::RangeException: column index out of range

one of the things you might do is to investigate the broken query (joins/includes) and check if there is any association with limit condition like in the example below:

has_one :current_term, -> { order(created_at: :desc).limit(1) }, class_name: "Term"

the solution is to remove limit

has_one :current_term, -> { order(created_at: :desc) }, class_name: "Term"

Skipp model instance callbacks in Sequel

In ActiveRecord there are several ways to update a record. Each technique / method is described in the documentation, so you are aware of which methods will trigger the callbacks and which ones will skip them. In Sequel it’s not that obvious. And the update, update_all and save - callbacks are triggered in all cases.

It turns out that you need to drop down on a dataset level. Either by:

HuddleScore.where(id: 14900).update(updated_at: DateTime.parse("04/11/2019 14:53"))

or in the following manner:

model_instance.this.update(updated_at: DateTime.parse("04/11/2019 14:53"))

Accessing helpers and paths in Ruby On Rails console

You can access path/url in two ways:

1) using app, for instance: app.game_path(1)

2) including Rails.application.routes.url_helpers, for instance:

include Rails.application.routes.url_helpers

both will result with a string => "/games/1"

The above knowledge will be handy if you are going to use helpers with paths.

Accessing helpers in rails console

The easiest way to do this is through helper object, for instance

helper.number_to_currency(100) => "$100.00"

helper.link_to("test", app.game_path(1), method: :get) =>

"<a data-method=\"get\" href=\"/games/1\">test</a>"

The nice thing about is that you can use your helpers in the same fashion:

Some real life usage:

module EventsHelper
  def event_form_header(event)
    content_tag :h2, class: 'text-center pull-left' do
        "#{event.category.capitalize} for #{}"
        "Global #{event.category.capitalize}"


"<h2 class=\"text-center pull-left\">Cash_ladder for Gentleman Test Game</h2>"

Unfortunately, if you want to use your helper in console and this helper uses path inside you will have to first include Rails.application.routes.url_helpers in this helper (which is of course not needed if you are using it in views)

module PlayerHelper
  include Rails.application.routes.url_helpers

  def user_player_info(player)
    if player.user.removed_by_user?
      'User removed his account'
      content_tag(:div, player.user.username) +
                game_player_path(, player),
                class: 'highlight')

helper.user_player_info(Player.last) =>

"<div>Varian</div><a class=\"highlight\" href=\"/games/2-hots/players/3\">Thrall</a>"

Ruby On Rails console sandbox mode

If you want to perform some data migration/modification on production and you want to be sure that it will not break anything you can always perform a test run using rails console with --sandbox flag.

After entering console with

rails console production --sandbox

you will get following information

Loading production environment in sandbox (Rails X.X.X)
Any modifications you make will be rolled back on exit

You can now safely perform data migration and check if everything is OK.

Truncate database in Ruby on Rails 6

Insteaed of chaining rails db:drop, rails db:create and rails db:migrate we are finally able to use one commend that will clean databse in case you have to seed it one more time:

rails db:truncate_all

NOTE: In older version of rails you can always use database_cleaner gem. Thanks to this gem you can run DatabaseCleaner.clean_with :truncation in rails console or create a rake task on your own.

Ruby on Rails with RSpec - yielding multiple arguments

If you are yielding many things in one yield statement, for instance:

def each
  author.books.each do |book|

you are able to test it in RSpec using yield_successive_args with arguments packed within an array:

expect { |b| book_collection.each(&b) }.to yield_successive_args(
  [book_decorator1, epic_book_decorator1],
  [book_decorator2, epic_book_decorator2]

** operator as a way to merge hashes && symbolize_keys

array.group_by(&:something).map do |scheduled_at, form_submissions|
  { scheduled_at: scheduled_at, **form_submissions.first.payload.symbolize_keys }

What’s happening here?

The double splat operator acts as a handy way to merge two hashes. But since the payload contains a json with keys being strings, it will throw an exception of TypeError: hash key "name" is not a Symbol

This is because of the Ruby’s implementation internal detail: The keyword arguments, which the double-splat operator is supposed to capture, are expressed in Ruby as Symbols. Hence, the symbolize_keys call.

Arel basic use case

veg =

query = veg[:created_at].gteq( 5.days.ago ).and(

#  "vegetables"."created_at" >= '2016-12-13 03:54:28.575342'
#    AND ("vegetables"."color" = 'green' OR "vegetables"."gardener" = 'Susan')

Vegetable.where( query )

Triggered by birthday-cake-top-consumer!

FactoryBot: Constructing objects using Dry::Types

If you face error similar to the one below

Dry::Struct::Error: [] :some_attribute_name is missing in Hash input

when building objects of the class using Dry::Types and FactoryBot, be advised, that

Although factory_bot is written to work with ActiveRecord out of the box, it can also work with any Ruby class. For maximum compatibility with ActiveRecord, the default initializer builds all instances by calling new on your build class without any arguments. It then calls attribute writer methods to assign all the attribute values. While that works fine for ActiveRecord, it actually doesn’t work for almost any other Ruby class.

The fix is to add following line to your factory definition

initialize_with  { new(attributes) }

How to change stubbed return value with another stub?

Simple - just re-define spy as a result of another stub

valid_token = instance_double(ValidToken)
allow(ValidToken).to receive(:new) { valid_token }
allow(valid_token).to receive(:to_s) { '123' }
allow(valid_token).to receive(:clear!) do
   allow(valid_token).to receive(:to_s) { '456' }
valid_token =
valid_token.to_s # 123
valid_token.to_s # 456

Stubbing responses from AWS services

We have started integration with Amazon SQS recently and did need to write some unit tests related to it. Unfortunately stubbing AWS client library the regular way turned out to be pretty cumbersome and challenging. Fortunately AWS SDK for ruby provides tools that make it pretty comfortable.

# Simple stubbing...
sqs_response_mock =
sqs_response_mock.messages << 'abc')
Aws.config[:sqs] = {
    stub_responses: {
        receive_message: sqs_response_mock

# ...allows properly polling the queue
poller ='')
poller.poll do |msg|
  puts msg.body

# => abc

Documentation can be found here

Passing multiple exceptions to rescue

To rescue multiple classes of exceptions, you have to pass them as a list to rescue method. Passing as an array won’t work, unless you prefix it with * (effectively breaking it into a list)

# Works
  # some code here    
rescue ExceptionA, ExceptionB
  puts "uff, thanks!"

# Doesn't work
  # some code here    
rescue [ExceptionA, ExceptionB]
  puts "uff, thanks!"

# Works
EXCEPTIONS = [ExceptionA, ExceptionB]
  # some code here    
  puts "uff, thanks!"


We’ve recently experienced some peculiar errors when processing capybara-based automation scripts on Heroku. Most of the time, the error returned did not show anything useful…

Selenium::WebDriver::Error::NoSuchDriverError: no such session

yet for a brief period of time, following error was reported when attempting to access capybara session

Selenium::WebDriver::Error::UnknownError: unknown error: session deleted because of page crash
from tab crashed

Finally, after spotting this comment we’ve reduced chrome window size from 1920,1200 to 1440,900 and the problem is no longer present.

The root reason is unknown, but most likely it is at least partially related to running out of memory (reference). Most of recommendations when using docker in this scenario, was to increase shm-size, by providing --shm-size=2g to docker run. That was not an option for us though…

Hope it helps in case you run into similar situation.

Difference between <<~ and <<-

If you use heredoc in ruby you can choose one of two syntaxes the first <<- it takes all whitespace before stared of text so

def custom_html  

so if you will get string with all withspaces " <html>\n <body>\n </body>\n </html>\n" second <<~ will strip it so

def custom_html  

will give you will get text "<html>\n <body>\n </body>\n</html>\n"


When you not only use Capybara for rspec, you would like to change the wait time to visit the page, so I think it can be useful.

   Capybara.register_driver :selenium do |app|
      client =
      client.read_timeout = 20, browser: :chrome,  http_client: client)