Today I Learned

46 posts about #rails

Interval column in Rails 6.1 and AS' Duration#parts

Rails 6.1 introduces support for PostgreSQL interval datatype by mapping it to the ActiveSupport::Duration:

add_column :triggers, :time_offset, :interval
> + 2.hours + 3.minutes + 4.seconds)
=> #<Trigger:0x00007fbe18c9af08
=> #  id: nil,
=> #  time_offset: 1 day, 2 hours, 3 minutes, and 4 seconds
=> #>

Under the hood, it serializes duration to iso8601:

> (1.year + 2.days + 3.hours + 4.minutes + 5.seconds).iso8601
=> "P1Y2DT3H4M5S"
> ActiveSupport::Duration.parse("P1Y2DT3H4M5S")
=> 1 year, 2 days, 3 hours, 4 minutes, and 5 seconds

Today I learned, that iso8601 does not support mixing “weeks” part with any other date parts:

> 1.week.iso8601
=> "P1W"
> (1.week + 2.days).iso8601
=> "P9D"

Because of that, if you try to save a duration with weeks and other date parts, those weeks will be converted to days:

> trigger.time_offset = 1.week + 2.days + 3.hours
=> 1 week, 2 days, and 3 hours
=> {:weeks=>1, :days=>2, :hours=>3}
=> true
=> {:days=>9, :hours=>3}

While saving just weeks, or any duration without weeks, will give you consistent results:

> trigger.time_offset = 5.weeks
=> 5 weeks
=> true
> trigger.time_offset
=> 5 weeks
> trigger.time_offset = 500.days
=> 500 days
=> true
> trigger.time_offset
=> 500 days

with_options in Rails

Active Support in Rails adds a with_option method to the Object (i.e., it’s available everywhere).

It yields a proxy object, which adds given options to each method call:

def log(arg, **kwargs)
  puts(arg: arg, kwargs: kwargs)

> with_options a: 123 do |with_a|
>   with_a.log(:called_with_a)
>   log(:called_without_a)
>   with_a.log(:called_with_a_and_b, b: 456)
>   with_a.log(:called_with_overwritten_a, a: 789)
> end
{:arg=>:called_with_a, :kwargs=>{:a=>123}}
{:arg=>:called_without_a, :kwargs=>{}}
{:arg=>:called_with_a_and_b, :kwargs=>{:a=>123, :b=>456}}
{:arg=>:called_with_overwritten_a, :kwargs=>{:a=>789}}

You can use it to group multiple similar calls or to avoid repetition.
E.g., if you need to call I18n multiple times in a place that does not support inferring the scope:

I18n.with_options(scope: 'active_admin.orders.edit.details') do |i18n|
  panel i18n.t('header', order_number: order_number) do

That way, each call to i18n.t will have the correct scope option without you having to repeat it over and over.

Another use case is to group conditional ActiveModel validations:

class EventForm < ApplicationRecord
  with_options if: :teams_enabled? do |teams|
    teams.validates :team_max, presence: true
    teams.validates :team_descripion, length: { maximum: 500 }

Searching for files in AWS s3 from rails console

first set your credentials to AWS_ACCESS_KEY_ID, AWS_REGION, AWS_SECRET_ACCESS_KEY in .env


rails c
s3_client =

if credentials are set it will by default look for them there, but there is much more other options

then we can

    bucket: 'S3_BUCKET',
    max_keys: 'max_records by def 1000',
    prefix: 'some/key/to/file/'

It is good to note that prefix doesn’t begin from ‘/‘

also it works from ‘aws-sdk’ > 2.3.2

Different values with every call from stubbed method

Today I needed to get two different responses from called client to test it. To do this you just stub method classic way but you provide multiple arguments:

allow(Service).to receive(:call).and_return('1', '2')

When service is called for first time it returns '1' and for second time it’s returning '2'.

It also works with WebMock :

stub_request(:get, "").to_return({body: "abc"}, {body: "def"})

Uniq index with one optional column, which can be NULL

When you want to do it, Rails allows creating this index.

In this case, table_invitation_id column is optional.

Example below:

add_index :orders, [:creator_id, :brunch_series_id, :table_invitation_id], unique: true

But then we are able to create two the same records and it’s obscure.

Example below doesn’t raise ActiveRecord::RecordNotUnique: PG::UniqueViolation

2.times { Order.create(creator_id: 1, brunch_series_id: 10, table_invitation_id: nil } 


We are not able to do it, Postgres doesn’t have support for it “[…] Null values are not considered equal” -

We just have to create two indexes one when a value is not null and the second when is null.

add_index :orders, [:creator_id, :brunch_series_id, :table_invitation_id],
              unique: true,
              where: 'table_invitation_id IS NOT NULL',
              name: 'uniq_order_with_table_invitation_id'
add_index :orders, [:creator_id, :brunch_series_id],
              unique: true,
              where: 'table_invitation_id IS NULL',
              name: 'uniq_order_without_table_invitation_id'

Then index works fine and code below exists only one Order

2.times { Order.create(creator_id: 1, brunch_series_id: 10, table_invitation_id: nil } 

rack difference after upgrade from 2.0.7 to 2.2.2

I had to update rack because of the security issues with the previously used version. Unfortunately specs for this controller:

module Internal
  module Dashboard
    class StatisticsController < Sinatra::Base
      include Serial::RailsHelpers

      use Rack::Auth::Basic, 'Protected Area' do |username, password|
        username == App.config.internal_basic_auth_name &&
          password == App.config.internal_basic_auth_password

      get '/', provides: :json do
        statistics = params[:year], month: params[:month])



      def view_context

started to fail in one specific case:

require 'rails_helper'

RSpec.describe Internal::Dashboard::StatisticsController do
  def post(params: {}, token:)
    env = {
      'REQUEST_METHOD' => 'GET',
      'CONTENT_TYPE' => 'application/json',
      'QUERY_STRING' => "&#{params.to_param}",
      'HTTP_AUTHORIZATION' => "Basic #{token}",
      'rack.input' =>'')

    status, _headers, response_body =
    { status: status, body: response_body.first }

  it 'rejects unauthorized requests' do
    response = post(token: nil)
    expect(response[:status]).to eq 401

I started to get 400 instead of 401. Because I was not familiar with the code I started to digging in if this is a real problem and what causing it.

After comparing the code in rack gem I have noticed that basic? method has changed.

From (rack-2.0.7/lib/rack/auth/basic.rb)

def basic?
  "basic" == scheme

to (rack-2.2.2/lib/rack/auth/basic.rb)

def basic?
  "basic" == scheme && credentials.length == 2

So it is a correct behaviour now but if we want to get 401 status in specs we have to pass wrong (not empty) HTTP_AUTHORIZATION which we can do like this:

it 'rejects unauthorized requests' do
  response = post(token: Base64.strict_encode64(":"))
  expect(response[:status]).to eq 401

Find real error messages in Rails

Today after messing with some gems I wanted to run rspec but i was getting strange error

  can't modify frozen Array

Failure/Error: require File.expand_path('../config/environment', __dir__)

I tried to find solution in Google but those weren’t working. I found that running bundle exec rails c reveals true error messages. In my case I got this after running this command:

JSONAPI: Could not find resource 'api/v1/creators'. (Class Api::V1::CreatorResource not found) (NameError)

And I was finally able to solve this problem :)

Clear Sidekiq morgue from duplicate entries

Cleaning up Sidekiq’s Dead queue (Morgue) using WebUI may be really cumbersome, especially if you would like to remove only particular kinds of errors.

Simple script helps here (is also helpful when cleaning up other queues). do |item| 
   item.delete if (item['class'] == 'User::DeliverRegistrationNotificationWorker') 

There are quite a few interesting attributes you can perform search on. Example below

 "error_backtrace"=> []

Find error in Rollbar by Sidekiq job id

It is on many occasions hard to find a matching error on Rollbar for particular job that failed in Sidekiq. Fortunately, we can find it very effectively by the job id (JID), or many other attributes that are not picked by UI based search.

First, open the RQL console{organization_name_here}/{project_name_here}/rql (just click RQL in top navigation bar)

Then, enter following query into the Query field, i.e.

FROM item_occurrence
WHERE `request.params.jid`="ea336956c5a33327f2dfe399"

Clicking Execute will bring up a list of items matching the query - just click view next to one that interests you the most to see data you were looking for (searching by JID will produce just one result anyway).

Login to Ruby on Rails console with email and password

If you ever need to protect your rails console (or need to authenticate for any other reason) you can create a new file in config/initializers with code like this:

module Rails
  module ConsoleMethods
    def self.included(_base)
      puts 'Enter your email:'
      email = gets
      user = User.find_by(email: email.strip)
      unless user
        puts 'Email not found in database! Exiting...'

      puts 'Enter your password:'
      pass = $stdin.noecho(&:gets)
      if user.valid_password?(pass.strip)
        puts "Welcome #{}!"
        puts 'Provided password is not correct! Exiting...'

You can of course add condition so this code will be executed only on production and/or staging.

Note 1: If you are using devise for authentication as it is is done in my example above please make sure that your file is loaded after config/initializers/devise.rb which means that your file’s name should be “bigger” than devise.rb, for example: perform_authentication_in_console.rb

Note 2: with ruby version “2.3” or higher you are able to use IO::console.getpass instead of $stdin.noecho(&:gets)

Extend Ruby On Rails console with custom methods

If you find yourself executing complicated code to get some information in rails console and you know that you might do this many times in the futere you can consider extending Rails::ConsoleMethods.

Create a new file in config/initializers directory, for example:


in which you can define your module and prepend it to Rails::ConsoleMethods. The most trival example would be:

module CustomConsoleMethods
  def user(id)
  alias_method :u, :user

require 'rails/console/helpers'

group models by range of values

The task is to group calls with durations:

  • up to one minute
  • up to two minutes
  • up to five minutes
  • more than five minutes

Consider the following records with various durations

# duration is in seconds
2.times { Call.create(duration: rand(10..60)) }
3.times { Call.create(duration: rand(65..120)) }
4.times { Call.create(duration: rand(130..300)) }
5.times { Call.create(duration: rand(300..3000)) }

We can send such SQL to #group to get this done

def duration_in_minutes_ranges_sql
      WHEN duration BETWEEN 0 AND 60 THEN 'up_to_one'
      WHEN duration BETWEEN 61 AND 120 THEN 'up_to_two'
      WHEN duration BETWEEN 121 AND 300 THEN 'up_to_five'
      WHEN duration > 300 THEN 'over_five'
=> {

versions: rails: 6.0.0.rc1 pg: 1.1.4 psql: 11.3

Skip callbacks in Rails when saving record

This is valid for at least Rails 6. It is also dependent on low-level implementation, but should be rather safe to use.

def without_update_callbacks(record)
    existing_caller = record.method(:_run_update_callbacks)
    record.define_singleton_method(:_run_update_callbacks, ->(&block){ })

    record.define_singleton_method(:_run_update_callbacks, existing_caller)


user = User.last = "Tony" # runs callbacks = "Stark"
without_update_callbacks(user) do # does not run callbacks

rails, postgres & jsonb; find if key exists using where

Let’s assume that we have an ActiveRecord model called Day with jsonb field called references.

In the references field we stores property ids with some references, for example:

references: {'1' => 'ref123', '2' => 'ref456'}

If we want to find all days that does not have reference for a specific property id we can do this with following command:

Day.where("(references ->> '?')::text IS NULL",

validate! can raise various classes of exceptions

class ActiveRecord::Base and module ActiveModel::Validations raise different exceptions on failed validate!

consider the following classes:

class Foo
  include ActiveModel::Validations

  validates :foo, presence: true

  def foo

class Organisation < ActiveRecord::Base
  validates :name, presence: true

Take a look on classes of raised exceptions:

[2] pry(#<Api::V1::HooksController>)>!
ActiveModel::ValidationError: Validation failed: Foo can't be blank
from /Users/bartoszmaka/.asdf/installs/ruby/2.6.2/lib/ruby/gems/2.6.0/gems/activemodel-6.0.0.rc1/lib/active_model/validations.rb:412:in `raise_validation_error'
[3] pry(#<Api::V1::HooksController>)>!
ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
from /Users/bartoszmaka/.asdf/installs/ruby/2.6.2/lib/ruby/gems/2.6.0/gems/activerecord-6.0.0.rc1/lib/active_record/validations.rb:80:in `raise_validation_error'

The highest common ancestor of those classes is StandardError

[4] pry(#<Api::V1::HooksController>)> ActiveModel::ValidationError.ancestors
=> [ActiveModel::ValidationError,

[5] pry(#<Api::V1::HooksController>)> ActiveRecord::RecordInvalid.ancestors
=> [ActiveRecord::RecordInvalid,

If you’re using form objects - chances are, that you’re importing ActiveModel::Validations. Make sure that you rescue_from the proper exceptions.

Used versions: ruby 2.6.2p47, Rails 6.0.0.rc1

Form with multiple submit buttons

When you need to use the form in rails with submitting to different URL, you probably will try to handle it with js. Now you don’t need to, just use formaction on submit button. The formaction override the URL in form action and used its own.

<%= form_for @article, url: {action: "create"} do |f| %>
  <%= f.text_field :title %>
  <%= f.text_area :body, size: "60x12" %>
  <%= f.submit "Create" %> #this will use URL from form
  <%= f.submit "Create Draft Article",  formaction: '/draft_articles' %> #this will use URL from formaction
<% end %>

How to make path accessible in Rails Engine

Since routes in engine are isolated by default from main application so that you can have the same routes without clashing using default route helpers like about_path from main app will result in an error when you try to load it from inside the engine or access engine root from main app.

To fix that issue assuming you have an engine mounted:

Blorgh::Engine.routes.draw do
  resources :articles

to enforce using engine roots you need to change about_path to blorgh.about_path and to enforce using main application routes you need to change it to main_app.about_path

ActiveSupport::IncludeWithRange gotcha

Lets see how ruby implements === for ranges.

As documentation say “Returns true if obj is an element of the range, false otherwise”. Let’s try it out.

2.5.1 :001 > (1..10) === 5
 => true

Looks fine… how about if we compare it to another range?

 2.5.1 :001 > (1..10) === (5..15)
 => false

Seems to work properly again. How about if one range is a sub range of the other one.

2.5.1 :004 > (1..10) === (5..6)
 => false

As expected. Those ranges are not equal after all. Or at least (5..6) is not an element that (1..10) holds.

What is surprising, is what happens if we run the same thing in rails console (5.2.0 at the time of writing). Suddenly

[1] pry(main)> (1..10) === (5..6)
=> true

WAT? It now checks if range is included in the original range! Rails do not override === itself though. After looking at what rails adds to range…

[2] pry(main)> (1..10).class.ancestors
=> [ActiveSupport::EachTimeWithZone,

…we have identified this suspicious module ActiveSupport::IncludeWithRange. Its documentation explains everything.

# Extends the default Range#include? to support range comparisons.
#  (1..5).include?(1..5) # => true
#  (1..5).include?(2..3) # => true
#  (1..5).include?(2..6) # => false

Now guess what ruby’s Range#=== uses behind the scenes

              static VALUE
range_eqq(VALUE range, VALUE val)
    return rb_funcall(range, rb_intern("include?"), 1, val);

Yes… include?. The consequences are… there are consequences ;) The most annoying one is related to rspec.

expect(1..10).to match(5..6) # => true
expect([1..10]).to include(5..6) # => true
expect([1..10]).to match_array([5..6]) # => true

It is not possible to easily compare array of ranges matching on exact begin and end values yet disregarding actual order. Also the match behaviour is really misleading in my opinion. The only matcher we can use safely here is eq, as expect(1..10).to eq(5..6) will fail properly.

How to get XPath of Capybara's query

Have you ever found yourself in a situation, where you were trying to do something like e.g. click_link 'Approve' and Capybara was not able to find that element on the page despite the fact that it’s quite clearly visible, and you were asking yourself “what the heck is it searching for then?”. Or maybe your find(sth) is failing and you think it’s a bug in the Capybara 😱
Worry no more! Now you can easily check generated XPath used by Capybara*. In most cases, find(*args, **options) translates to:*args, session_options: current_scope.session_options, **options).xpath

E.g. to see XPath for click_on 'Approve':, 'Approve', session_options: current_scope.session_options).xpath

And XPath for find('tbody > tr > td:nth-child(2)'):'tbody > tr > td:nth-child(2)', session_options: current_scope.session_options).xpath

Then you can copy that XPath to the Chrome’s console and test it with $x('xpath').

* Presented solution doesn’t work with some types of more complicated queries, e.g. find('a', text: 'APPROVED') actually uses CSS selector instead of the XPath, and then filter results using Capybara::Result. You can check type of the selector used using .selector.format on your selector query.

Rake / rails console does not work in docker?

When using default ruby image in your Dockerfile (FROM ruby:2.5.1) if you encounter any problems with missing gems in your container when running rake task or rails console:

Could not find rake-x.y.z in any of the sources. Run bundle install to install missing gems.

That’s because you probably used:

RUN bundle install --deployment

You can fix it with:

RUN bundle install --without development test

Making enumerator from method that yields values

Original challenge related to AWS SQS QueuePoller

The challenge was to test a static method that yields multiple values but should stop, when some condition is met.

Let’s imagine such method as follows

class Poller
  def self.poll(condition = -> {})
    counter = 0

    while true do
      yield counter += 1
      break if

The problem is with testing such method. We do not only need to test what it yields, but we also need to test and control when it stops. To control when it stops, we need to access the actual block, but to test what it yields, we either need yield_successive_args matcher or we need to fetch consecutive results.

It is possible by aggregating each yielded value and then asserting them altogether, but the resultant code is not nice. The solution would be to make an Enumerator from the poll method and use next to get consecutive results. It is also easy as described in this blog post. The problem is, that we do not want to write code that is only required by our tests.

So the idea is to add creating enumerators when the block is not provided dynamically to the class when testing.

Poller.define_singleton_method(:poll_with_enum) do |*args, &block| 
    return enum_for(:poll) unless block.present?
    poll_without_enum(*args, &block)
# alias_method_chain is deprecated
# Poller.singleton_class.alias_method_chain(:poll, :enum)
Poller.singleton_class.alias_method :poll_without_enum, :poll
Poller.singleton_class.alias_method :poll, :poll_with_enum

if we turn this into a helper…

def with_enumerated(subject, method_name)
    subject.define_singleton_method("#{method_name}_with_enum") do |*args, &block|
      return enum_for(method_name, *args) unless block.present?
      public_send("#{method_name}_without_enum",*args, &block)

    subject.singleton_class.alias_method "#{method_name}_without_enum", method_name
    subject.singleton_class.alias_method method_name, "#{method_name}_with_enum"

    subject.singleton_class.alias_method method_name, "#{method_name}_without_enum"
    subject.singleton_class.remove_method "#{method_name}_with_enum"

…then we could leverage it in our tests!

with_enumerated(Poller, :poll) do
  $stop = false
  poller = Poller.poll(condition = -> { $stop == true })

  first_value =
  expect(first_value).to eq 1

  $stop = true
  second_value =
  expect(second_value).to eq 2

  expect { }.to raise_exception(StopIteration)

Handling uniqueness violations in factories

If it happens that your factories’ cross-dependencies result in creating records that violate uniqueness constraint, you can can fix it quick’n’dirty way

FactoryGirl.define do
  factory :day do
    initialize_with {
      Day.where(date: date).first_or_initialize

    sequence(:date) { |n| Date.current + n.days }

Ba aware that this is just a hack, and a hack that would add 1 SQL query to each creation of given object. Preferred way is to fix underlying problem. Still, if you do not care about retrieving the actual instance of object from factory, then different strategy can be used, that would mitigate extra query problem.

FactoryGirl.define do
  factory :day do
    to_create do |day|!
    rescue ActiveRecord::RecordNotUnique

    sequence(:date) { |n| Date.current + n.days }

If for any reason you really need to handle this kind of case, then introducing a custom create strategy (i.e. find_or_create(:day)) might be a way to go.

Mocking database views in Rspec

Sometimes it is a chore to feed database with all data we need for given database view to return results we are after within specs. Fortunately we can utilize temporary tables to mock such database views!

RSpec.configure do |config|
  ActiveRecord::Migration.verbose = false

  config.before(:all, :use_dummy_db_views) do |_example|
    ActiveRecord::Migration.create_table :my_database_view_entries, id: false, force: true do |t|
      t.integer :some_foreign_key      
      t.string :some_name
      t.string :some_code

    MyDatabaseViewModel.table_name = 'my_database_view_entries'

  config.after(:all, :use_dummy_db_views) do |_example|
    MyDatabaseViewModel.table_name = 'orignal_name_of_my_database_view'
    ActiveRecord::Migration.drop_table :my_database_view_entries, force: true    

Then it is just a matter of creating simulated view results, i.e. using factories.

# factory
FactoryBot.define do
  factory :my_database_view_model do
    some_foreign_key 10
    some_name 'Iron Man'
    some_code 'IRON'

# some spec
it 'some expectation', :use_dummy_db_views do
  # ...

Running heavy specs on CircleCI on demand

We have some specs that we do want to run only once a day (smoke specs that connect to actual live services). To handle this case we have introduced following setup:


--exclude-pattern "spec/smoke/*_spec.rb"


- run:
    name: run specs
    command: |
      if [[ ${RUN_ALL_TESTS} == "true" ]]; then
        bundle exec rspec --format progress --exclude-pattern ""
        bundle exec rspec --format progress

This way we can control if some specs are excluded or not, using ENV variable. You can then trigger such build on demand or use CircleCI Workflows.

How to run build on CircleCI recurrently

To run CircleCI build nightly (or at any interval we want), we need a couple of things. First we need to get CircleCI token that will allow us to access CircleCI API.

Next we need a script, that will trigger the build. In example below we also provide an extra build parameter RUN_ALL_TESTS that effectively allows us to conditionally run some specs we do not want to run during regular build.

namespace :ci do
  desc 'Runs build on CircleCI'
  task build: :environment do
    command = 'curl -X POST --header "Content-Type: application/json" ' \
      "--data '{\"build_parameters\": {\"RUN_ALL_TESTS\": \"true\"}}' " \
      '' \


Last thing is to schedule the build. Simplest way is to use Heroku Scheduler and just configure it to run rake ci:build command.

Edit: No longer valid! For Circle 2.0 we can use workflows for the same effect!

Making factory_bot work with read-only models

In one of our apps we need to ensure that all models are in read-only mode. Still, for testing purposes we need to be able to create instances of such models. Following code makes it possible.

# spec/models/application_record_spec.rb
require 'rails_helper'

RSpec.describe ApplicationRecord do
  it 'ensures all descendants are read-only' do
    Unit =

    expect( eq true
    expect { Unit.create! }.to raise_exception(ActiveRecord::ReadOnlyRecord)

  it 'allows creating objects using factories' do
    Unit =

    expect { read_only(:unit) }.to change { Unit.count }.by(1)
  it 'disallows updating objects' do
    Unit =
    unit = read_only(:unit)

    expect { unit.update_attributes!(name: 'New name') }.to \

# spec/support/factory_bot.rb
module FactoryBot
  module Strategy
    class ReadOnly <  Create
      def result(evaluation)
        is_readonly = evaluation.object.readonly?
        evaluation.object.define_singleton_method(:readonly?, -> { false })

        super.tap do |object|
          object.define_singleton_method(:readonly?, -> { is_readonly })

FactoryBot.register_strategy(:read_only, FactoryBot::Strategy::ReadOnly)

RSpec.configure do |config|
  config.include FactoryBot::Syntax::Methods

# spec/factories/units_factory.rb
FactoryBot.define do
  factory :unit do
    sequence(:name) { |i| "Unit #{i}" }

Configuring capybara-screenshot with Heroku

Due to many problems with our capybara-based automations we execute in Sidekiq on Heroku, we did need some visual feedback of what is going wrong. Unfortunately due to read-only nature of Heroku’s file system we did need to customize capybara-screenshot a bit to achieve this functionality.

# initializers/capybara_screenshot.rb

Capybara::Screenshot.s3_configuration = {
  s3_client_credentials: {
    access_key_id: ENV['DEBUG_BUCKET_S3_KEY'],
    secret_access_key: ENV['DEBUG_BUCKET_S3_SECRET'],
  bucket_name: ENV['DEBUG_BUCKET_S3_BUCKET']

# Default available methods use lunchy gem. 
# We do neither need nor want that. 
# Hence introducing simplified version.
Capybara::Screenshot.class_eval do
  def self.save_screenshot
    new_saver(Capybara,, false).save

Capybara.save_path = '/tmp' # Writeable directory on heroku

Then, we have decided to rescue and re-raise all exceptions, but also save a screenshot in the process…

rescue => exception # rubocop:disable Style/RescueStandardError
  raise exception

What to look for when your factory is missing id

So yesterday evening I’ve spotted a factory that was missing its _factory suffix. Easy peasy, quick rename and off we go… yes.. no… suddenly factories linting failed with following error

  The following factories are invalid:

  * lockoff - PG::NotNullViolation: ERROR:  null value in column "id" violates not-null constraint
  DETAIL:  Failing row contains (null, null, null, {}, {}, null, null, 2018-06-13 20:20:02.606616, 2018-06-13 20:20:02.606616).
  : INSERT INTO "lockoffs" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" (ActiveRecord::StatementInvalid)

null value in column "id" ? What? How? After checking absolutely everything reasonable, I’ve checked column definitions. It turned out, that during selecting changes for structure.sql, somebody forgot to add one important piece to commit, that was effectively preventing automatic generation of consecutive primary key values.

-- Name: lockoffs id; Type: DEFAULT; Schema: public; Owner: -

ALTER TABLE ONLY lockoffs ALTER COLUMN id SET DEFAULT nextval('lockoffs_id_seq'::regclass);

If you ever face similar issue, do not forget to look for something like that as well…

Joining multiple change expectations in rspec

To look for multiple changes on multiple objects, we could aggregate those changes and verify them en-masse, i.e.

expect { }.to \
  change { [property_1, property_2].map{ |p| p.reload.synced_at }.
    to([, 1, 1, 16, 35),, 1, 1, 16, 35)])

This will pick partial changes though (if only one value changes), so it is not recommended. Another way is nesting expectations, i.e.

expect {
  expect { }.to \
    change { property_1.reload.synced_at }.
      to(, 1, 1, 16, 35))
}.to change { property_2.reload.synced_at }.
  to(, 1, 1, 16, 35))

This will work, but you will get only one failure at a time. Still it is useful for joining conflicting expectations (i.e. to with not_to). For non conflicting ones, following syntax is recommended:

# using .and
expect { }.to \
  change { property_1.reload.synced_at }.
    to(, 1, 1, 16, 35)).and \
  change { property_2.reload.synced_at }.
    to(, 1, 1, 16, 35))


# using &
expect { }.to \
  change { property_1.reload.synced_at }.
  to(, 1, 1, 16, 35)) &
  change { property_2.reload.synced_at }.
  to(, 1, 1, 16, 35))

Properly yielding responses in Rspec

We had some problem with configuring response that should be yielded to block… after a short investigation, here are outcomes

allow(collection).to receive(:each).and_yield([1, 2])

will yield array ([1, 2]) to one block variable

allow(collection).to receive(:each).and_yield(1, 2)

will yield two values (1, 2) to two block variables

allow(collection).to receive(:each).and_yield(1).and_yield(2)

will yield two consecutive values (1, 2) to one block variable, twice

Clicked link and still on the same page (capybara)

When writing some automation i’ve tried to navigate to next page using a link…

click_on '6366'

…but saving and showing screenshot resulted in the same page being displayed. The reason was simple, link’s target was a new window (or tab). One way to handle this, is to switch context of window. It is as easy as

switch_to_window(window_opened_by { click_on '6366' })

but if you do not need/want to play around with multiple window contexts, consider following solution

visit find_link('6366')['href']

This way you will stay with current window context :)

How to use post/response in an RSpec matcher

In request specs post/response helpers are available:

post '/graphql', params: { query: query }
json = JSON.parse(response.body)

If you want to use them in a custom matcher, you have to include ActionDispatch::Integration::Runner and define you app:

class TheBestMatcherEverCreated
  include ActionDispatch::Integration::Runner

  # typical matcher's methods omitted for brevity

  def my_best_method
    post '/graphql', params: { query: query }


  def app

Custom validation error messages when using forms

Normally for rails model (i.e. Brand) you would do:

              invalid: "should contain only 'a-z', '0-9' or '-' but not as starting or ending character"

When using validations in forms you need a small tweak:

  • activerecord => activemodel
  • brand => brand_form (assuming your form is BrandForm)

Final translation:

              invalid: "should contain only 'a-z', '0-9' or '-' but not as starting or ending character"

Note: invalid is the key for validation type, full list here

Problems matching body params using stub_request?

Surprised that
  headers: { content_type: 'application/x-www-form-urlencoded' },
  body: { name: 'Tony', surname: 'Stark'}

is not matched by

stub_request(:post, '').
    headers: { 'Content-Type' => 'application/x-www-form-urlencoded' },
    body: { name: 'Tony', surname: 'Stark' }
  ).to_return(body: 'OK')


Remember, that HTTP Client does not send actually a hash, and stub_request does not know automagically which encoding you intended to use for your body params. You need to make it explicit, i.e.

stub_request(:post, '').
    headers: { 'Content-Type' => 'application/x-www-form-urlencoded' },
    body: URI.encode_www_form({ name: 'Tony', surname: 'Stark' })
  ).to_return(body: 'OK')

Missing your POST data in controller ?

So you can see JSON payload in inspector…

{days: [{date: "2018-05-18", unitPricing: {1: 285, 2: 285}},…]}

…but cannot see it in controller?

pry(#<Apis::V1::PricingBulkUpdatesController>)> params
=> <ActionController::Parameters {"controller"=>"apis/v1/pricing_bulk_updates", "action"=>"create"} permitted: false>

Remember to POST it with 'Content-Type': 'application/json' header and sell yourself a nice, juicy facepalm :)

Using have_selector with css and exact: option

Using have_selector with css selector and exact: option basically does not work.


expect(page).to_not have_content('a.hidden', exact: 'Push pricing to CB')


The :exact option only has an effect on queries using the XPath#is method. Using it with the query "div.hidden" has no effect.

We can use :contains("") css selector though!

expect(page).to_not have_content('a.hidden:contains("Push pricing to CB")')