Latest from the Rocketship

We like writing about our personal experiences, our challenges and our take on programming and management. Enjoy!

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 ]

  end



  # record is one of:

  # Object - when record was found

  # false - when record was not found

  def update_record(record, attributes)

    record.attributes = attributes

    record

  end



  # record is one of:

  # false

  # ZipRegion

  def validate_record(record)

    case record

    when false

      [:error, nil]

    else

      validate_record!(record)

    end

  end



  # record is ZipRegion object

  def validate_record!(record)

    if record.valid?

      [:ok, record]

    else

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

      [:error, record]

    end

  end



  def persist_record!(validation, record)

    case validation

    when :ok

      record.save

    when :error

      false

    end

  end

end

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))))))

  end

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!) >>

                             method(:success).curry[row[:region_name]]



    composition.call(row)

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(

  {

    HomeStack,

    BlurModal,

  },

  {

    ...NAVIGATOR_CONFIG,

    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>

    </BlurWrapper>

  );

}



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 

    FROM

      (

      SELECT

        taggings.taggable_id,

        ARRAY_AGG ( tags.NAME ) tags_array 

      FROM

        taggings

        LEFT JOIN tags ON taggings.tag_id = tags.ID 

      WHERE

        taggable_type = 'Note' 

      GROUP BY

        taggings.taggable_id 

      ) AS grouped_taggings 

    WHERE

      notes.ID = grouped_taggings.taggable_id

    SQL

  end

end

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


def tag_list

  tags.join(', ')

end

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:


CREATE OR REPLACE VIEW note_tags AS



SELECT UNNEST

  ( tags ) AS name,

  COUNT ( * ) AS taggings_count 

FROM

  notes 

GROUP BY

  name 

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, {}, {

  getUsers,  

})

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


import apiAdapter from './apiAdapter'



apiAdapter.getUsers().then(...)

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


const apiAdapter = createAdapter(

  client,

  { withCredentials: true },

  {

    getUsers,  

  },

  successHandler,

  errorHandler, 

)

Github

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) {

    dispatch(actions.removeJwt());

  } else {

    next(action);

  }

};

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 '192.168.0.1/24';

      inet

----------------

 192.168.0.1/24


select cidr '192.168.0.0/24'; -- valid cidr

      cidr

----------------

 192.168.0.0/24


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

ERROR:  invalid cidr value: "192.168.0.1/24"

LINE 1: select cidr '192.168.0.1/24';

                    ^

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('127.0.0.1');

     cidr

--------------

 127.0.0.1/32

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


select inet('127.0.0.1');

   inet

-----------

 127.0.0.1

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


select inet '192.168.0.1/24' >>= inet '192.168.0.0'; -- returns true

select cidr '192.168.0.0/24' >>= inet '192.168.0.0/12'; -- returns false

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

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


select netmask(inet('192.168.0.0/24')); -- returns 255.255.255.0

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

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:


$input.typeahead(

  {

    ...

  },

  {

        ...

    async: true

    source: (query, syncResults, asyncResults) ->

      href = self.href

      # Added getAsyncResults call to throttle requests:

      getAsyncResults(

        () ->

          $.ajax(

            url: href

            data:

              query: query

            dataType: 'json'

          ).success(

            (data, textStatus, jqXHR) ->

              asyncResults(data)

          )

      )

  })

where getAsyncResults defined before typeahead init:


# throttle is lodash function

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

    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:




myArray

    .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?




myArray

    .map(item => {

        // ...

    })

    // Get rid of falsy values

    .filter(Boolean);





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 = {

  ...user,

  id: 2,

  ...(position && { position })

}



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