TIL from the Rocketship

TILSeptember 03, 2019by Andrey Morozov

Git switch command

A switch command has been added in the new version of git

Let's look at examples:

# switched to <branch>

git switch <branch>

# creates a new <branch>

git switch -c <branch>

# switched to commit 

git switch -d <commit> 

# creates and switches to branch from remote. 

# need to use if branch exists in multiple remotes 

git switch -c <branch> --track <remote>/<branch> 

# switch to a branch even if the index or working tree is different from HEAD

# this is used to throw away local changes

git switch --discard-changes <branch>

# alias for  --discard-changes

git switch -f <branch> 

# switch back to the previous branch before we switched

git switch - 

Command available on version Git 2.23.0 or higher

TILSeptember 02, 2019by Alexander Spitsyn

Determining class of an object with case equality operator (===)

Case equality operator (or triple equals, ===) in Ruby returns true if the passed class is in the ancestors list of the passed object's class:

1.class.ancestors # [Integer, Numeric, Object, ...]

Numeric === 1 # true

Object === 1 # true

So it can be used for determining object's class:

String === 'abc' # true

'abc'.class #=> String

In cases above the case equality operator works like #kind_of? (or #is_a?):

1.kind_of?(Integer) # true

1.is_a?(Numeric) # true

The classes above has different implementations of === operator, that's why the results of comparison are different:

String.===('abc') # the same as String === 'abc'

Also it means that order of the arguments is important:

1 === Integer # false

TILAugust 30, 2019by Alexander Spitsyn

Testing external API integration with VCR gem

Suppose your application connects to an external service via API and you have a wrapper for this API that handles and parses response. The VCR gem gives you ability to store parsed response in a special format (cassetes). VCR makes a real request to the API for the first test run and writes it's response to the cassete for next test runnings.

First you need to set VCR configuration:

VCR.configure do |config|

  config.cassette_library_dir = "spec/vcr_cassettes"

  config.hook_into :webmock


And then write specs like the following:

RSpec.describe ExternalService do

  describe '#new_order' do

    let(:params) { { foo: 'bar' } }

    it 'creates new order' do

      VCR.use_cassette("external_service") do

        result = subject.new_order(params) # makes HTTP POST to an external service

        expect(result.successful?).to be_truthy

        expect(result.data).to have_key('order_id')





See more details on the official page of the gem.

TILAugust 28, 2019by Ilia Kriachkov

Ruby double splat (**) operator cheatsheet

The operator ** is useful as an options hash.

def one_method(**options);end

This form is completely similar to the following:

def another_method(options = {});end

In addition, you can strictly define the set of required keys for the method.

def one_strict_method(first_name:, last_name: , **options)

  puts "options: #{options}"

  greeting = "Hello #{first_name} #{last_name}"

  puts options[:upcase] ? greeting.upcase : greeting


pry(main)> one_strict_method(upcase: true)

ArgumentError: missing keywords: first_name, last_name

pry(main)> one_strict_method(first_name: 'John', last_name: 'Doe', upcase: true)

options: {:upcase=>true}


=> nil

Another advantage of double splat literal is that it works like #merge for Ruby Hash

class Contact::ShowRepresenter #:nodoc:

  def call(contact)


      contact: {



        # You can add something more complex here.

        # **GeoLocaionRepresenter.new.(contact)





  def base_info(contact)


      id: contact.id,

      first_name: contact.first_name,

      last_name: contact.last_name,

      email: contact.email,

      phone: contact.phone



  def legal_info(contact)


      legal_name: contact.legal_name,

      legal_type: contact.legal_type,

      mailing_address: contact.mailing_address





=> {









    :mailing_address=>"15 Let Oktyabrya Street, #10b, Tver, Russian Federation 170008"



In conclusion, I want to demonstrate some benchmark results.

As you can see, the ** operator is a bit faster than Hash#merge.

require 'benchmark'

n = 50_000

Benchmark.bm(2) do |x|

  x.report('merge:             ') { n.times { merge } }

  x.report('double_splat_merge:') { n.times { double_splat_merge } }


def merge

  hash = { a: 'a' }

  { b: 'b' }.merge(hash)


def double_splat_merge

  hash = { a: 'a' }

  { b: 'b', **hash }


                     user      system      total        real

merge:               0.109247   0.088652   0.197899 (  0.204470)

double_splat_merge:  0.079480   0.003590   0.083070 (  0.083642)

TILAugust 26, 2019by Dmitry Voronov

How to store large JSON in PostgreSQL with Rails Attributes API

If you store large objects in the database (such as JSON), for example, data for big reports, then this can take up a lot of space. To reduce the size of data, you can compress and store in binary form.

PostgreSQL has a bytea field type for storing such data. You can add bytea column in Rails using migration

add_column :reports, :data, :binary

For binary field operations, you can use the Rails Attributes API and add a new BinaryHash data type

# app/types/binary_hash.rb

class BinaryHash < ActiveRecord::Type::Binary

  def serialize(value)

    super value_to_binary(value.to_json)


  def deserialize(value)

    super case value

          when NilClass


          when ActiveModel::Type::Binary::Data







  def value_to_hash(value)



      symbolize_names: true

    ) || {}


  def value_to_binary(value)




Register new type in initializers

# config/initializers/types.rb

ActiveRecord::Type.register(:binary_hash, BinaryHash)

And add to binary type attribute in model

# app/models/snapshot.rb

class Reports < ApplicationRecord

  attribute :data, :binary_hash


Tests show that data size is reduced by almost 3 times

Run time with 100000 width JSON

                           user     system      total        real

Compress JSON          0.008671   0.001535   0.010206 (  0.010885)

Decompress JSON        0.001357   0.000095   0.001452 (  0.001509)

json size       95450 bytes

binary size   33868 bytes

~ 2.82 times compression

TILAugust 26, 2019by Dmitry Voronov

A simple way to distribute jobs in Sidekiq queues

This option implies that jobs of one context are executed sequentially in one queue, and jobs of different contexts in parallel in different queues.

Let's look at the following example.

There are investment funds for which we want to make time-consuming reporting calculations. Jobs for calculation within the same fund are carried out sequentially so that there are no errors in the calculations, jobs of different funds are performed in parallel.

We automate the distribution of jobs in queues so as not to specify a queue manually.

Specify the queues in the sidekiq.yml configuration file:


  - fund_processor_0

  - fund_processor_1

  - fund_processor_2

  - fund_processor_3

  - fund_processor_4

It is important that the queues are numbered from 0.

Now, when starting the worker, we indicate in which queue we will set the job depending on the fund. To do this, we use the operation of obtaining the remainder from dividing the fund ID and the count of queues. So we get the queue number.

# 5 queues

# fund ID % count of queues = queue number

# 1 % 5 => 1

# 2 % 5 => 2

# 3 % 5 => 3

# 4 % 5 => 4

# 5 % 5 => 0

def queue_name(fund_id)

  queue_number = fund_id % 5



Start the worker, indicating to him the received queue name.

This can be done using the Sidekiq API


  'queue' => queue_name(fund_id),

  'class' => Fund::ReportCalculator,

  'args' => [fund_id]


TILAugust 23, 2019by Eugene Komissarov

Rubyists life made easier with composition operators.

If you write Ruby code and wandered into FP world you might just started writing those little tiny methods inside your classes/modules. And that was awesome to write code like this:

class Import

  # Some code goes here...

  def find_record(row)

    [ Msa.find_or_initialize_by(region_name: row[:region_name], city: row[:city], state: row[:state], metro: row[:metro] ), row ]


  # record is one of:

  # Object - when record was found

  # false - when record was not found

  def update_record(record, attributes)

    record.attributes = attributes



  # record is one of:

  # false

  # ZipRegion

  def validate_record(record)

    case record

    when false

      [:error, nil]





  # record is ZipRegion object

  def validate_record!(record)

    if record.valid?

      [:ok, record]


      error(record.id, record.errors.messages)

      [:error, record]



  def persist_record!(validation, record)

    case validation

    when :ok


    when :error





Yeah, I know there is YARD, and argument types are somewhat weird but at the time of coding, I was fascinated with Gregor Kiczales's HTDP courses (that was a ton of fun, sincerely recommend for every adventurous soul).

And next comes dreadful composition:

def process(row, index)

    return if header_row?(row)

    success(row[:region_name], persist_record!(*validate_record(update_record(*find_record(parse_row(row))))))


The pipeline is quite short but already hard to read. Luckily, in Ruby 2.6 we now have 2 composition operators: Proc#>> and its reverse sibling Proc#<<.

And, with a bit of refactoring composition method becomes:

def process(row, index)

    return if header_row?(row)

    composition = method(:parse_row) >>

                             method(:find_record) >>

                             method(:update_record) >>

                             method(:validate_record) >>

                             method(:persist_record!) >>



Much nicier, don't you think? Ruby just became one step closer to FP-friendly languages family, let's hope there'll be more!

TILAugust 02, 2019by Maxim Romanov

How to blur a screen in React Navigation

Screens overlap each other in stackNavigator. React Navigation provides us not only with changing the background color of these screens, but also controlling their transparency.

To make the screen background blur, we first need to make the screens transparent.

import { createStackNavigator } from 'react-navigation';

export default createStackNavigator(







    mode: 'modal',

    cardStyle: { opacity: 1 },

    transparentCard: true,



And then use blurView as background.

import React from 'react';

import { BlurView } from '@react-native-community/blur';

import Styled from 'styled-components';

function BlurModal() {

  return (

    <BlurWrapper blurType="light" blurAmount={20}>

      <Text>Modal with blur background</Text>




const BlurWrapper = Styled(BlurView)`

  position: absolute;

  top: 0;

  left: 0;

  bottom: 0;

  right: 0;


TILJuly 31, 2019by Igor Alexandrov

Migrate tags in Rails to PostgreSQL array from ActsAsTaggableOn

ActsAsTaggableOn is a swiss army knife solution if you need to add tags to your ActiveRecord model.

Just by adding one gem to your Gemfile and acts_as_taggable to the model you get everything you need: adding tags, searching for a model by tag, getting top tags, etc. However, sometimes you don't need all these.

In our project, we used acts_as_taggable to store tags for Note model. Then we displayed a list of notes on several pages with assigned tags and had autocompleted input for tags on Note form. Everything worked well, but since we use PostgreSQL, I decided to store tags as an array in Note model.

First of all, I added tags Array<String> column to Note, after this migrated actsastaggable tags to notes table with migration.

class MigrateNoteTags < ActiveRecord::Migration[5.2]

  def change

    execute <<-SQL

    UPDATE notes 

    SET tags = grouped_taggings.tags_array 





        ARRAY_AGG ( tags.NAME ) tags_array 



        LEFT JOIN tags ON taggings.tag_id = tags.ID 


        taggable_type = 'Note' 

      GROUP BY


      ) AS grouped_taggings 


      notes.ID = grouped_taggings.taggable_id




To have backward compatibility, I added Note#tag_list method:

def tag_list

  tags.join(', ')


The last thing is to add the ability to search for tags. Since there about 500k records in the Notes table, I decided to create an SQL view:



  ( tags ) AS name,

  COUNT ( * ) AS taggings_count 





That's it! It takes from 100ms to 150ms to search for tags in this view, which is fine for me.

If you have more significant data sets, then the best would be to create tags table and add triggers to notes table that will update tags on INSERT/UPDATE/DELETE.

TILJuly 27, 2019by Alexander Ivlev

Request Api Adapter

When developing client applications, it is often necessary to send requests to the server.

// ...

client({ url: "/users.json", method: "GET" }).then(...)

We can make our lives a little easier. A convenient abstraction is apiAdapter

// apiAdapter.js

function getUsers() {

  return { url: "/users.json", method: "GET" };


const apiAdapter = createAdapter(client, {}, {



By defining a request in one place, you can now simply call the adapter method you want.

import apiAdapter from './apiAdapter'


It is also a useful option to specify basic settings for all requests, as well as handling errors and successful requests.

const apiAdapter = createAdapter(


  { withCredentials: true },








Live example

TILJuly 22, 2019by Alexey Belousov

Networking and thermal conditions debugging in Xcode 11

Xcode 11 introduces tool for networking and thermal conditions debugging. This feature requires device running iOS 13. Previously network conditioning feature was available within Additional Tools for Xcode.

See also

TILJuly 17, 2019by Maxim Romanov

How to handle 401 unauthorized error in a Redux React application

In response to a client request, the server may return a 401 Unauthorized error. You must correctly catch it, for example, clear the state and redirect to the authorization page. To solve this problem, we wrote a custom Middleware which, in our opinion, is the best solution.

import * as actions from 'actions';

const authInterceptor = ({ dispatch }) => (next) => (action) => {

  if (action.status === 401) {


  } else {




TILApril 22, 2019by Dmitry Sokolov

Integrate Drawer inside Stack Navigation in React Native

I wanted to use more than one navigation in a React Native app.

The documentation didn’t give a clear way to implement this.

Here is what you can do on purpose to add several types of navigation in your React Native app:

import { createStackNavigator, createDrawerNavigator } from 'react-navigation';

import * as Screens from './screens/index';

import Drawer from './components/DrawerMenu';

import getDrawerWidth from './utils/scale';

const AppStackNavigator = createStackNavigator({

  home: {

    screen: Screens.MainScreen,


  about: {

    screen: Screens.AboutScreen,



const AppNavigator = createDrawerNavigator(


    home: {

       screen: AppStackNavigator,




    contentComponent: Drawer,

    drawerWidth: getDrawerWidth,

    headerMode: 'none',



export default AppNavigator;

TILMay 15, 2019by Alexander Spitsyn

Handling IP addresses using PostgreSQL

PostgreSQL provides a inet and cidr datatypes for storing net addresses and proceed operations with them.

Host address and it's subnet can be stored with inet, while cidr can contain only network address:

select inet '';



select cidr ''; -- valid cidr



select cidr ''; -- invalid: cidr must not be a host address

ERROR:  invalid cidr value: ""

LINE 1: select cidr '';


DETAIL:  Value has bits set to right of mask.

In case there's no number after slash in cidr address the netmask is to equal 32:

select cidr('');



The value above represents a subnet address, while the same value passed to inet represents a host:

select inet('');



Checking inclusion or equality can be performed with >>= and <<= operators:

select inet '' >>= inet ''; -- returns true

select cidr '' >>= inet ''; -- returns false

select cidr '' >>= cidr ''; -- returns true

And getting a netmask by a net address can be performed with netmask:

select netmask(inet('')); -- returns

select netmask(cidr('')); -- returns

TILMay 14, 2019by Dmitry Sokolov

JS: Parsing URL

So far, I have used the indexOf operator or regular expressions to get query string values. Today I learned how to make it easier using the URLSearchParams API:

// Assuming "?post=1234&action=edit"

let urlParams = new URLSearchParams(window.location.search);

console.log(urlParams.has('post')); // true

console.log(urlParams.get('action')); // "edit"

console.log(urlParams.getAll('action')); // ["edit"]

console.log(urlParams.toString()); // "?post=1234&action=edit"

console.log(urlParams.append('active', '1')); // "?post=1234&action=edit&active=1"

Important: Does not work in IE

TILMay 01, 2019by Suhomozgy Andrey

Typeahead async requests throttling

As everybody else have some legacy code (who does not have it?), and yesterday we faced with problem.

Our internal app have search panel where users could write and get related results. But search was written with typeahead and without any throttling. So it's triggered full text search on backend side on every input change which leads to DB connection pool exhaustion.

We didn't found any ready to use solutions, so we added following fix:







    async: true

    source: (query, syncResults, asyncResults) ->

      href = self.href

      # Added getAsyncResults call to throttle requests:


        () ->


            url: href


              query: query

            dataType: 'json'


            (data, textStatus, jqXHR) ->





where getAsyncResults defined before typeahead init:

# throttle is lodash function

getAsyncResults = _.throttle(((fn) ->


  ), 300,

  { ... })

Would be happy if someone find this simple solution helpfull.

TILApril 23, 2019by Suhomozgy Andrey

Trick: filter falsy values out of an array?

Lets say you need to filter all falsy

0, undefined, null, false, ''(empty string), NaN

values from array.

Of course you can use following construnction:


    .map(item => {

        // ...


    // Get rid of falsy values

    .filter(item => item);

Since the values we don't want aren't truthy, the filter above removes those falsy items.

Did you know there's a clearer way with Boolean?


    .map(item => {

        // ...


    // Get rid of falsy values


TILApril 17, 2019by Suhomozgy Andrey

Add conditional properties in JS objects

How conditionaly add properties to object?

As it turned out, it's pretty simple:

const user = { id: 1, name: 'Suhomozgy Andrey' }

const position = 'Frontend Developer'

const userWithPosition = {


  id: 2,

  ...(position && { position })


userWithPosition //=> { id: 2, name: 'Suhomozgy Andrey', position: 'Frontend Developer' }

TILMarch 28, 2019by Dmitry Voronov

Create factory with an uploaded file in Rails rspec

To create a factory with an uploaded file in Rails rspec, needs to be included ActionDispatch::TestProcess module in the rails_helper.rb file to use the #fixture_file_upload method in the factories.

# spec/rails_helper.rb

include ActionDispatch::TestProcess

In a factory, when setting the attribute value, use the method #fixture_file_upload, specifying the path and file type.

# spec/factories/import_file.rb

FactoryBot.define do

  factory :import_file, class: ImportFile do

    data { fixture_file_upload 'spec/fixtures/test_file.pdf', 'application/pdf' }



TILMarch 20, 2019by Suhomozgy Andrey

Get most performant function

Let's say you need to determine which function is execute faster.

Write a function that take array of functions and iterates each of them a certain number of times.

Will use the difference in performance.now() values before and after to get the total time in milliseconds each iteration running.

Play with iterations argument to get more or less reliable results.

const mostPerformantFunction = (fns, iterations = 10000) => {

  const times = fns.map(fn => {

    const before = performance.now();

    for (let i = 0; i < iterations; i++) fn();

    return performance.now() - before;



  return times.indexOf(Math.min(...times));



  () => {

    [1, 2, 3, 4, 5, 6, 7, 8, 9, '10'].every(el => typeof el === 'number');


  () => {

    [1, '2', 3, 4, 5, 6, 7, 8, 9, 10].every(el => typeof el === 'number');


]); // 1