Today I Learned

A Hashrocket project

Search and Replace Control Characters In Vim

In vim, if you want to search and replace non-printable control characters (for example you have ^M scattered throughout your file and it’s messing up an import) you can use ctrl+v+<char>. The ctrl+v allows you to type the control character, but you must hold down control for both of the key presses.

:%s/ctrl+v+M/

This would search and replace all non-printable ^M characters in your text.

Slack Hot-Keys Rock

Do you like slack? Chances are it doesn’t really matter whether you do or don’t. It’s a matter of course at this point that you will probably work with it. Because that’s the case, you should at least strive to be as aware of neat features as possible!

To that end, you should check out the built-in hot-key help using:

MacOS: Command + / Linux/Windows: Ctrl + /

I’m particularly fond of the “Navigation” and “Channels & Direct Messages”!

No ifconfig on Linux?

If you are on a Linux machine and trying to run ifconfig you may get Command not found: ifconfig. That is because some Linux distros don’t come with it bundled by default.

On Manjaro Linux which is an Arch Linux based distro you can use pacman to install the package containing ifconfig called net-tools:

sudo pacman -S net-tools

On Debian based distros you can use:

sudo apt-get install net-tools

If you don’t want to install net-tools you can try using the ip command:

ip addr

This will return the interfaces on your machine and their IP bindings.

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

Most recent branches

You can get a list of all the branches in git using the very programmatically named for-each-ref command. This command by itself isn’t very useful and needs quite a bit of tailoring for it to produce the information you want.

Just by itself it returns all refs

git for-each-ref
# returns all the references

This shows all refs (branches, tags, stashes, remote branches). To just get branches pass an argument that corresponds to the .git directory that contain branch refs.

git for-each-ref ref/heads

To reduce the number of refs it produces use --count

git for-each-ref --count=5
# returns just 5 refs

This outputs a line that looks like this:

c1973f1ae3e707668b500b0f6171db0a7c464877 commit refs/heads/feature/some-feature

Which is a bit noisy. To reduce the noise we can use --format.

git for-each-ref --format="$(refname)"
# line is just: refs/heads/feature/some-feature

Which can be shortened with :short

git for-each-ref --format="$(refname:short)"
#just: feature/some-feature

To get most recent branches we’ll need to sort. The - in front of the field name reverses the sort.

git for-each-ref --sort=-committerdate

All together:

git for-each-ref \
--format="%(refname:short)" \
--count=5 \
--sort=-committerdate \
refs/heads/

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;
}

Import GraphQL Queries Where You Need Them (CRA)

If you want to export/import your GraphQL queries in your Create-React-App, you’ll need to add a library to make it work. You can use a library called grapql.macro to use .gql or .graphql files in your app.

If you’re also using jest, you’ll need to add jest-transform-graphql and update your jest configuration to pick up these file types.

Source: https://www.apollographql.com/docs/react/integrations/webpack/#jest

Do not prefix TypeScript interface names

TLDR: If you’re writing TypeScript, you’re using intellisense, and it will yell at you if you try to use an interface as a variable. So there is no need anymore for naming conventions like IMyInterface or AMyAbstractClass.

When I first started using TypeScript in 2018 I saw a lot of people’s code prefixing interfaces with “I”:

interface ICar {
    color: string
    horsepower: number
}

function drive(car: ICar) { /*...*/ }

However, that is a convention borrowed from the past (other languages). It used to be mentioned in the TypeScript handbook to not prefix, but that generated more argument than resolution. The handbook code examples, however, do not use prefixes.

I believe modern dev environments have relieved us from the need to include type and contextual information into the names of many code things, not just interfaces.

Now we get proper type and context from things like intellisense and language servers, whether it’s inferred or strictly typed. This frees us to have names for things that can be more descriptive of the processes and data in question.

Control your magic strings in Firebase projects

Firebase’s real-time database JavaScript API makes it really easy to wind up with a ton of magic strings in your codebase. Since you access data via “refs” like:

firebase.database.ref(`customers/1`).update({ name: 'Mike' })

In that example, “customers/1” is a magic string that points to some data in the database. Magic strings are well known as something to avoid in software development. And here we are updating a database with one, yikes!

These can easily be managed via patterns. I’ve been abstracting these into their own modules in an “API-like” pattern. For example:

// api/customers.ts
import { protectedRef } from 'protected-ref'

export const rootRef = 'customers'
export const getCustomerRef = (customerID: string) => protectedRef(rootRef, customerID)
export const updateCustomer = (customerID: string, updateDocument: CustomerUpdateDocument) => getCustomerRef(customerID).update(updateDocument)

And then use it like:

import { updateCustomer } from '../api/customers'

updateCustomer('1', { name: 'Mike' })

Also protected-ref is a firebase package that manifested from this TIL: https://til.hashrocket.com/posts/hyrbwius3s-protect-yourself-against-firebase-database-refs

What does that git alias do? 🕵️‍♂️

I have some git aliases in my dotfiles and sometimes I use an alias for too long that I actually forget what it does under the hood.

I can open my ~/.gitconfig file in nvim (which I have an alias for) and search for the line that introduced the alias, but when pairing sometimes it’s easier and faster to just use git help to get the definition of an alias:

git help doff
'doff' is aliased to 'reset HEAD^'

Postgres `null` and `where <VALUE> not in`

Always watch out for null in Postgres. When null sneaks into a result set it may confuse the results of your query.

Without nulls a where in query could look like this:

psql> select 'found it' as c0 where 1 in (1);
    c0
----------
 found it
 (1 row)

For the where in clause a null does not change the results.

psql> select 'found it' as c0 where 1 in (null, 1);
    c0
----------
 found it
(1 row)

The where not in formulation however is sensitive to null. Without a null it looks like this:

psql> select 'found it' as c0 where 17 not in (1);
    c0
----------
 found it
(1 row)

Add in the null and the results can be counterintuitive:

psql> select 'found it' as c0 where 17 not in (1, null);
 c0
----
(0 rows)

Watch out for those nulls!!

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.

JavaScript "falsy" values are still values

Having good defaults doesn’t mean always having them in the function signature. I try to provide good defaults for my function args when possible. I like to provide them in the signature, too. However, I ran into a case where doing it in an object destructure would have caused a bug.

This function receives an object (fruit) and returns its price based on its selectedRate. It is expected that selectedRate can be '', null, or undefined, in addition to constants like "standardRate". In any case that the selected rate doesn’t exist, standardRate should be used:

function getFruitPrice({ selectedRate = 'standardRate', ...fruit }) {
  //...
}

The code above doesn’t satisfy this requirement. JavaScript considers empty strings and null to be values even though they are falsy. So, we can’t rely on setting the default this way. Instead a better implentation might look like this:

function getFruitPrice(fruit) {
  const rateToUse = fruit.selectedRate || 'standardRate'
  // ...
}

Equality comparison and null in postgres

null is weird in postgres. Sure, it’s a way of saying that there is no data. But if there is a null value Postgres doesn’t want to be responsible for filtering the null value unless you explicitly tell it to.

psql> select 1 where null;
 ?column?
----------
(0 rows)

Comparing null to null with = returns null, not true.

psql> select 1 where null = null;
 ?column?
----------
(0 rows)

And comparing a value to null returns neither true nor false, but null.

psql> select 1 where 17 != null or 17 = null;
 ?column?
----------
(0 rows)

So when we apply a comparison to a nullable column over many rows, we have to be cognisant that null rows will not be included.

psql> select x.y from (values (null), (1), (2)) x(y) where x.y != 1;
 y
---
 2
(1 row)

To include the rows which have null values we have to explicitly ask for them with is null.

psql> select x.y from (values (null), (1), (2)) x(y) where x.y != 1 or x.y is null;
 y
---
 ø
 2
(2 rows)

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.

How to change the request body size in Phoenix

Plug.Parsers accepts a length: key with a value of approximate bytes to accept and can be used in an endpoint file.

# endpoint.ex
plug Plug.Parsers,
  pasrsers: [:urlencoded, :multipart],
  length: 100_000_000 # 100 MB body size (approximately)

You can also configure the length for a specific parser:

plug Plug.Parsers,
  parsers: [:urlencoded, {:multipart, length: 100_000_000}] # 100 MB body size (approximately)

Seeing tmux messages easier

Using tmux is pretty slick, until something doesn’t work and you want to see the error message for longer that about 750ms. That’s the default setting for the messages to be displayed.

Set a new display time

For the current session

:set-option display-time <milliseconds>

So set-option display-time 4000 would set your display time to 4 seconds.

Globally

Add the -g flag.
Or add it to your tmux.conf to hold on to the configuration:

set-option -g display-time <milliseconds>

View all messages

If you want to view all the recent messages, then you can use

:show-messages

Default hot-key

<tmux-leader>~

To close out of the messages, just press <enter>.

Use pgrep and xargs to kill (processes) #zsh #bash

Have you ever found yourself doing this:

ps aux | grep [b]eam

And then copying the pids one by one so you can pass them to kill?

There’s a better way to return just the pids of the process you care about and not having to worry about ps finding your grep call (that’s why I’m surrounding the b in beam with square brackets).

pgrep -f beam

This will return just the pids, one in each line (which is perfect for use with xargs)

Example output:

11632
11456

Use with xargs to kill (-9 for extra brutality points 😈):

pgrep -f beam | kill -9

Rails CollectionProxy #length vs #count

When testing a controller, it’s not unreasonable to right a test something like this:

expect(post_to_endpoint).to change { some.objects.length }.from(0).to(1)

Because of the details of CollectionProxy, this will not work as expected. Instead, you need to write:

expect(post_to_endpoint).to change { some.objects.count }.from(0).to(1)

The length method will evaluate to the same value, while the count method will reflect changes to the underlying data since the last call.

Ruby 2.7 Enumerable#tally

Have you ever wanted to get back a count of how often objects showed up within an array? If so, you’ve likely written (or copy-pasta’d) some variation of the code below:

list = %w(red red red blue blue fish blue blue)

list.each_with_object(Hash.new(0)) { |v, h| h[v] += 1 }          # => {"red"=>3, "blue"=>4, "fish"=>1}
list.each_with_object({}) { |v, h| h[v] ? h[v] += 1 : h[v] = 1 } # => {"red"=>3, "blue"=>4, "fish"=>1}
list.group_by { |v| v }.map { |k, v| [k, v.size] }.to_h          # => {"red"=>3, "blue"=>4, "fish"=>1}

Lucky for us, Ruby 2.7 introduces a new convenience method in Enumerable#tally, so our above code reduces to:

list = %w(red red red blue blue fish blue blue)
list.tally # => {"red"=>3, "blue"=>4, "fish"=>1}

Huzzah!

You can read the feature discussion here!

Linking to a PayPal Transaction Page ⛓

This is a follow up to PayPal Transaction Pages aren’t Linkable. We figured out a way to build this feature!

When you process a PayPal transaction with the SDK (i.e. as a seller), you get a token that could be considered a transaction ID. It is something like a primary key on an object that is the parent to several other transactions: the seller’s transaction, any currency conversions, etc.

With this ID in hand, you can link to the seller’s dashboard and see a summary of this parent transaction. It’s not a documented feature, so the link could be broken at any time.

Thanks for sticking with me, PayPal support.

No More Destructuring Objs in Func Args in Ruby2.7

Ruby has supported destructuring of objects into keyword arguments for a while in the Ruby 2.* series of releases, but now you’ll be getting a warning if you try this:

The 2.6 version

> def foo(a:, b:); puts [a, b]; end
> foo({a: 2, b: 2})
1
2

The 2.7 version

> def foo(a:, b:); puts [a, b]; end
> foo({a: 2, b: 2})
warning: Using the last argument as keyword parameters is deprecated; maybe ** should be added to the call

This warning will turn into an error in Ruby 3.0.

And if you add ** to the call like it tells you to:

> def foo(a:, b:); puts [a, b]; end
> foo(**{a: 2, b: 2})

OK Everything is cool. You can’t put ** if your keyword arguments are in a lambda which is being passed to map(&myLambda) though.

In this case, you’ll have to rewrite your code, so do that or you’ll be version locked!

H/T Brian Dunn

Read more here.

Protect Yourself Against Firebase Database Refs!

My worst nightmare is to lose production data, thank God for backups. But my nightmare came to life on a project’s Firebase database. This is a noSQL JSON-like document database offered as a service from Google Cloud Platform. You use it like this:

firebase.database.ref('/customers/1')

This is how you get the data at that path. Like a file system.

And when you want to make it dynamic:

firebase.database.ref(`/customers/${customerID}`)

Cool.

But, what if customerID is blank?

cue Nightmare Before Christmas soundtrack

funtion handleDelete(customerID) {
    firebase.database.ref(`/customers/${customerID}`)
    // ...delete()
}

If I ran that function in production with a blank customer ID, well then I just deleted all the customers.

Fear Not!

import protectedRef from './protectedRef.js'

const customerID = ''
protectedRef('customers', customerID) 
// => [ERROR] Bad ref! 1: ""

Use protection kids.

// protectedRef.js

import { sanitize } from './sanitize.js'

function protectedRefReducer(pathSoFar, pathPart, index) {
  const sanitizedPathPart = sanitize(pathPart)


  if (!sanitizedPathPart) {
    throw new Error(`Bad ref! ${index}: "${pathPart}"`)
  }

  return pathSoFar + '/' + sanitizedPathPart
}

export default function protectedRef(...parts) {
    if (!parts.length) {
    throw new Error('noop')
  }
  return firebase.database().ref(parts.reduce(protectedRefReducer, ''))
}

Ecto can only execute 1 sql statement at a time

Ecto can only ever execute 1 sql statement at a time, by design. For performance concerns, every statement is wrapped in a prepared statement
*some “security” is a fringe benefit of prepare statements.

In regards to the performance concerns of the prepared statement, PostgreSQL will force re-analysis of the statement when the objects in the statement have undergone definitional changes (DDL), making its use in a migration useless.

An example of a migration if you need to perform multiple statements:

def up do
  execute("create extension if not exists \"uuid-ossp\";")
  execute("alter table schedules add column user_id uuid;")
  execute("create unique index on schedules (id, user_id);")
end

def down do
  execute("alter table schedules drop column user_id;")
  execute("drop extension if exists \"uuid-ossp\";")
end

So you heard about Ruby 2.7 Pattern Matching?

Ruby has an experimental feature “Pattern Matching” introduced in this latest release, 2.7.

When I think pattern matching I think function arguments but for Ruby this is not the case. Instead, pattern matching is all about the case statement.

When I open up a 2.7 irb repl I can do this:

irb(main):001:0> case [1, 2]; in [1, x]; puts "x: #{x}"; end
(irb):5: warning: Pattern matching is experimental, and the behavior may change in future versions of Ruby!
x: 2
=> nil

Yep, it’s experimental. Seems like you shouldn’t use it in production code 😡.

There it is! Check out a slide deck with more info here

What happens on Ecto.Repo.rollback?

The rollback function interupts the execution of the function that you pass to transaction, but what is the mechanism for interupting execution?

When you call rollback you are essentially calling this code:

throw({DBConnection, conn_ref, reason})

throw is no good without a catch and you can find the try ... catch in the DBConnection module as well. It can be boiled down to this:

try do
  # call function passed to Ecto.Repo.transaction
catch
  :throw, {__MODULE__, ^conn_ref, reason} ->
    # send rollback request to database and return with {:error, reason}
    return {:error, reason}
  kind, reason ->
    # send rollback request to database and raise with reason
    :erlang.raise(kind, reason, stack)
end

This code exploits the ability of catch to both handle a throw and handle a raise.

PayPal Transaction Pages aren't Linkable ⛓

I spent some time this week trying to create a link directly to a PayPal Merchant transaction show page. This is the page you’d see as a merchant when somebody has paid you via PayPal.

These pages use the transaction’s ID as a lookup, so I’d need the know that ID in order to dynamically create the link. After some Googling, reading GitHub issues, and finally the source code of my PayPal SDK of choice, I’d like to report: PayPal does not expose this ID in the normal course of business.

To quote my PayPal caseworker:

“While there are ways of retrieving the Transaction ID… you will not be able to link directly to the PayPal transaction details page, for security. The only way that your buyers can view the transaction details page is by accessing PayPal themselves, logging in with their credentials, and navigating to the Activity.”

I hope this spares another developer from the same rabbit-hole I just emerged from. Sometimes things with technology don’t work. 🤷‍♀️

Edit: we figured it out!

Class.new with Superclass

Today I encountered the following syntax (observed in Ruby 2.7):

SpecialError = Class.new(StandardError)

What’s going on here?

According to the Class docs, Class.new takes a superclass as an argument, creating an anonymous unnamed class. So this could be written as:

class SpecialError < StandardError
end

We’d lose the anonymity of the SpecialError class, but in the first example, we are assigning it to a Pascal-cased variable name anyway.

I think the first syntax would make sense when the code looks just like this example: a simple class that inherits some behavior, where the name is needed only in the scope where the class is declared.

Sort Git Branches by Latest Commit

Some of my repos have a lot of branches, and a command I discovered this week sorts them by the most recent committer date:

$ git branch --sort=-committerdate
* new-branch
  not-so-new-branch
  super-old-branch

The - reverses the order, putting the most-recently updated branch at the top, rather than the bottom, of my output. That’s more intuitive to me, but it certainly works in reverse.

Ruby Array#sort_by

Let’s say we have the following list of very interesting items.

items = [
  { name: "dogfood", price: 4.99 },
  { name: "catfood", price: 5.99 },
  { name: "batfood", price: 4.99 }
]

Our client wants the list sorted by price descending, and we should break ties in price by then sorting by item name descending. So with our example list, the result should be:

items = [
  { name: "batfood", price: 4.99 },
  { name: "dogfood", price: 4.99 },
  { name: "catfood", price: 5.99 }
]

Here’s a method that does the job for our client:

def sort_by_price_and_name(items)
  items
    .group_by { |i| i[:price] }
    .map { |_k,v| v.sort { |a, b| a[:name] <=> b[:name] } }
    .flatten
end

It’s a bit unwieldy and inelegant. It turns out there is a perfect method for this in Ruby:

def sort_by_price_and_name(items)
  items.sort_by { |i| [i[:price], i[:name]] }
end

Ruby’s Enumerable#sort_by works in this case where we sort by all the fields in ascending order. If we wanted to sort by price ascending, then name descending, sort_by would no longer be the tool we’d want to use.

Hat tip to @joshcheek for the knowledge.

Useful Git Diff Filenames

If you type git config --global diff.noprefix true into your terminal, it will set the value like so in your ~/.gitconfig file:

[diff]
    noprefix = true

The upshot is that git diffs will no longer prefix files with a/ and b/, such that you can copy a useful path when you double-click to select it in the terminal.

--- a/file/that/changed.awesome
+++ b/file/that/changed.awesome

becomes

--- file/that/changed.awesome
+++ file/that/changed.awesome

Credit to @brandur for the tip. Here’s the source tweet

Capitalize Hashtags for #a11y

At RubyConf 2019, I learned that social media hashtags are more easily read by screen readers if they are capitalized. So #rubyconf is more accessible when written as #RubyConf, because the screen reader can more easily make sense of the word breaks. Keep this in mind when posting and designing marketing campaigns.

I saw #rubyfriends and #rubyconf migrate to #RubyFriends and #RubyConf at the conference in realtime as this idea spread.

more info

h/t Coraline Ada Ehmke

Use Typescript to help migrate/upgrade code

I am tasked with migrating a large javascript codebase from using the beta version of firebase-functions to the latest. Like most major upgrades, there are many API changes to deal with. Here’s an example cloud function:

Beta version with node 6:

exports.dbCreate = functions.database.ref('/path').onCreate((event) => {
  const createdData = event.data.val(); // data that was created
});

Latest version with node 10:

exports.dbCreate = functions.database.ref('/path').onCreate((snap, context) => {
  const createdData = snap.val(); // data that was created
});

The parameters changed for onCreate.

In the real codebase there are hundreds of cloud functions like this and they all have varying API changes to be made. With no code-mod in sight, I’m on my own to figure out an effecient way to upgrade. Enter Typescript.

After upgrading the dependencies to the latest versions I added a tsconfig:

{
  "compilerOptions": {
    "module": "commonjs",
    "outDir": "lib",
    "target": "es2017",
    "allowJs": true,
    "checkJs": true,
  },
  "include": ["src"]
}

The key is to enable checkJs so that the Typescript compiler reports errors in javascript files.

And running tsc --noEmit against the codebase provided me with a list of 200+ compiler errors pointing me to every change required.

`random()` in subquery is only executed once

I discovered this morning that random() when used in a subquery doesn’t really do what you think it does.

Random generally looks like this:

> select random() from generate_series(1, 3)
      random
-------------------
 0.856217631604522
 0.427044434007257
 0.237484132871032
(3 rows)

But when you use random() in a subquery the function is only evaluated one time.

> select (select random()), random() from generate_series(1, 3);
      random       |      random
-------------------+-------------------
 0.611774671822786 | 0.212534857913852
 0.611774671822786 | 0.834582580719143
 0.611774671822786 | 0.415058249142021
(3 rows)

So do something like this:

insert into things (widget_id) 
select 
  (select id from widgets order by random() limit 1)
from generate_series(1, 1000);

Results in 1000 entries into things all with the same widget_id.