TIL from the Rocketship

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

  • What’s New in Xcode 11

  • Debugging in Xcode 11

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' } 
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' } end end 
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; }); console.log(times); return times.indexOf(Math.min(...times)); }; 
 mostPerformantFunction([ () => { [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 
TILMarch 20, 2019by Alexander Blinov

How to download files with Axios

A simple example using the Blob() constructor:

 export function someFunction(values) { return (dispatch) => { ... const method = 'GET'; const url = 'http://go.api/download_file'; ... axios .request({ url, method, responseType: 'blob', //important }) .then(({ data }) => { const downloadUrl = window.URL.createObjectURL(new Blob([data])); const link = document.createElement('a'); link.href = downloadUrl; link.setAttribute('download', 'file.zip'); //any other extension document.body.appendChild(link); link.click(); link.remove(); }); }; } 
TILMarch 05, 2019by Alexander Spitsyn

How to conditionally render template in Grape resources?

Suppose you need to conditionally render some template in a given endpoint of a Grape resource. There is a env data in Grape::API class that represents Rack environment of the request. env is just a simple Ruby hash and we can pass the template name to it's api.tilt.template key to render that template:

 env['api.tilt.template'] = 'foo/bar.json' 

Then let's define a render method in Base resourсe:

 class Base < Grape::API def self.inherited(subclass) super subclass.instance_eval do helpers do def render(template_name) env['api.tilt.template'] = template_name end end end end end 

Now we can use it in the following way:

 class ContactsResource < Base desc 'Create contact', http_codes: [[201, 'Created']] post '/contacts' do contact = Contact.new(params[:contact]) if contact.save render 'v1/contacts/show.json' else error!({ errors: contact.errors.full_messages }, 422) end end end 
TILMarch 05, 2019by Alexander Spitsyn

Append string to a route with a slash operator

If you need to append string to some route to build a path name you can use few ways:

 Rails.root + 'foo/bar' [Rails.root, 'foo/bar'].join('/') "#{Rails.root}/foo/bar" 

But also there is a Pathname#/ method, so you can append string to route using a slash:

 Rails.root/'foo/bar' # => #<Pathname:/Users/alex/myproject/foo/bar> 

Looks more readable!

TILFebruary 25, 2019by Arina Shmeleva

How to add a line break to PostgreSQL?

You have to insert some data into your PostgreSQL table that has line break. As a result, the cell will look like this:

 First line Second line 

To use "escape sequences" in a string literal you need to use an "extended" constant. You can specify the escape character by prefixing the letter E:

 UPDATE posts SET body = E'First Line\nSecond line.' WHERE id = 2701; 
TILFebruary 23, 2019by Alexander Ivlev

Recompose withContext and getContext. Simple example

Very often for nested components you need to transfer some props of the parent. For example size. So, we have the Card component. It contains a Button component.

 // App.js <Card size="sm"> {/* ... */} <div> {/* ... */} <Button size="sm" onClick={action}>Action</Button> </div> </Card> 

The size for both components is set by the "size" parameter. In the example above, the parameter passed is indicated for both components. In order not to specify the size for the Button each time, you can use the Context to use the one specified for the Card. If you use recompose, it can be super easy:

 // Card.js import { getContext } from 'recompose'; // ... export default withContext( { size: PropTypes.oneOf(['sm', 'md', 'lg']) }, ({ size }) => ({ size }), )(Card); // App.js import Button from './Button'; // ... const EnhancedButton = getContext({ size: PropTypes.oneOf(['sm', 'md', 'lg']) })(Button); 

Live example

TILFebruary 20, 2019by Alexander Budchanov

AND & OR Operators Precedence

Are you still sure that && and and is the same operators? Look at this:

 a = true && false a => false 
 a = true and false a => true 

The same situation could be reproduced for || and or. Why? The answer lies in Ruby Operator Precedence.

The first example can be represented as:

 a = (true && false) 

Second:

 (a = true) and false 

Thanks to Igor Alexandrov