Today I Learned

A Hashrocket project

Ready to join Hashrocket? Find Openings here and apply today.

108 posts by dillonhafer

How to use ActiveRecored.pluck without Arel.sql

It is too cumbersome to remember to wrap every string in a class method, so this is a shortcut:

# app/models/application_record.rb

class ApplicationRecord < ActiveRecord::Base
  module ::ActiveRecord::Sanitization::ClassMethods
    def disallow_raw_sql!(args, permit: connection.column_name_matcher) # :nodoc:
      unexpected = nil
      args.each do |arg|
        next if arg.is_a?(Symbol) || arg.is_a?(String) || Arel.arel_node?(arg) || permit.match?(arg.to_s)
        (unexpected ||= []) << arg
      end

      if unexpected
        raise(ActiveRecord::UnknownAttributeReference,
          "Query method called with non-attribute argument(s): " +
          unexpected.map(&:inspect).join(", "))
      end
    end
  end
end

Then you can write sql without the class methods

This can be easier to write:

User.where(payment_due: true)
  .pluck(Arel.sql("coalesce('last_billed_date', 'start_date')"))

Can now just be written as:

User.where(payment_due: true)
  .pluck("coalesce('last_billed_date', 'start_date')")

This can also be useful for .order as well

Rails has helpers for uploading spec fixture files

Instead of writing your own helper method in specs:

module HelpMePlease
  def uploaded_file(path)
    Rack::Test::UploadedFile.new(Rails.root.join("spec/fixtures", path))
  end
end

Rails has a built in helper method:

require "rails_helper"

RSpec.describe HelpNeeded do
  describe "something" do
    it "sends the file" do
      post :change_avatar, params: { avatar: fixture_file_upload("spongebob.png", "image/png") }
    end
  end
end

Add a folder to git with exceptions

Given this folder structure:

app
├── bin
│  └── parse-ansi-codes.rs
├── Cargo.lock
├── Cargo.toml
├── README.md
├── src
│  ├── cursor.rs
│  ├── lib.rs
│  └── style.rs
├── target
│  └── debug
└── test

I can add the entire app directory to git, while ignoring the bin folder:

$ git add . ':!bin'

rspec should receive thrice

rspec has a #thrice method for testing receive counts:

describe Account do
  context "when opened" do
    it "logger#account_opened was called once" do
      logger = double("logger")
      account = Account.new
      account.logger = logger

      logger.should_receive(:account_opened).thrice

      account.open
      account.open
      account.open
    end
  end
end

Replace multiple characters in ruby strings

Ruby String#tr allows you to replace characters or patterns in strings:

irb(main):001:0> "I love coffee".tr("love", "😍")
=> "I 😍😍😍😍 c😍ff😍😍"

Compare with #gsub:

irb(main):001:0> "I love coffee".gsub("love", "😍")
=> "I 😍 coffee"

If your pattern arg to gsub is only one character consider using #tr, but beware of multi-length from_str arg to #tr

Split text in postgres

You can split text in postgres

select email from users;
bob@example.com
mary@example.com
select split_part(email, '@', 1) from users;
bob
mary
select split_part(email, '@', 2) from users;
@example.com
@example.com

Which can be helpful when sanitizing data:

update users
  set email = split_part(email, '@', 1) || '@example.com';

List cli xcodebuild archives in Xcode's organizer

When using a Makefile to build Xcode applications, it’s nice to have the archives listed in the Organizer window, for easy distribution. This can be accomplished with the -archivePath flag, using a specific directory:

BOB_THE_BUILD_DIR="~/Library/Developer/Xcode/Archives/$(date +%Y-%m-%d)"
ARCHIVE_PATH="$BOB_THE_BUILD_DIR/MyApp-$(date|md5).xcarchive"

xcodebuild -scheme MyApp -workspace "MyApp.xcworkspace" archive -configuration release -archivePath "$ARCHIVE_PATH"

🚯 Prevent development logs from bloating on macOS

I rarely need to refer to development.log or test.log when working on rails applications, but yet I end up keeping weeks or even years of records [gigabytes]. I’m used to working with logrotate, and I wanted to find a similar solution that was preinstalled with macOS. macOS comes preinstalled with a program called newsyslog that can keep file sizes in check. I just created a new file at /etc/newsyslog.d/rails.conf which limits all rail’s log files to just 10MB

# /etc/newsyslog.d/rails.conf
# logfilename                                                   [owner:group]      mode count size(KB)     when  flags [/pid_file] [sig_num]
/Users/<username>/dev/<rails projects>/*/log/*.log              <username>:staff   644  0     10000         *     G
# Mono repos
/Users/<username>/dev/<rails projects>/*/*/log/*.log            <username>:staff   644  0     10000         *     G
# Deeper mono repos
/Users/<username>/dev/<rails projects>/*/*/*/log/*.log          <username>:staff   644  0     10000         *     G

You can also perform a dryrun of the config for testing:

sudo newsyslog -v -n -f /etc/newsyslog.d/rails.conf

What directory is the parent of root 👨‍👦📁

I learned that in unix, root (e.g. /) is the only directory that is the parent directory of itself.

$ ls -lai / | grep '\./'
                  2 drwxr-xr-x   20 root  wheel   640 Jan  1  2020 ./
                  2 drwxr-xr-x   20 root  wheel   640 Jan  1  2020 ../

In the above example, the files . and .. both have the same i-node: 2

Source: Brian W. Kernighan, & Rob Pike (1984) The UNIX Programming Environment. Prentice-Hall, Inc

prevent execution when creating materialized views

When working with foreign data wrappers, one uses a materialized view to store the downloaded foreign table data. The process of downloading could be very expensive and managed by another process or program, but your program needs to define the parameters of materialzied view/foreign table. Maybe the data is loaded out of band by cron:

@daily /usr/bin/psql -d cool_db -c 'refresh materialized view big_view;'

So to create the materialized view without loading the data we use the WITH NO DATA clause:

create foreign table measurement_y2016m07
    partition of measurement for values from ('2016-07-01') to ('2016-08-01')
    server server_07;

create materialized view big_view
  as select *
  from measurement_y2016m07
  with no data;

This way we are able to execute the create foreign table and create materialized view statements in a very short amount of time. A different process can start the download with refresh materialized view

Fix poor type support in immutablejs

Immutablejs doesn’t work very well with typescript. But I can patch types myself for some perceived type safety:


import {Map,fromJS} from 'immutable';

interface TypedMap<T> extends Map<any, any> {
  toJS(): T;
  get<K extends keyof T>(key: K, notSetValue?: T[K]): T[K];
}

interface User {
  name: string;
  points: number;
}

const users = List() as List<TypedMap<User>>;

users.forEach(u => {
  const aString = u.name
  const aNumber = u.points
});

Postgres Identity Column

The Postgres wiki recommends not using the serial type, and instead added identity columns to replace them.

Old way:

create table todos (
  id bigserial primary key,
  todo text not null
);

The new way with identity columns:

create table todos (
  id bigint generated by default as identity primary key,
  todo text not null
);

Data:

insert into todos (todo) values
  ('write a til'),
  ('get some coffee');

select *
from todos;
 id |      todo
----+-----------------
  1 | write a til
  2 | get some coffee
(2 rows)

Source: PG wiki: Don’t use serial

Expo PushNotifications with pop-ups on Android

Expo has an amazing push notification service. In order to display push notifications in a pop-up style (the default on iOS) on Android, you must do two things:

  1. Create an Android push notification channel
  2. Set the priority and channelId in the push notification payload to expo.

Create channel example:

import React from "react";
import { Platform } from "react-native";
import { Notifications } from "expo";

class App extends React.Component<Props> {
  componentDidMount() {
    if (Platform.OS === "android") {
      Notifications.createChannelAndroidAsync("chat-messages", {
        name: "Messages",
        priority: "max",
        sound: true,
        vibrate: [0, 250, 500, 250]
      });
    }
  }

  render() {
    // ...
  }
}

Send PN with priority and channel example:

curl -H "Content-Type: application/json" -X POST "https://exp.host/--/api/v2/push/send" -d '{
  "to": "ExponentPushToken[xxxxxxxxxxxxxxxxxxxxxx]",
  "title":"New Message",
  "body": "Are you there?",
  "channelId": "chat-messages",
  "priority": "max"
}'

Prefetch images in ReactNative

When a react native app boots and there is a known set of user/group avatar urls received from an API, it may be a better user experience to load them before hand.

React Native’s Image component has a prefetch method that will save the image into the device’s image cache:

import { Image } from "react-native";

const App = () => {
  const { users, loading } = useUserFetcher();

  users.forEach(u => Image.prefetch(u.avatarURL));

  return (
    <View>
      <UserList users={users} loading={loading} />
    </View>
  );
};

RN fetch on Android requires mime type

ReactNative on Android requires a mime type when uploading files with FormData. But the only way to get the mime type of a user-chosen file is to read the file or files into memory after a user select one or more. In all of my use cases, my server didn’t care if the mimetype was in the FormData.

The easy solution is to just set the mimetype to a binary file type (e.g. application/octet-stream):

let data = new FormData();
data.append("file", {
  name: "cool.pdf",
  uri: "///files/cool.pdf",
  // MIME-type required on Android
  type: "application/octet-stream"
});

fetch("http://example.com/upload-file", data);

Ecto queries can be combined

Queries can take other queries to build on top of, which is very useful for conditional query logic:

def find_posts(group_id, title) do
  posts =
    from(posts in Phoenix.Post,
      where: posts.group_id == ^group_id,
    )
    |> title_search(title)
    |> Phoenix.Repo.all()
end

defp title_search(query, title) do
  query
    |> where([posts], posts.title == ^title)
end

defp title_search(query, nil) do
  query
end

💅 styled-components limit prop names

💅 styled-components is one of my absolute favorite libraries! ❤️ I just have to be careful not use prop names for my components that could be interpreted as styles.

For example:

interface Props {
  position: "left-side" | "right-side";
  item: Item;
}

const ListItem= styled.View<{position: "left-side" | "right-side"}>(({position}) => ({
  backgroundColor: position === "left-side" ? "burlywood" : "skyblue";
}));

const ListItemContainer: React.FC<Props> = ({position, item}) => {
  return (
    <ListItem position={position}>
      <Text>{item.name}</Text>
    </ListItem>
  );
}

<ListItemContainer item={item} position="left-side" />

This will return an error because in StyleSheet, position must be one of absolute or relative

How to manually edit ufw rules

Usually you don’t want to manually edit ufw, but in this case I just needed to update an ip address across multiple ip4 and ip6 rules. Turns out there is config file that can be very carefully edited:

$ sudo vim /lib/ufw/user.rules
$ sudo vim /lib/ufw/user6.rules

Very carefully update the ip addresses and then reload ufw:

$ sudo ufw reload

See git history of a renamed file

If you rename a file, git won’t show history of the previous name:

$ git log --pretty=oneline things/text.txt
8567d... Move file into things directory

However, the --follow flag will allow you to see the history of commits beyond the rename:

$ git log --follow --pretty=oneline things/text.txt
8567d... Move file into things directory
1458a... Fix something
0aac5... Add something

Docs:

--follow
  Continue listing the history of a file beyond renames (works only for a single file).

How to typescript react native list refs

VirtualLists (i.e. SectionList, FlatList) have always been dificult for me to describe in typescript, but the following seems to work:

interface Props {
  list: MutableRefObject<SectionList<any> | undefined>;
}

Example:

import React, { MutableRefObject } from "react";
import { SectionList, View, TouchableOpacity, Text } from "react-native";

interface Props {
  listRef: MutableRefObject<SectionList<any> | undefined>;
}

const Button = (props: Props) => {
  return (
    <TouchableOpacity
      onPress={() => {
        props.listRef.current?.scrollToLocation({
          itemIndex: 0,
          sectionIndex: 0,
          animated: true
        });
      }}
    >
      <Text>Scroll Up</Text>
    </TouchableOpacity>
  );
};

const Screen = () => {
  const listRef = useRef<SectionList<any>>();

  return (
    <View>
      <SectionList ref={listRef} />
      <Button listRef={listRef} />
    </View>
  );
};

How to redirect standard error

2> Allows us to redirect standard error.

Taking advantage of rm’s ability to not delete files recursively can come in handy, especially when writing clean up scripts, but it can be noisy when you don’t care.

Give this file structure:

~/
  tmpfile1.txt
  tmpfile2.txt
  tmpfile3.txt
  tmpfile4.txt
  do-not-delete/
    secrets.yml

I may want to delete all the files, but not touch the directories (to keep file removal simple)

$ rm *
rm: cannot remove 'do-not-delete': Is a directory

$ ls
~/
  do-not-delete/
    secrets.yml

Because the directory error message comes over stderr, we can simply redirect it to /dev/null to ignore it:

rm * 2> /dev/null

How to only stage deleted files

If I delete a bunch of files and just want to stage the deleted ones, I can use xargs to add them:

$ git ls-files --deleted | xargs git add

Example output:

$ git status

  deleted: lib/to/deleted_1.txt
  deleted: lib/to/deleted_2.txt
  modified lib/that/changed_1.txt
  deleted: lib/to/deleted_3.txt
  deleted: lib/to/deleted_4.txt
  modified lib/that/changed_2.txt
  deleted: lib/to/deleted_5.txt
  modified lib/that/changed_3.txt
$ git ls-files --deleted | xargs git add
Changes to be committed:

  deleted: lib/to/deleted_1.txt
  deleted: lib/to/deleted_2.txt
  deleted: lib/to/deleted_3.txt
  deleted: lib/to/deleted_4.txt
  deleted: lib/to/deleted_5.txt

Changes not staged for commit:

  modified lib/that/changed_1.txt
  modified lib/that/changed_2.txt
  modified lib/that/changed_3.txt

How to change the placeholder text color

The TextInput component in react-native has a property of placeholderTextColor that allows you to configure the placeholder text color! However, react-native-web does not have this property and it must be done in css. So if you were using react-native-web you would need to include .css file:

// screen.tsx
import "./styles.css";
import { TextInput } from "react-native";

const Screen = () => (
  <TextInput
    testID="change-placeholder-color"
    placeholder="enter some text"
    defaultValue=""
  />
);
/************* 
*
*  styles.css
*
**************/
[data-testid="change-placeholder-color"]::-webkit-input-placeholder {
  color: skyblue;
}
[data-testid="change-placeholder-color"]::-moz-placeholder {
  color: skyblue;
}
[data-testid="change-placeholder-color"]::-ms-placeholder {
  color: skyblue;
}
[data-testid="change-placeholder-color"]::placeholder {
  color: skyblue;
}

Browsers have a Web Cryptography API

All major versions of browser implement a Web Cryptography API for obtaining random numbers:

const numbers = new Uint32Array(1);
window.crypto.getRandomValues(numbers);
console.log(numbers);
// => [1627205277]

However, the methods are overridable, not read-only, and are vulnerable to polyfill attacks. This shouldn’t be used yet and is still being developed, but I found out the API exists.

How to remove unused deps from mix.lock

When you remove a dep from the mix.exs file it will remain in the mix.lock file. Unused deps need to be “unlocked” in order to be removed from the lock file.

For example:

mix deps.clean --unused

will only remove the unused dependencies’ files, but will keep the library in the mix.lock file. If you run the clean command the --unlock option, it will also remove it from the lock file.

For example:

mix deps.clean --unlock --unused

What a cursor is in postgres

SQL has a structure called a CURSOR that, according to the docs:

Rather than executing a whole query at once, read the query result a few rows at a time

This is mainly for solving memory usage issues, but probably not very applicable to web applications. Here’s an example syntax to periodically fetch a limited amount of rows:

begin;
declare posts_cursor cursor for select * from posts;
fetch 10 from posts_cursors;
fetch 10 from posts_cursors;
commit;

This is not advised due to leaving a transaction open, but a simple example. More powerful use cases would be iterating a query in a function for updating a small amount of records at a time.